adding everything
This commit is contained in:
		
						commit
						0ef5628d44
					
				
							
								
								
									
										10
									
								
								block_content.info.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								block_content.info.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| name: 'Block Content' | ||||
| type: module | ||||
| description: 'Allows the creation of content blocks and block types.' | ||||
| package: Core | ||||
| version: VERSION | ||||
| dependencies: | ||||
|   - drupal:block | ||||
|   - drupal:text | ||||
|   - drupal:user | ||||
| configure: entity.block_content.collection | ||||
							
								
								
									
										65
									
								
								block_content.install
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								block_content.install
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,65 @@ | ||||
| <?php | ||||
| 
 | ||||
| /** | ||||
|  * @file | ||||
|  * Install, update and uninstall functions for the block_content module. | ||||
|  */ | ||||
| 
 | ||||
| use Drupal\block_content\BlockContentStorageSchema; | ||||
| use Drupal\Core\Entity\Form\RevisionDeleteForm; | ||||
| use Drupal\Core\Entity\Form\RevisionRevertForm; | ||||
| use Drupal\Core\Entity\Routing\RevisionHtmlRouteProvider; | ||||
| use Drupal\Core\StringTranslation\TranslatableMarkup; | ||||
| 
 | ||||
| /** | ||||
|  * Implements hook_update_last_removed(). | ||||
|  */ | ||||
| function block_content_update_last_removed() { | ||||
|   return 8600; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Update entity definition to handle revision routes. | ||||
|  */ | ||||
| function block_content_update_10100(&$sandbox = NULL): TranslatableMarkup { | ||||
|   $entityDefinitionUpdateManager = \Drupal::entityDefinitionUpdateManager(); | ||||
|   $definition = $entityDefinitionUpdateManager->getEntityType('block_content'); | ||||
|   $routeProviders = $definition->get('route_provider'); | ||||
|   $routeProviders['revision'] = RevisionHtmlRouteProvider::class; | ||||
|   $definition | ||||
|     ->setFormClass('revision-delete', RevisionDeleteForm::class) | ||||
|     ->setFormClass('revision-revert', RevisionRevertForm::class) | ||||
|     ->set('route_provider', $routeProviders) | ||||
|     ->setLinkTemplate('revision-delete-form', '/admin/content/block/{block_content}/revision/{block_content_revision}/delete') | ||||
|     ->setLinkTemplate('revision-revert-form', '/admin/content/block/{block_content}/revision/{block_content_revision}/revert') | ||||
|     ->setLinkTemplate('version-history', '/admin/content/block/{block_content}/revisions'); | ||||
|   $entityDefinitionUpdateManager->updateEntityType($definition); | ||||
|   return \t('Added revision routes to Content block entity type.'); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Remove the unique values constraint from block content info fields. | ||||
|  */ | ||||
| function block_content_update_10200() { | ||||
|   $constraint = 'UniqueField'; | ||||
|   $definition_update_manager = \Drupal::entityDefinitionUpdateManager(); | ||||
|   $field_storage_definition = $definition_update_manager->getFieldStorageDefinition('info', 'block_content'); | ||||
|   $constraints = $field_storage_definition->getConstraints(); | ||||
|   if (isset($constraints[$constraint])) { | ||||
|     unset($constraints[$constraint]); | ||||
|     $field_storage_definition->setConstraints($constraints); | ||||
|     $definition_update_manager->updateFieldStorageDefinition($field_storage_definition); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Apply index to reusable column. | ||||
|  */ | ||||
| function block_content_update_10300(): void { | ||||
|   $manager = \Drupal::entityDefinitionUpdateManager(); | ||||
|   $entity_type = $manager->getEntityType('block_content') | ||||
|     ->setHandlerClass('storage_schema', BlockContentStorageSchema::class); | ||||
|   $manager->updateEntityType($entity_type); | ||||
|   $manager->updateFieldStorageDefinition(\Drupal::service('entity_field.manager') | ||||
|     ->getBaseFieldDefinitions('block_content')['reusable']); | ||||
| } | ||||
							
								
								
									
										13
									
								
								block_content.links.action.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								block_content.links.action.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| block_content_type_add: | ||||
|   route_name: block_content.type_add | ||||
|   title: 'Add block type' | ||||
|   appears_on: | ||||
|     - entity.block_content_type.collection | ||||
| 
 | ||||
| block_content_add_action: | ||||
|   route_name: block_content.add_page | ||||
|   title: 'Add content block' | ||||
|   appears_on: | ||||
|     - block.admin_library | ||||
|     - entity.block_content.collection | ||||
|   class: \Drupal\block_content\Plugin\Menu\LocalAction\BlockContentAddLocalAction | ||||
							
								
								
									
										4
									
								
								block_content.links.contextual.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								block_content.links.contextual.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| block_content.block_edit: | ||||
|   title: 'Edit' | ||||
|   group: block_content | ||||
|   route_name: 'entity.block_content.edit_form' | ||||
							
								
								
									
										5
									
								
								block_content.links.menu.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								block_content.links.menu.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| entity.block_content_type.collection: | ||||
|   title: 'Block types' | ||||
|   parent: system.admin_structure | ||||
|   description: 'Create and manage fields, forms, and display settings for your content blocks.' | ||||
|   route_name: entity.block_content_type.collection | ||||
							
								
								
									
										19
									
								
								block_content.links.task.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								block_content.links.task.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | ||||
| entity.block_content.collection: | ||||
|   title: Blocks | ||||
|   route_name: entity.block_content.collection | ||||
|   base_route: system.admin_content | ||||
| 
 | ||||
| entity.block_content.canonical: | ||||
|   title: Edit | ||||
|   route_name: entity.block_content.canonical | ||||
|   base_route: entity.block_content.canonical | ||||
| entity.block_content.delete_form: | ||||
|   title: Delete | ||||
|   route_name: entity.block_content.delete_form | ||||
|   base_route: entity.block_content.canonical | ||||
| 
 | ||||
| # Default tab for block type editing. | ||||
| entity.block_content_type.edit_form: | ||||
|   title: 'Edit' | ||||
|   route_name: entity.block_content_type.edit_form | ||||
|   base_route: entity.block_content_type.edit_form | ||||
							
								
								
									
										243
									
								
								block_content.module
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										243
									
								
								block_content.module
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,243 @@ | ||||
| <?php | ||||
| 
 | ||||
| /** | ||||
|  * @file | ||||
|  * Allows the creation of content blocks through the user interface. | ||||
|  */ | ||||
| 
 | ||||
| use Drupal\block\BlockInterface; | ||||
| use Drupal\block_content\BlockContentInterface; | ||||
| use Drupal\Core\Entity\EntityInterface; | ||||
| use Drupal\Core\Url; | ||||
| use Drupal\Core\Routing\RouteMatchInterface; | ||||
| use Drupal\field\Entity\FieldConfig; | ||||
| use Drupal\field\Entity\FieldStorageConfig; | ||||
| use Drupal\Core\Database\Query\SelectInterface; | ||||
| use Drupal\Core\Database\Query\AlterableInterface; | ||||
| use Drupal\Core\Database\Query\ConditionInterface; | ||||
| 
 | ||||
| /** | ||||
|  * Implements hook_help(). | ||||
|  */ | ||||
| function block_content_help($route_name, RouteMatchInterface $route_match) { | ||||
|   switch ($route_name) { | ||||
|     case 'help.page.block_content': | ||||
|       $field_ui = \Drupal::moduleHandler()->moduleExists('field_ui') ? Url::fromRoute('help.page', ['name' => 'field_ui'])->toString() : '#'; | ||||
|       $output = ''; | ||||
|       $output .= '<h2>' . t('About') . '</h2>'; | ||||
|       $output .= '<p>' . t('The Block Content module allows you to create and manage custom <em>block types</em> and <em>content-containing blocks</em>. For more information, see the <a href=":online-help">online documentation for the Block Content module</a>.', [':online-help' => 'https://www.drupal.org/documentation/modules/block_content']) . '</p>'; | ||||
|       $output .= '<h2>' . t('Uses') . '</h2>'; | ||||
|       $output .= '<dl>'; | ||||
|       $output .= '<dt>' . t('Creating and managing block types') . '</dt>'; | ||||
|       $output .= '<dd>' . t('Users with the <em>Administer blocks</em> permission can create and edit block types with fields and display settings, from the <a href=":types">Block types</a> page under the Structure menu. For more information about managing fields and display settings, see the <a href=":field-ui">Field UI module help</a> and <a href=":field">Field module help</a>.', [':types' => Url::fromRoute('entity.block_content_type.collection')->toString(), ':field-ui' => $field_ui, ':field' => Url::fromRoute('help.page', ['name' => 'field'])->toString()]) . '</dd>'; | ||||
|       $output .= '<dt>' . t('Creating content blocks') . '</dt>'; | ||||
|       $output .= '<dd>' . t('Users with the <em>Administer blocks</em> permission can create, edit, and delete content blocks of each defined block type, from the <a href=":block-library">Content blocks page</a>. After creating a block, place it in a region from the <a href=":blocks">Block layout page</a>, just like blocks provided by other modules.', [':blocks' => Url::fromRoute('block.admin_display')->toString(), ':block-library' => Url::fromRoute('entity.block_content.collection')->toString()]) . '</dd>'; | ||||
|       $output .= '</dl>'; | ||||
|       return $output; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Implements hook_theme(). | ||||
|  */ | ||||
| function block_content_theme($existing, $type, $theme, $path) { | ||||
|   return [ | ||||
|     'block_content_add_list' => [ | ||||
|       'variables' => ['content' => NULL], | ||||
|       'file' => 'block_content.pages.inc', | ||||
|     ], | ||||
|   ]; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Implements hook_entity_type_alter(). | ||||
|  */ | ||||
| function block_content_entity_type_alter(array &$entity_types) { | ||||
|   /** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */ | ||||
|   // Add a translation handler for fields if the language module is enabled. | ||||
|   if (\Drupal::moduleHandler()->moduleExists('language')) { | ||||
|     $translation = $entity_types['block_content']->get('translation'); | ||||
|     $translation['block_content'] = TRUE; | ||||
|     $entity_types['block_content']->set('translation', $translation); | ||||
|   } | ||||
| 
 | ||||
|   // Swap out the default EntityChanged constraint with a custom one with | ||||
|   // different logic for inline blocks. | ||||
|   $constraints = $entity_types['block_content']->getConstraints(); | ||||
|   unset($constraints['EntityChanged']); | ||||
|   $constraints['BlockContentEntityChanged'] = NULL; | ||||
|   $entity_types['block_content']->setConstraints($constraints); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Adds the default body field to a block type. | ||||
|  * | ||||
|  * @param string $block_type_id | ||||
|  *   Id of the block type. | ||||
|  * @param string $label | ||||
|  *   (optional) The label for the body instance. Defaults to 'Body' | ||||
|  * | ||||
|  * @return \Drupal\field\Entity\FieldConfig | ||||
|  *   A Body field object. | ||||
|  */ | ||||
| function block_content_add_body_field($block_type_id, $label = 'Body') { | ||||
|   // Add or remove the body field, as needed. | ||||
|   $field = FieldConfig::loadByName('block_content', $block_type_id, 'body'); | ||||
|   if (empty($field)) { | ||||
|     $field = FieldConfig::create([ | ||||
|       'field_storage' => FieldStorageConfig::loadByName('block_content', 'body'), | ||||
|       'bundle' => $block_type_id, | ||||
|       'label' => $label, | ||||
|       'settings' => [ | ||||
|         'display_summary' => FALSE, | ||||
|         'allowed_formats' => [], | ||||
|       ], | ||||
|     ]); | ||||
|     $field->save(); | ||||
| 
 | ||||
|     /** @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface $display_repository */ | ||||
|     $display_repository = \Drupal::service('entity_display.repository'); | ||||
| 
 | ||||
|     // Assign widget settings for the default form mode. | ||||
|     $display_repository->getFormDisplay('block_content', $block_type_id) | ||||
|       ->setComponent('body', [ | ||||
|         'type' => 'text_textarea_with_summary', | ||||
|       ]) | ||||
|       ->save(); | ||||
| 
 | ||||
|     // Assign display settings for default view mode. | ||||
|     $display_repository->getViewDisplay('block_content', $block_type_id) | ||||
|       ->setComponent('body', [ | ||||
|         'label' => 'hidden', | ||||
|         'type' => 'text_default', | ||||
|       ]) | ||||
|       ->save(); | ||||
|   } | ||||
| 
 | ||||
|   return $field; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Implements hook_query_TAG_alter(). | ||||
|  * | ||||
|  * Alters any 'entity_reference' query where the entity type is | ||||
|  * 'block_content' and the query has the tag 'block_content_access'. | ||||
|  * | ||||
|  * These queries should only return reusable blocks unless a condition on | ||||
|  * 'reusable' is explicitly set. | ||||
|  * | ||||
|  * Block_content entities that are not reusable should by default not be | ||||
|  * selectable as entity reference values. A module can still create an instance | ||||
|  * of \Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface | ||||
|  * that will allow selection of non-reusable blocks by explicitly setting | ||||
|  * a condition on the 'reusable' field. | ||||
|  * | ||||
|  * @see \Drupal\block_content\BlockContentAccessControlHandler | ||||
|  */ | ||||
| function block_content_query_entity_reference_alter(AlterableInterface $query) { | ||||
|   if ($query instanceof SelectInterface && $query->getMetaData('entity_type') === 'block_content' && $query->hasTag('block_content_access')) { | ||||
|     $data_table = \Drupal::entityTypeManager()->getDefinition('block_content')->getDataTable(); | ||||
|     if (array_key_exists($data_table, $query->getTables()) && !_block_content_has_reusable_condition($query->conditions(), $query->getTables())) { | ||||
|       $query->condition("$data_table.reusable", TRUE); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Utility function to find nested conditions using the reusable field. | ||||
|  * | ||||
|  * @todo Replace this function with a call to the API in | ||||
|  *   https://www.drupal.org/project/drupal/issues/2984930 | ||||
|  * | ||||
|  * @param array $condition | ||||
|  *   The condition or condition group to check. | ||||
|  * @param array $tables | ||||
|  *   The tables from the related select query. | ||||
|  * | ||||
|  * @see \Drupal\Core\Database\Query\SelectInterface::getTables | ||||
|  * | ||||
|  * @return bool | ||||
|  *   Whether the conditions contain any condition using the reusable field. | ||||
|  */ | ||||
| function _block_content_has_reusable_condition(array $condition, array $tables) { | ||||
|   // If this is a condition group call this function recursively for each nested | ||||
|   // condition until a condition is found that return TRUE. | ||||
|   if (isset($condition['#conjunction'])) { | ||||
|     foreach (array_filter($condition, 'is_array') as $nested_condition) { | ||||
|       if (_block_content_has_reusable_condition($nested_condition, $tables)) { | ||||
|         return TRUE; | ||||
|       } | ||||
|     } | ||||
|     return FALSE; | ||||
|   } | ||||
|   if (isset($condition['field'])) { | ||||
|     $field = $condition['field']; | ||||
|     if (is_object($field) && $field instanceof ConditionInterface) { | ||||
|       return _block_content_has_reusable_condition($field->conditions(), $tables); | ||||
|     } | ||||
|     $field_parts = explode('.', $field); | ||||
|     $data_table = \Drupal::entityTypeManager()->getDefinition('block_content')->getDataTable(); | ||||
|     foreach ($tables as $table) { | ||||
|       if ($table['table'] === $data_table && $field_parts[0] === $table['alias'] && $field_parts[1] === 'reusable') { | ||||
|         return TRUE; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|   } | ||||
|   return FALSE; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Implements hook_theme_suggestions_HOOK_alter() for block templates. | ||||
|  */ | ||||
| function block_content_theme_suggestions_block_alter(array &$suggestions, array $variables) { | ||||
|   $suggestions_new = []; | ||||
|   $content = $variables['elements']['content']; | ||||
| 
 | ||||
|   $block_content = $variables['elements']['content']['#block_content'] ?? NULL; | ||||
| 
 | ||||
|   if ($block_content instanceof BlockContentInterface) { | ||||
|     $bundle = $content['#block_content']->bundle(); | ||||
|     $view_mode = strtr($variables['elements']['#configuration']['view_mode'], '.', '_'); | ||||
| 
 | ||||
|     $suggestions_new[] = 'block__block_content__view__' . $view_mode; | ||||
|     $suggestions_new[] = 'block__block_content__type__' . $bundle; | ||||
|     $suggestions_new[] = 'block__block_content__view_type__' . $bundle . '__' . $view_mode; | ||||
| 
 | ||||
|     if (!empty($variables['elements']['#id'])) { | ||||
|       $suggestions_new[] = 'block__block_content__id__' . $variables['elements']['#id']; | ||||
|       $suggestions_new[] = 'block__block_content__id_view__' . $variables['elements']['#id'] . '__' . $view_mode; | ||||
|     } | ||||
| 
 | ||||
|     // Remove duplicate block__block_content. | ||||
|     $suggestions = array_unique($suggestions); | ||||
|     array_splice($suggestions, 1, 0, $suggestions_new); | ||||
|   } | ||||
| 
 | ||||
|   return $suggestions; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Implements hook_entity_operation(). | ||||
|  */ | ||||
| function block_content_entity_operation(EntityInterface $entity): array { | ||||
|   $operations = []; | ||||
|   if ($entity instanceof BlockInterface) { | ||||
|     $plugin = $entity->getPlugin(); | ||||
|     if ($plugin->getBaseId() === 'block_content') { | ||||
|       $custom_block = \Drupal::entityTypeManager()->getStorage('block_content')->loadByProperties([ | ||||
|         'uuid' => $plugin->getDerivativeId(), | ||||
|       ]); | ||||
|       $custom_block = reset($custom_block); | ||||
|       if ($custom_block && $custom_block->access('update')) { | ||||
|         $operations['block-edit'] = [ | ||||
|           'title' => t('Edit block'), | ||||
|           'url' => $custom_block->toUrl('edit-form')->setOptions([]), | ||||
|           'weight' => 50, | ||||
|         ]; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   return $operations; | ||||
| } | ||||
							
								
								
									
										37
									
								
								block_content.pages.inc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								block_content.pages.inc
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | ||||
| <?php | ||||
| 
 | ||||
| /** | ||||
|  * @file | ||||
|  * Provides page callbacks for content blocks. | ||||
|  */ | ||||
| 
 | ||||
| use Drupal\Core\Link; | ||||
| use Drupal\Core\Url; | ||||
| 
 | ||||
| /** | ||||
|  * Prepares variables for a block type creation list templates. | ||||
|  * | ||||
|  * Default template: block-content-add-list.html.twig. | ||||
|  * | ||||
|  * @param array $variables | ||||
|  *   An associative array containing: | ||||
|  *   - content: An array of block types. | ||||
|  * | ||||
|  * @see block_content_add_page() | ||||
|  */ | ||||
| function template_preprocess_block_content_add_list(&$variables) { | ||||
|   $variables['types'] = []; | ||||
|   $query = \Drupal::request()->query->all(); | ||||
|   foreach ($variables['content'] as $type) { | ||||
|     $variables['types'][$type->id()] = [ | ||||
|       'link' => Link::fromTextAndUrl($type->label(), Url::fromRoute('block_content.add_form', ['block_content_type' => $type->id()], ['query' => $query]))->toString(), | ||||
|       'description' => [ | ||||
|         '#markup' => $type->getDescription(), | ||||
|       ], | ||||
|       'title' => $type->label(), | ||||
|       'localized_options' => [ | ||||
|         'query' => $query, | ||||
|       ], | ||||
|     ]; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										16
									
								
								block_content.permissions.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								block_content.permissions.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| permission_callbacks: | ||||
|   - \Drupal\block_content\BlockContentPermissions::blockTypePermissions | ||||
| 
 | ||||
| access block library: | ||||
|   title: 'Access the Content blocks overview page' | ||||
|   description: 'Get an overview of all content blocks.' | ||||
| 
 | ||||
| administer block types: | ||||
|   title: 'Administer block types' | ||||
|   description: 'Maintain the block types of block content available and the fields that are associated with those types.' | ||||
|   restrict access: TRUE | ||||
| 
 | ||||
| administer block content: | ||||
|   title: 'Administer block content' | ||||
|   description: 'View, edit and delete all block content regardless of permission restrictions.' | ||||
|   restrict access: TRUE | ||||
							
								
								
									
										94
									
								
								block_content.post_update.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								block_content.post_update.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,94 @@ | ||||
| <?php | ||||
| 
 | ||||
| /** | ||||
|  * @file | ||||
|  * Post update functions for Content Block. | ||||
|  */ | ||||
| 
 | ||||
| use Drupal\block_content\BlockContentTypeInterface; | ||||
| use Drupal\Core\Config\Entity\ConfigEntityUpdater; | ||||
| use Drupal\user\Entity\Role; | ||||
| use Drupal\views\Entity\View; | ||||
| 
 | ||||
| /** | ||||
|  * Implements hook_removed_post_updates(). | ||||
|  */ | ||||
| function block_content_removed_post_updates() { | ||||
|   return [ | ||||
|     'block_content_post_update_add_views_reusable_filter' => '9.0.0', | ||||
|   ]; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Clear the entity type cache. | ||||
|  */ | ||||
| function block_content_post_update_entity_changed_constraint() { | ||||
|   // Empty post_update hook.
 | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Moves the custom block library to Content. | ||||
|  */ | ||||
| function block_content_post_update_move_custom_block_library() { | ||||
| 
 | ||||
|   if (!\Drupal::service('module_handler')->moduleExists('views')) { | ||||
|     return; | ||||
|   } | ||||
|   if (!$view = View::load('block_content')) { | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   $display =& $view->getDisplay('page_1'); | ||||
|   if (empty($display) || $display['display_options']['path'] !== 'admin/structure/block/block-content') { | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   $display['display_options']['path'] = 'admin/content/block'; | ||||
|   $menu =& $display['display_options']['menu']; | ||||
|   $menu['title'] = 'Blocks'; | ||||
|   $menu['description'] = 'Create and edit block content.'; | ||||
|   $menu['expanded'] = FALSE; | ||||
|   $menu['parent'] = 'system.admin_content'; | ||||
|   $view->set('label', 'Content blocks'); | ||||
| 
 | ||||
|   $view->save(); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Update block_content 'block library' view permission. | ||||
|  */ | ||||
| function block_content_post_update_block_library_view_permission() { | ||||
|   $config_factory = \Drupal::configFactory(); | ||||
|   $config = $config_factory->getEditable('views.view.block_content'); | ||||
|   $current_perm = $config->get('display.default.display_options.access.options.perm'); | ||||
|   if ($current_perm === 'administer blocks') { | ||||
|     $config->set('display.default.display_options.access.options.perm', 'access block library') | ||||
|       ->save(TRUE); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Update permissions for users with "administer blocks" permission. | ||||
|  */ | ||||
| function block_content_post_update_sort_permissions(&$sandbox = NULL) { | ||||
|   \Drupal::classResolver(ConfigEntityUpdater::class)->update($sandbox, 'user_role', function (Role $role) { | ||||
|     if ($role->hasPermission('administer blocks')) { | ||||
|       $role->grantPermission('administer block content'); | ||||
|       $role->grantPermission('access block library'); | ||||
|       $role->grantPermission('administer block types'); | ||||
|       return TRUE; | ||||
|     } | ||||
|     return FALSE; | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Update configuration for revision type. | ||||
|  */ | ||||
| function block_content_post_update_revision_type(&$sandbox = NULL) { | ||||
|   \Drupal::classResolver(ConfigEntityUpdater::class) | ||||
|     ->update($sandbox, 'block_content_type', function (BlockContentTypeInterface $block_content_type) { | ||||
|       $block_content_type->set('revision', (bool) $block_content_type->get('revision')); | ||||
|       return TRUE; | ||||
|     }); | ||||
| } | ||||
							
								
								
									
										132
									
								
								block_content.routing.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								block_content.routing.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,132 @@ | ||||
| block_content.add_page: | ||||
|   path: '/block/add' | ||||
|   defaults: | ||||
|     _controller: '\Drupal\block_content\Controller\BlockContentController::add' | ||||
|     _title: 'Add content block' | ||||
|   options: | ||||
|     _admin_route: TRUE | ||||
|   requirements: | ||||
|     _entity_create_any_access: 'block_content' | ||||
| 
 | ||||
| # @todo Deprecate this route once | ||||
| #   https://www.drupal.org/project/drupal/issues/3159210 is fixed, or remove | ||||
| #   it in Drupal 11. | ||||
| # @see https://www.drupal.org/node/3320855 | ||||
| entity.block_content_type.collection.bc: | ||||
|   path: '/admin/structure/block/block-content/types' | ||||
|   defaults: | ||||
|     _controller: '\Drupal\block_content\Controller\BlockContentController::blockContentTypeRedirect' | ||||
|   options: | ||||
|     _admin_route: TRUE | ||||
|   requirements: | ||||
|     _permission: 'administer block types' | ||||
| 
 | ||||
| # @todo Deprecate this route once | ||||
| #   https://www.drupal.org/project/drupal/issues/3159210 is fixed, or remove | ||||
| #   it in Drupal 11. | ||||
| # @see https://www.drupal.org/node/3320855 | ||||
| entity.block_content_type.edit_form.bc: | ||||
|   path: '/admin/structure/block/block-content/manage/{block_content_type}' | ||||
|   defaults: | ||||
|     _controller: '\Drupal\block_content\Controller\BlockContentController::blockContentTypeRedirect' | ||||
|   options: | ||||
|     _admin_route: TRUE | ||||
|   requirements: | ||||
|     _permission: 'administer block types' | ||||
| 
 | ||||
| block_content.add_form: | ||||
|   path: '/block/add/{block_content_type}' | ||||
|   defaults: | ||||
|     _controller: '\Drupal\block_content\Controller\BlockContentController::addForm' | ||||
|     _title_callback: '\Drupal\block_content\Controller\BlockContentController::getAddFormTitle' | ||||
|   options: | ||||
|     _admin_route: TRUE | ||||
|   requirements: | ||||
|     _entity_create_access: 'block_content:{block_content_type}' | ||||
| 
 | ||||
| entity.block_content.canonical: | ||||
|   path: '/admin/content/block/{block_content}' | ||||
|   defaults: | ||||
|     _entity_form: 'block_content.edit' | ||||
|     _title_callback: '\Drupal\Core\Entity\Controller\EntityController::title' | ||||
|   options: | ||||
|     _admin_route: TRUE | ||||
|   requirements: | ||||
|     _entity_access: 'block_content.update' | ||||
|     block_content: \d+ | ||||
| 
 | ||||
| entity.block_content.edit_form: | ||||
|   path: '/admin/content/block/{block_content}' | ||||
|   defaults: | ||||
|     _entity_form: 'block_content.edit' | ||||
|   options: | ||||
|     _admin_route: TRUE | ||||
|   requirements: | ||||
|     _entity_access: 'block_content.update' | ||||
|     block_content: \d+ | ||||
| 
 | ||||
| # @todo Deprecate this route once | ||||
| #   https://www.drupal.org/project/drupal/issues/3159210 is fixed, or remove | ||||
| #   it in Drupal 11. | ||||
| # @see https://www.drupal.org/node/2317981 | ||||
| entity.block_content.edit_form.bc: | ||||
|   path: '/block/{block_content}' | ||||
|   defaults: | ||||
|     _controller: '\Drupal\block_content\Controller\BlockContentController::editRedirect' | ||||
|   options: | ||||
|     _admin_route: TRUE | ||||
|   requirements: | ||||
|     _entity_access: 'block_content.update' | ||||
|     block_content: \d+ | ||||
| 
 | ||||
| entity.block_content.delete_form: | ||||
|   path: '/admin/content/block/{block_content}/delete' | ||||
|   defaults: | ||||
|     _entity_form: 'block_content.delete' | ||||
|     _title: 'Delete' | ||||
|   options: | ||||
|     _admin_route: TRUE | ||||
|   requirements: | ||||
|     _entity_access: 'block_content.delete' | ||||
|     block_content: \d+ | ||||
| 
 | ||||
| # @todo Deprecate this route once | ||||
| #   https://www.drupal.org/project/drupal/issues/3159210 is fixed, or remove | ||||
| #   it in Drupal 11. | ||||
| # @see https://www.drupal.org/node/2317981 | ||||
| entity.block_content.delete_form.bc: | ||||
|   path: '/block/{block_content}/delete' | ||||
|   defaults: | ||||
|     _controller: '\Drupal\block_content\Controller\BlockContentController::editRedirect' | ||||
|   options: | ||||
|     _admin_route: TRUE | ||||
|   requirements: | ||||
|     _entity_access: 'block_content.delete' | ||||
|     block_content: \d+ | ||||
| 
 | ||||
| block_content.type_add: | ||||
|   path: '/admin/structure/block-content/add' | ||||
|   defaults: | ||||
|     _entity_form: 'block_content_type.add' | ||||
|     _title: 'Add' | ||||
|   requirements: | ||||
|     _entity_create_access: 'block_content_type' | ||||
| 
 | ||||
| entity.block_content.collection: | ||||
|   path: '/admin/content/block' | ||||
|   defaults: | ||||
|     _title: 'Content blocks' | ||||
|     _entity_list: 'block_content' | ||||
|   requirements: | ||||
|     _permission: 'access block library+administer block content' | ||||
| 
 | ||||
| # @todo Deprecate this route once | ||||
| #   https://www.drupal.org/project/drupal/issues/3159210 is fixed, or remove | ||||
| #   it in Drupal 11. | ||||
| # @see https://www.drupal.org/node/3320855 | ||||
| entity.block_content.collection.bc: | ||||
|   path: '/admin/structure/block/block-content' | ||||
|   defaults: | ||||
|     _controller: '\Drupal\block_content\Controller\BlockContentController::blockLibraryRedirect' | ||||
|   requirements: | ||||
|     _permission: 'access block library+administer block content' | ||||
							
								
								
									
										11
									
								
								block_content.services.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								block_content.services.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | ||||
| services: | ||||
|   _defaults: | ||||
|     autoconfigure: true | ||||
|   block_content.uuid_lookup: | ||||
|     class: \Drupal\block_content\BlockContentUuidLookup | ||||
|     arguments: ['@cache.bootstrap', '@lock', '@entity_type.manager'] | ||||
|     tags: | ||||
|       - { name: needs_destruction } | ||||
|   block_content.bc_subscriber: | ||||
|     class: Drupal\block_content\Routing\RouteSubscriber | ||||
|     arguments: ['@entity_type.manager', '@module_handler'] | ||||
							
								
								
									
										10
									
								
								config/install/core.entity_view_mode.block_content.full.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								config/install/core.entity_view_mode.block_content.full.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| langcode: en | ||||
| status: false | ||||
| dependencies: | ||||
|   module: | ||||
|     - block_content | ||||
| id: block_content.full | ||||
| label: Full | ||||
| description: '' | ||||
| targetEntityType: block_content | ||||
| cache: true | ||||
							
								
								
									
										18
									
								
								config/install/field.storage.block_content.body.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								config/install/field.storage.block_content.body.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| langcode: en | ||||
| status: true | ||||
| dependencies: | ||||
|   module: | ||||
|     - block_content | ||||
|     - text | ||||
| id: block_content.body | ||||
| field_name: body | ||||
| entity_type: block_content | ||||
| type: text_with_summary | ||||
| settings: {  } | ||||
| module: text | ||||
| locked: false | ||||
| cardinality: 1 | ||||
| translatable: true | ||||
| indexes: {  } | ||||
| persist_with_no_fields: true | ||||
| custom_storage: false | ||||
							
								
								
									
										550
									
								
								config/optional/views.view.block_content.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										550
									
								
								config/optional/views.view.block_content.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,550 @@ | ||||
| langcode: en | ||||
| status: true | ||||
| dependencies: | ||||
|   module: | ||||
|     - block_content | ||||
|     - user | ||||
| id: block_content | ||||
| label: 'Content blocks' | ||||
| module: views | ||||
| description: 'Find and manage content blocks.' | ||||
| tag: default | ||||
| base_table: block_content_field_data | ||||
| base_field: id | ||||
| display: | ||||
|   default: | ||||
|     id: default | ||||
|     display_title: Default | ||||
|     display_plugin: default | ||||
|     position: 0 | ||||
|     display_options: | ||||
|       title: 'Content blocks' | ||||
|       fields: | ||||
|         info: | ||||
|           id: info | ||||
|           table: block_content_field_data | ||||
|           field: info | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           entity_type: null | ||||
|           entity_field: info | ||||
|           plugin_id: field | ||||
|           label: 'Block description' | ||||
|           exclude: false | ||||
|           alter: | ||||
|             alter_text: false | ||||
|             text: '' | ||||
|             make_link: false | ||||
|             path: '' | ||||
|             absolute: false | ||||
|             external: false | ||||
|             replace_spaces: false | ||||
|             path_case: none | ||||
|             trim_whitespace: false | ||||
|             alt: '' | ||||
|             rel: '' | ||||
|             link_class: '' | ||||
|             prefix: '' | ||||
|             suffix: '' | ||||
|             target: '' | ||||
|             nl2br: false | ||||
|             max_length: 0 | ||||
|             word_boundary: true | ||||
|             ellipsis: true | ||||
|             more_link: false | ||||
|             more_link_text: '' | ||||
|             more_link_path: '' | ||||
|             strip_tags: false | ||||
|             trim: false | ||||
|             preserve_tags: '' | ||||
|             html: false | ||||
|           element_type: '' | ||||
|           element_class: '' | ||||
|           element_label_type: '' | ||||
|           element_label_class: '' | ||||
|           element_label_colon: true | ||||
|           element_wrapper_type: '' | ||||
|           element_wrapper_class: '' | ||||
|           element_default_classes: true | ||||
|           empty: '' | ||||
|           hide_empty: false | ||||
|           empty_zero: false | ||||
|           hide_alter_empty: true | ||||
|           click_sort_column: value | ||||
|           type: string | ||||
|           settings: | ||||
|             link_to_entity: true | ||||
|           group_column: value | ||||
|           group_columns: {  } | ||||
|           group_rows: true | ||||
|           delta_limit: 0 | ||||
|           delta_offset: 0 | ||||
|           delta_reversed: false | ||||
|           delta_first_last: false | ||||
|           multi_type: separator | ||||
|           separator: ', ' | ||||
|           field_api_classes: false | ||||
|         type: | ||||
|           id: type | ||||
|           table: block_content_field_data | ||||
|           field: type | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           entity_type: block_content | ||||
|           entity_field: type | ||||
|           plugin_id: field | ||||
|           label: 'Block type' | ||||
|           exclude: false | ||||
|           alter: | ||||
|             alter_text: false | ||||
|             text: '' | ||||
|             make_link: false | ||||
|             path: '' | ||||
|             absolute: false | ||||
|             external: false | ||||
|             replace_spaces: false | ||||
|             path_case: none | ||||
|             trim_whitespace: false | ||||
|             alt: '' | ||||
|             rel: '' | ||||
|             link_class: '' | ||||
|             prefix: '' | ||||
|             suffix: '' | ||||
|             target: '' | ||||
|             nl2br: false | ||||
|             max_length: 0 | ||||
|             word_boundary: true | ||||
|             ellipsis: true | ||||
|             more_link: false | ||||
|             more_link_text: '' | ||||
|             more_link_path: '' | ||||
|             strip_tags: false | ||||
|             trim: false | ||||
|             preserve_tags: '' | ||||
|             html: false | ||||
|           element_type: '' | ||||
|           element_class: '' | ||||
|           element_label_type: '' | ||||
|           element_label_class: '' | ||||
|           element_label_colon: true | ||||
|           element_wrapper_type: '' | ||||
|           element_wrapper_class: '' | ||||
|           element_default_classes: true | ||||
|           empty: '' | ||||
|           hide_empty: false | ||||
|           empty_zero: false | ||||
|           hide_alter_empty: true | ||||
|           click_sort_column: target_id | ||||
|           type: entity_reference_label | ||||
|           settings: | ||||
|             link: false | ||||
|           group_column: target_id | ||||
|           group_columns: {  } | ||||
|           group_rows: true | ||||
|           delta_limit: 0 | ||||
|           delta_offset: 0 | ||||
|           delta_reversed: false | ||||
|           delta_first_last: false | ||||
|           multi_type: separator | ||||
|           separator: ', ' | ||||
|           field_api_classes: false | ||||
|         changed: | ||||
|           id: changed | ||||
|           table: block_content_field_data | ||||
|           field: changed | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           entity_type: block_content | ||||
|           entity_field: changed | ||||
|           plugin_id: field | ||||
|           label: Updated | ||||
|           exclude: false | ||||
|           alter: | ||||
|             alter_text: false | ||||
|             text: '' | ||||
|             make_link: false | ||||
|             path: '' | ||||
|             absolute: false | ||||
|             external: false | ||||
|             replace_spaces: false | ||||
|             path_case: none | ||||
|             trim_whitespace: false | ||||
|             alt: '' | ||||
|             rel: '' | ||||
|             link_class: '' | ||||
|             prefix: '' | ||||
|             suffix: '' | ||||
|             target: '' | ||||
|             nl2br: false | ||||
|             max_length: 0 | ||||
|             word_boundary: true | ||||
|             ellipsis: true | ||||
|             more_link: false | ||||
|             more_link_text: '' | ||||
|             more_link_path: '' | ||||
|             strip_tags: false | ||||
|             trim: false | ||||
|             preserve_tags: '' | ||||
|             html: false | ||||
|           element_type: '' | ||||
|           element_class: '' | ||||
|           element_label_type: '' | ||||
|           element_label_class: '' | ||||
|           element_label_colon: true | ||||
|           element_wrapper_type: '' | ||||
|           element_wrapper_class: '' | ||||
|           element_default_classes: true | ||||
|           empty: '' | ||||
|           hide_empty: false | ||||
|           empty_zero: false | ||||
|           hide_alter_empty: true | ||||
|           type: timestamp | ||||
|           settings: | ||||
|             date_format: short | ||||
|             custom_date_format: '' | ||||
|             timezone: '' | ||||
|             tooltip: | ||||
|               date_format: long | ||||
|               custom_date_format: '' | ||||
|             time_diff: | ||||
|               enabled: false | ||||
|               future_format: '@interval hence' | ||||
|               past_format: '@interval ago' | ||||
|               granularity: 2 | ||||
|               refresh: 60 | ||||
|         operations: | ||||
|           id: operations | ||||
|           table: block_content | ||||
|           field: operations | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           entity_type: block_content | ||||
|           plugin_id: entity_operations | ||||
|           label: Operations | ||||
|           exclude: false | ||||
|           alter: | ||||
|             alter_text: false | ||||
|             text: '' | ||||
|             make_link: false | ||||
|             path: '' | ||||
|             absolute: false | ||||
|             external: false | ||||
|             replace_spaces: false | ||||
|             path_case: none | ||||
|             trim_whitespace: false | ||||
|             alt: '' | ||||
|             rel: '' | ||||
|             link_class: '' | ||||
|             prefix: '' | ||||
|             suffix: '' | ||||
|             target: '' | ||||
|             nl2br: false | ||||
|             max_length: 0 | ||||
|             word_boundary: true | ||||
|             ellipsis: true | ||||
|             more_link: false | ||||
|             more_link_text: '' | ||||
|             more_link_path: '' | ||||
|             strip_tags: false | ||||
|             trim: false | ||||
|             preserve_tags: '' | ||||
|             html: false | ||||
|           element_type: '' | ||||
|           element_class: '' | ||||
|           element_label_type: '' | ||||
|           element_label_class: '' | ||||
|           element_label_colon: true | ||||
|           element_wrapper_type: '' | ||||
|           element_wrapper_class: '' | ||||
|           element_default_classes: true | ||||
|           empty: '' | ||||
|           hide_empty: false | ||||
|           empty_zero: false | ||||
|           hide_alter_empty: true | ||||
|           destination: true | ||||
|       pager: | ||||
|         type: mini | ||||
|         options: | ||||
|           offset: 0 | ||||
|           pagination_heading_level: h4 | ||||
|           items_per_page: 50 | ||||
|           total_pages: null | ||||
|           id: 0 | ||||
|           tags: | ||||
|             next: 'Next ›' | ||||
|             previous: '‹ Previous' | ||||
|           expose: | ||||
|             items_per_page: false | ||||
|             items_per_page_label: 'Items per page' | ||||
|             items_per_page_options: '5, 10, 25, 50' | ||||
|             items_per_page_options_all: false | ||||
|             items_per_page_options_all_label: '- All -' | ||||
|             offset: false | ||||
|             offset_label: Offset | ||||
|       exposed_form: | ||||
|         type: basic | ||||
|         options: | ||||
|           submit_button: Apply | ||||
|           reset_button: true | ||||
|           reset_button_label: Reset | ||||
|           exposed_sorts_label: 'Sort by' | ||||
|           expose_sort_order: true | ||||
|           sort_asc_label: Asc | ||||
|           sort_desc_label: Desc | ||||
|       access: | ||||
|         type: perm | ||||
|         options: | ||||
|           perm: 'access block library' | ||||
|       cache: | ||||
|         type: tag | ||||
|         options: {  } | ||||
|       empty: | ||||
|         area_text_custom: | ||||
|           id: area_text_custom | ||||
|           table: views | ||||
|           field: area_text_custom | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           plugin_id: text_custom | ||||
|           empty: true | ||||
|           content: 'There are no content blocks available.' | ||||
|           tokenize: false | ||||
|         block_content_listing_empty: | ||||
|           id: block_content_listing_empty | ||||
|           table: block_content | ||||
|           field: block_content_listing_empty | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           entity_type: block_content | ||||
|           plugin_id: block_content_listing_empty | ||||
|           label: '' | ||||
|           empty: true | ||||
|       sorts: {  } | ||||
|       arguments: {  } | ||||
|       filters: | ||||
|         info: | ||||
|           id: info | ||||
|           table: block_content_field_data | ||||
|           field: info | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           entity_type: block_content | ||||
|           entity_field: info | ||||
|           plugin_id: string | ||||
|           operator: contains | ||||
|           value: '' | ||||
|           group: 1 | ||||
|           exposed: true | ||||
|           expose: | ||||
|             operator_id: info_op | ||||
|             label: 'Block description' | ||||
|             description: '' | ||||
|             use_operator: false | ||||
|             operator: info_op | ||||
|             operator_limit_selection: false | ||||
|             operator_list: {  } | ||||
|             identifier: info | ||||
|             required: false | ||||
|             remember: false | ||||
|             multiple: false | ||||
|             remember_roles: | ||||
|               authenticated: authenticated | ||||
|               anonymous: '0' | ||||
|               administrator: '0' | ||||
|           is_grouped: false | ||||
|           group_info: | ||||
|             label: '' | ||||
|             description: '' | ||||
|             identifier: '' | ||||
|             optional: true | ||||
|             widget: select | ||||
|             multiple: false | ||||
|             remember: false | ||||
|             default_group: All | ||||
|             default_group_multiple: {  } | ||||
|             group_items: {  } | ||||
|         type: | ||||
|           id: type | ||||
|           table: block_content_field_data | ||||
|           field: type | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           entity_type: block_content | ||||
|           entity_field: type | ||||
|           plugin_id: bundle | ||||
|           operator: in | ||||
|           value: {  } | ||||
|           group: 1 | ||||
|           exposed: true | ||||
|           expose: | ||||
|             operator_id: type_op | ||||
|             label: 'Block type' | ||||
|             description: '' | ||||
|             use_operator: false | ||||
|             operator: type_op | ||||
|             operator_limit_selection: false | ||||
|             operator_list: {  } | ||||
|             identifier: type | ||||
|             required: false | ||||
|             remember: false | ||||
|             multiple: false | ||||
|             remember_roles: | ||||
|               authenticated: authenticated | ||||
|               anonymous: '0' | ||||
|               administrator: '0' | ||||
|             reduce: false | ||||
|           is_grouped: false | ||||
|           group_info: | ||||
|             label: '' | ||||
|             description: '' | ||||
|             identifier: '' | ||||
|             optional: true | ||||
|             widget: select | ||||
|             multiple: false | ||||
|             remember: false | ||||
|             default_group: All | ||||
|             default_group_multiple: {  } | ||||
|             group_items: {  } | ||||
|         reusable: | ||||
|           id: reusable | ||||
|           table: block_content_field_data | ||||
|           field: reusable | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           entity_type: block_content | ||||
|           entity_field: reusable | ||||
|           plugin_id: boolean | ||||
|           operator: '=' | ||||
|           value: '1' | ||||
|           group: 1 | ||||
|           exposed: false | ||||
|           expose: | ||||
|             operator_id: '' | ||||
|             label: '' | ||||
|             description: '' | ||||
|             use_operator: false | ||||
|             operator: '' | ||||
|             operator_limit_selection: false | ||||
|             operator_list: {  } | ||||
|             identifier: '' | ||||
|             required: false | ||||
|             remember: false | ||||
|             multiple: false | ||||
|             remember_roles: | ||||
|               authenticated: authenticated | ||||
|           is_grouped: false | ||||
|           group_info: | ||||
|             label: '' | ||||
|             description: '' | ||||
|             identifier: '' | ||||
|             optional: true | ||||
|             widget: select | ||||
|             multiple: false | ||||
|             remember: false | ||||
|             default_group: All | ||||
|             default_group_multiple: {  } | ||||
|             group_items: {  } | ||||
|       style: | ||||
|         type: table | ||||
|         options: | ||||
|           grouping: {  } | ||||
|           row_class: '' | ||||
|           default_row_class: true | ||||
|           columns: | ||||
|             info: info | ||||
|             type: type | ||||
|             changed: changed | ||||
|             operations: operations | ||||
|           default: changed | ||||
|           info: | ||||
|             info: | ||||
|               sortable: true | ||||
|               default_sort_order: asc | ||||
|               align: '' | ||||
|               separator: '' | ||||
|               empty_column: false | ||||
|               responsive: '' | ||||
|             type: | ||||
|               sortable: true | ||||
|               default_sort_order: asc | ||||
|               align: '' | ||||
|               separator: '' | ||||
|               empty_column: false | ||||
|               responsive: '' | ||||
|             changed: | ||||
|               sortable: true | ||||
|               default_sort_order: desc | ||||
|               align: '' | ||||
|               separator: '' | ||||
|               empty_column: false | ||||
|               responsive: '' | ||||
|             operations: | ||||
|               sortable: false | ||||
|               default_sort_order: asc | ||||
|               align: '' | ||||
|               separator: '' | ||||
|               empty_column: false | ||||
|               responsive: '' | ||||
|           override: true | ||||
|           sticky: false | ||||
|           summary: '' | ||||
|           empty_table: true | ||||
|           caption: '' | ||||
|           description: '' | ||||
|       row: | ||||
|         type: fields | ||||
|       query: | ||||
|         type: views_query | ||||
|         options: | ||||
|           query_comment: '' | ||||
|           disable_sql_rewrite: false | ||||
|           distinct: false | ||||
|           replica: false | ||||
|           query_tags: {  } | ||||
|       relationships: {  } | ||||
|       header: {  } | ||||
|       footer: {  } | ||||
|       display_extenders: {  } | ||||
|     cache_metadata: | ||||
|       max-age: -1 | ||||
|       contexts: | ||||
|         - 'languages:language_content' | ||||
|         - 'languages:language_interface' | ||||
|         - url | ||||
|         - url.query_args | ||||
|         - user.permissions | ||||
|       tags: {  } | ||||
|   page_1: | ||||
|     id: page_1 | ||||
|     display_title: Page | ||||
|     display_plugin: page | ||||
|     position: 1 | ||||
|     display_options: | ||||
|       display_extenders: {  } | ||||
|       path: admin/content/block | ||||
|       menu: | ||||
|         type: tab | ||||
|         title: 'Blocks' | ||||
|         description: 'Create and edit content blocks.' | ||||
|         weight: 0 | ||||
|         menu_name: admin | ||||
|         parent: system.admin_content | ||||
|         context: '0' | ||||
|     cache_metadata: | ||||
|       max-age: -1 | ||||
|       contexts: | ||||
|         - 'languages:language_content' | ||||
|         - 'languages:language_interface' | ||||
|         - url | ||||
|         - url.query_args | ||||
|         - user.permissions | ||||
|       tags: {  } | ||||
							
								
								
									
										24
									
								
								config/schema/block_content.schema.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								config/schema/block_content.schema.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | ||||
| # Schema for the configuration files of the Content Block module. | ||||
| 
 | ||||
| block_content.type.*: | ||||
|   type: config_entity | ||||
|   label: 'Block type settings' | ||||
|   mapping: | ||||
|     id: | ||||
|       type: machine_name | ||||
|       label: 'ID' | ||||
|     label: | ||||
|       type: required_label | ||||
|       label: 'Label' | ||||
|     revision: | ||||
|       type: boolean | ||||
|       label: 'Whether a new revision should be created by default' | ||||
|     description: | ||||
|       type: text | ||||
|       label: 'Description' | ||||
|       nullable: true | ||||
|       constraints: | ||||
|         NotBlank: | ||||
|           allowNull: true | ||||
|   constraints: | ||||
|     FullyValidatable: ~ | ||||
							
								
								
									
										20
									
								
								help_topics/block_content.add.html.twig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								help_topics/block_content.add.html.twig
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | ||||
| --- | ||||
| label: 'Creating a content block' | ||||
| related: | ||||
|   - block.overview | ||||
|   - block.configure | ||||
|   - block.place | ||||
|   - block_content.type | ||||
| --- | ||||
| {% set library_link_text %}{% trans %}Content blocks{% endtrans %}{% endset %} | ||||
| {% set library_link = render_var(help_route_link(library_link_text, 'entity.block_content.collection')) %} | ||||
| <h2>{% trans %}Goal{% endtrans %}</h2> | ||||
| <p>{% trans %}Create a content block, which can later be placed on the site.{% endtrans %}</p> | ||||
| <h2>{% trans %}Steps{% endtrans %}</h2> | ||||
| <ol> | ||||
|   <li>{% trans %}In the <em>Manage</em> administrative menu, navigate to <em>Content</em>.{% endtrans %}</li> | ||||
|   <li>{% trans %}Open the {{ library_link }} tab.{% endtrans %}</li> | ||||
|   <li>{% trans %}Click <em>Add content block</em>. If you have more than one block type defined on your site, click the name of the type you want to create.{% endtrans %}</li> | ||||
|   <li>{% trans %}Enter a description of your block (to be shown to administrators) and the body text for your block.{% endtrans %}</li> | ||||
|   <li>{% trans %}Click <em>Save</em>.{% endtrans %}</li> | ||||
| </ol> | ||||
							
								
								
									
										24
									
								
								help_topics/block_content.type.html.twig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								help_topics/block_content.type.html.twig
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | ||||
| --- | ||||
| label: 'Defining a block type' | ||||
| related: | ||||
|   - block.overview | ||||
|   - block.configure | ||||
|   - block.place | ||||
|   - block_content.add | ||||
|   - field_ui.add_field | ||||
|   - field_ui.manage_form | ||||
|   - field_ui.manage_display | ||||
| --- | ||||
| {% set types_link_text %}{% trans %}Block types{% endtrans %}{% endset %} | ||||
| {% set types_link = render_var(help_route_link(types_link_text, 'entity.block_content_type.collection')) %} | ||||
| <h2>{% trans %}Goal{% endtrans %}</h2> | ||||
| <p>{% trans %}Define a block type and its fields.{% endtrans %}</p> | ||||
| <h2>{% trans %}Steps{% endtrans %}</h2> | ||||
| <ol> | ||||
|   <li>{% trans %}In the <em>Manage</em> administrative menu, navigate to <em>Structure</em> > <em>{{ types_link }}</em>.{% endtrans %}</li> | ||||
|   <li>{% trans %}Click  <em>Add block type</em>.{% endtrans %}</li> | ||||
|   <li>{% trans %}Enter a label for this block type (shown in the administrative interface). Optionally, edit the automatically-generated machine name or the description.{% endtrans %}</li> | ||||
|   <li>{% trans %}Click <em>Save</em>. You will be returned to the <em>Block types</em> page.{% endtrans %}</li> | ||||
|   <li>{% trans %}Click <em>Manage fields</em> in the row of your new block type, and add the desired fields to your block type.{% endtrans %}</li> | ||||
|   <li>{% trans %}Optionally, click <em>Manage form display</em> or <em>Manage display</em> to change the editing form or field display for your block type.{% endtrans %}</li> | ||||
| </ol> | ||||
							
								
								
									
										38
									
								
								migrations/block_content_body_field.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								migrations/block_content_body_field.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | ||||
| id: block_content_body_field | ||||
| label: Block content body field configuration | ||||
| migration_tags: | ||||
|   - Drupal 6 | ||||
|   - Drupal 7 | ||||
|   - Configuration | ||||
| source: | ||||
|   plugin: embedded_data | ||||
|   data_rows: | ||||
|     - | ||||
|       entity_type: block_content | ||||
|       bundle: basic | ||||
|       field_name: body | ||||
|       label: Body | ||||
|       display_summary: false | ||||
|       allowed_formats: {  } | ||||
|   ids: | ||||
|     entity_type: | ||||
|       type: string | ||||
|     bundle: | ||||
|       type: string | ||||
|     field_name: | ||||
|       type: string | ||||
|   source_module: block | ||||
| process: | ||||
|   entity_type: entity_type | ||||
|   bundle: bundle | ||||
|   field_name: field_name | ||||
|   label: label | ||||
|   'settings/display_summary': display_summary | ||||
| destination: | ||||
|   plugin: entity:field_config | ||||
| migration_dependencies: | ||||
|   required: | ||||
|     - block_content_type | ||||
| provider: | ||||
|   - block_content | ||||
|   - migrate_drupal | ||||
							
								
								
									
										40
									
								
								migrations/block_content_entity_display.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								migrations/block_content_entity_display.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,40 @@ | ||||
| id: block_content_entity_display | ||||
| label: Body field display configuration | ||||
| migration_tags: | ||||
|   - Drupal 6 | ||||
|   - Drupal 7 | ||||
|   - Configuration | ||||
| source: | ||||
|   plugin: embedded_data | ||||
|   data_rows: | ||||
|     - | ||||
|       entity_type: block_content | ||||
|       bundle: basic | ||||
|       view_mode: default | ||||
|       field_name: body | ||||
|       options: | ||||
|         label: hidden | ||||
|   ids: | ||||
|     entity_type: | ||||
|       type: string | ||||
|     bundle: | ||||
|       type: string | ||||
|     view_mode: | ||||
|       type: string | ||||
|     field_name: | ||||
|       type: string | ||||
|   source_module: block | ||||
| process: | ||||
|   entity_type: entity_type | ||||
|   bundle: bundle | ||||
|   view_mode: view_mode | ||||
|   field_name: field_name | ||||
|   options: options | ||||
| destination: | ||||
|   plugin: component_entity_display | ||||
| migration_dependencies: | ||||
|   required: | ||||
|     - block_content_body_field | ||||
| provider: | ||||
|   - block_content | ||||
|   - migrate_drupal | ||||
							
								
								
									
										37
									
								
								migrations/block_content_entity_form_display.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								migrations/block_content_entity_form_display.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | ||||
| id: block_content_entity_form_display | ||||
| label: Body field form display configuration | ||||
| migration_tags: | ||||
|   - Drupal 6 | ||||
|   - Drupal 7 | ||||
|   - Configuration | ||||
| source: | ||||
|   plugin: embedded_data | ||||
|   data_rows: | ||||
|     - | ||||
|       entity_type: block_content | ||||
|       bundle: basic | ||||
|       form_mode: default | ||||
|       field_name: body | ||||
|   ids: | ||||
|     entity_type: | ||||
|       type: string | ||||
|     bundle: | ||||
|       type: string | ||||
|     form_mode: | ||||
|       type: string | ||||
|     field_name: | ||||
|       type: string | ||||
|   source_module: block | ||||
| process: | ||||
|   entity_type: entity_type | ||||
|   bundle: bundle | ||||
|   form_mode: form_mode | ||||
|   field_name: field_name | ||||
| destination: | ||||
|   plugin: component_entity_form_display | ||||
| migration_dependencies: | ||||
|   required: | ||||
|     - block_content_body_field | ||||
| provider: | ||||
|   - block_content | ||||
|   - migrate_drupal | ||||
							
								
								
									
										24
									
								
								migrations/block_content_type.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								migrations/block_content_type.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | ||||
| id: block_content_type | ||||
| label: Block content type | ||||
| migration_tags: | ||||
|   - Drupal 6 | ||||
|   - Drupal 7 | ||||
|   - Configuration | ||||
| source: | ||||
|   plugin: embedded_data | ||||
|   data_rows: | ||||
|     - | ||||
|       id: basic | ||||
|       label: Basic | ||||
|   ids: | ||||
|     id: | ||||
|       type: string | ||||
|   source_module: block | ||||
| process: | ||||
|   id: id | ||||
|   label: label | ||||
| destination: | ||||
|   plugin: entity:block_content_type | ||||
| provider: | ||||
|   - block_content | ||||
|   - migrate_drupal | ||||
							
								
								
									
										24
									
								
								migrations/d6_custom_block.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								migrations/d6_custom_block.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | ||||
| id: d6_custom_block | ||||
| label: Content blocks | ||||
| audit: true | ||||
| migration_tags: | ||||
|   - Drupal 6 | ||||
|   - Content | ||||
| source: | ||||
|   plugin: d6_box | ||||
| process: | ||||
|   id: bid | ||||
|   info: info | ||||
|   'body/format': | ||||
|     plugin: migration_lookup | ||||
|     migration: d6_filter_format | ||||
|     source: format | ||||
|   'body/value': body | ||||
| destination: | ||||
|   plugin: entity:block_content | ||||
|   default_bundle: basic | ||||
|   no_stub: true | ||||
| migration_dependencies: | ||||
|   required: | ||||
|     - d6_filter_format | ||||
|     - block_content_body_field | ||||
							
								
								
									
										24
									
								
								migrations/d7_custom_block.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								migrations/d7_custom_block.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | ||||
| id: d7_custom_block | ||||
| label: Content blocks | ||||
| audit: true | ||||
| migration_tags: | ||||
|   - Drupal 7 | ||||
|   - Content | ||||
| source: | ||||
|   plugin: d7_block_custom | ||||
| process: | ||||
|   id: bid | ||||
|   info: info | ||||
|   'body/format': | ||||
|     plugin: migration_lookup | ||||
|     migration: d7_filter_format | ||||
|     source: format | ||||
|   'body/value': body | ||||
| destination: | ||||
|   plugin: entity:block_content | ||||
|   default_bundle: basic | ||||
|   no_stub: true | ||||
| migration_dependencies: | ||||
|   required: | ||||
|     - d7_filter_format | ||||
|     - block_content_body_field | ||||
							
								
								
									
										5
									
								
								migrations/state/block_content.migrate_drupal.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								migrations/state/block_content.migrate_drupal.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| finished: | ||||
|   6: | ||||
|     block: block_content | ||||
|   7: | ||||
|     block: block_content | ||||
							
								
								
									
										49
									
								
								src/Access/AccessGroupAnd.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/Access/AccessGroupAnd.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,49 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content\Access; | ||||
| 
 | ||||
| use Drupal\Core\Access\AccessibleInterface; | ||||
| use Drupal\Core\Access\AccessResult; | ||||
| use Drupal\Core\Session\AccountInterface; | ||||
| 
 | ||||
| /** | ||||
|  * An access group where all the dependencies must be allowed. | ||||
|  * | ||||
|  * @internal | ||||
|  */ | ||||
| class AccessGroupAnd implements AccessibleInterface { | ||||
| 
 | ||||
|   /** | ||||
|    * The access dependencies. | ||||
|    * | ||||
|    * @var \Drupal\Core\Access\AccessibleInterface[] | ||||
|    */ | ||||
|   protected $dependencies = []; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function addDependency(AccessibleInterface $dependency) { | ||||
|     $this->dependencies[] = $dependency; | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function access($operation, ?AccountInterface $account = NULL, $return_as_object = FALSE) { | ||||
|     $access_result = AccessResult::neutral(); | ||||
|     foreach (array_slice($this->dependencies, 1) as $dependency) { | ||||
|       $access_result = $access_result->andIf($dependency->access($operation, $account, TRUE)); | ||||
|     } | ||||
|     return $return_as_object ? $access_result : $access_result->isAllowed(); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function getDependencies() { | ||||
|     return $this->dependencies; | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										35
									
								
								src/Access/DependentAccessInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/Access/DependentAccessInterface.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content\Access; | ||||
| 
 | ||||
| /** | ||||
|  * Interface for AccessibleInterface objects that have an access dependency. | ||||
|  * | ||||
|  * Objects should implement this interface when their access depends on access | ||||
|  * to another object that implements \Drupal\Core\Access\AccessibleInterface. | ||||
|  * This interface simply provides the getter method for the access | ||||
|  * dependency object. Objects that implement this interface are responsible for | ||||
|  * checking access of the access dependency because the dependency may not take | ||||
|  * effect in all cases. For instance an entity may only need the access | ||||
|  * dependency set when it is embedded within another entity and its access | ||||
|  * should be dependent on access to the entity in which it is embedded. | ||||
|  * | ||||
|  * To check the access to the dependency the object implementing this interface | ||||
|  * can use code like this: | ||||
|  * @code | ||||
|  * $accessible->getAccessDependency()->access($op, $account, TRUE); | ||||
|  * @endcode | ||||
|  * | ||||
|  * @internal | ||||
|  */ | ||||
| interface DependentAccessInterface { | ||||
| 
 | ||||
|   /** | ||||
|    * Gets the access dependency. | ||||
|    * | ||||
|    * @return \Drupal\Core\Access\AccessibleInterface|null | ||||
|    *   The access dependency or NULL if none has been set. | ||||
|    */ | ||||
|   public function getAccessDependency(); | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										48
									
								
								src/Access/RefinableDependentAccessInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/Access/RefinableDependentAccessInterface.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,48 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content\Access; | ||||
| 
 | ||||
| use Drupal\Core\Access\AccessibleInterface; | ||||
| 
 | ||||
| /** | ||||
|  * An interface to allow adding an access dependency. | ||||
|  * | ||||
|  * @internal | ||||
|  */ | ||||
| interface RefinableDependentAccessInterface extends DependentAccessInterface { | ||||
| 
 | ||||
|   /** | ||||
|    * Sets the access dependency. | ||||
|    * | ||||
|    * If an access dependency is already set this will replace the existing | ||||
|    * dependency. | ||||
|    * | ||||
|    * @param \Drupal\Core\Access\AccessibleInterface $access_dependency | ||||
|    *   The object upon which access depends. | ||||
|    * | ||||
|    * @return $this | ||||
|    */ | ||||
|   public function setAccessDependency(AccessibleInterface $access_dependency); | ||||
| 
 | ||||
|   /** | ||||
|    * Adds an access dependency into the existing access dependency. | ||||
|    * | ||||
|    * If no existing dependency is currently set this will set the dependency | ||||
|    * will be set to the new value. | ||||
|    * | ||||
|    * If there is an existing dependency and it is not an instance of | ||||
|    * AccessGroupAnd the dependency will be set as a new AccessGroupAnd | ||||
|    * instance with the existing and new dependencies as the members of the | ||||
|    * group. | ||||
|    * | ||||
|    * If there is an existing dependency and it is an instance of AccessGroupAnd | ||||
|    * the dependency will be added to the existing access group. | ||||
|    * | ||||
|    * @param \Drupal\Core\Access\AccessibleInterface $access_dependency | ||||
|    *   The access dependency to merge. | ||||
|    * | ||||
|    * @return $this | ||||
|    */ | ||||
|   public function addAccessDependency(AccessibleInterface $access_dependency); | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										52
									
								
								src/Access/RefinableDependentAccessTrait.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/Access/RefinableDependentAccessTrait.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,52 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content\Access; | ||||
| 
 | ||||
| use Drupal\Core\Access\AccessibleInterface; | ||||
| 
 | ||||
| /** | ||||
|  * Trait for \Drupal\block_content\Access\RefinableDependentAccessInterface. | ||||
|  * | ||||
|  * @internal | ||||
|  */ | ||||
| trait RefinableDependentAccessTrait { | ||||
| 
 | ||||
|   /** | ||||
|    * The access dependency. | ||||
|    * | ||||
|    * @var \Drupal\Core\Access\AccessibleInterface | ||||
|    */ | ||||
|   protected $accessDependency; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function setAccessDependency(AccessibleInterface $access_dependency) { | ||||
|     $this->accessDependency = $access_dependency; | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function getAccessDependency() { | ||||
|     return $this->accessDependency; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function addAccessDependency(AccessibleInterface $access_dependency) { | ||||
|     if (empty($this->accessDependency)) { | ||||
|       $this->accessDependency = $access_dependency; | ||||
|       return $this; | ||||
|     } | ||||
|     if (!$this->accessDependency instanceof AccessGroupAnd) { | ||||
|       $accessGroup = new AccessGroupAnd(); | ||||
|       $this->accessDependency = $accessGroup->addDependency($this->accessDependency); | ||||
|     } | ||||
|     $this->accessDependency->addDependency($access_dependency); | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										116
									
								
								src/BlockContentAccessControlHandler.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								src/BlockContentAccessControlHandler.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,116 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content; | ||||
| 
 | ||||
| use Drupal\block_content\Access\DependentAccessInterface; | ||||
| use Drupal\block_content\Event\BlockContentGetDependencyEvent; | ||||
| use Drupal\Core\Access\AccessResult; | ||||
| use Drupal\Core\Access\AccessResultInterface; | ||||
| use Drupal\Core\Entity\EntityAccessControlHandler; | ||||
| use Drupal\Core\Entity\EntityHandlerInterface; | ||||
| use Drupal\Core\Entity\EntityInterface; | ||||
| use Drupal\Core\Entity\EntityTypeInterface; | ||||
| use Drupal\Core\Session\AccountInterface; | ||||
| use Symfony\Component\DependencyInjection\ContainerInterface; | ||||
| use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; | ||||
| 
 | ||||
| /** | ||||
|  * Defines the access control handler for the content block entity type. | ||||
|  * | ||||
|  * @see \Drupal\block_content\Entity\BlockContent | ||||
|  */ | ||||
| class BlockContentAccessControlHandler extends EntityAccessControlHandler implements EntityHandlerInterface { | ||||
| 
 | ||||
|   /** | ||||
|    * The event dispatcher. | ||||
|    * | ||||
|    * @var \Symfony\Contracts\EventDispatcher\EventDispatcherInterface | ||||
|    */ | ||||
|   protected $eventDispatcher; | ||||
| 
 | ||||
|   /** | ||||
|    * BlockContentAccessControlHandler constructor. | ||||
|    * | ||||
|    * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type | ||||
|    *   The entity type. | ||||
|    * @param \Symfony\Contracts\EventDispatcher\EventDispatcherInterface $dispatcher | ||||
|    *   The event dispatcher. | ||||
|    */ | ||||
|   public function __construct(EntityTypeInterface $entity_type, EventDispatcherInterface $dispatcher) { | ||||
|     parent::__construct($entity_type); | ||||
|     $this->eventDispatcher = $dispatcher; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) { | ||||
|     return new static( | ||||
|       $entity_type, | ||||
|       $container->get('event_dispatcher') | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) { | ||||
|     assert($entity instanceof BlockContentInterface); | ||||
|     $bundle = $entity->bundle(); | ||||
|     $forbidIfNotReusable = fn (): AccessResultInterface => AccessResult::forbiddenIf($entity->isReusable() === FALSE, sprintf('Block content must be reusable to use `%s` operation', $operation)); | ||||
|     $access = AccessResult::allowedIfHasPermissions($account, ['administer block content']); | ||||
|     if (!$access->isAllowed()) { | ||||
|       $access = match ($operation) { | ||||
|         // Allow view and update access to user with the 'edit any (type) block
 | ||||
|         // content' permission or the 'administer block content' permission.
 | ||||
|         'view' => AccessResult::allowedIf($entity->isPublished()) | ||||
|           ->orIf(AccessResult::allowedIfHasPermission($account, 'access block library')), | ||||
|         'update' => AccessResult::allowedIfHasPermission($account, 'edit any ' . $bundle . ' block content'), | ||||
|         'delete' => AccessResult::allowedIfHasPermission($account, 'delete any ' . $bundle . ' block content'), | ||||
|         // Revisions.
 | ||||
|         'view revision', 'view all revisions' => AccessResult::allowedIfHasPermission($account, 'view any ' . $bundle . ' block content history'), | ||||
|         'revert' => AccessResult::allowedIfHasPermission($account, 'revert any ' . $bundle . ' block content revisions') | ||||
|           ->orIf($forbidIfNotReusable()), | ||||
|         'delete revision' => AccessResult::allowedIfHasPermission($account, 'delete any ' . $bundle . ' block content revisions') | ||||
|           ->orIf($forbidIfNotReusable()), | ||||
| 
 | ||||
|         default => parent::checkAccess($entity, $operation, $account), | ||||
|       }; | ||||
|     } | ||||
| 
 | ||||
|     // Add the entity as a cacheable dependency because access will at least be
 | ||||
|     // determined by whether the block is reusable.
 | ||||
|     $access->addCacheableDependency($entity); | ||||
|     if ($entity->isReusable() === FALSE && $access->isForbidden() !== TRUE) { | ||||
|       if (!$entity instanceof DependentAccessInterface) { | ||||
|         throw new \LogicException("Non-reusable block entities must implement \Drupal\block_content\Access\DependentAccessInterface for access control."); | ||||
|       } | ||||
|       $dependency = $entity->getAccessDependency(); | ||||
|       if (empty($dependency)) { | ||||
|         // If an access dependency has not been set let modules set one.
 | ||||
|         $event = new BlockContentGetDependencyEvent($entity); | ||||
|         $this->eventDispatcher->dispatch($event, BlockContentEvents::BLOCK_CONTENT_GET_DEPENDENCY); | ||||
|         $dependency = $event->getAccessDependency(); | ||||
|         if (empty($dependency)) { | ||||
|           return AccessResult::forbidden("Non-reusable blocks must set an access dependency for access control."); | ||||
|         } | ||||
|       } | ||||
|       /** @var \Drupal\Core\Entity\EntityInterface $dependency */ | ||||
|       $access = $access->andIf($dependency->access($operation, $account, TRUE)); | ||||
|     } | ||||
|     return $access; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) { | ||||
|     return AccessResult::allowedIfHasPermissions($account, [ | ||||
|       'create ' . $entity_bundle . ' block content', | ||||
|       'access block library', | ||||
|     ])->orIf(AccessResult::allowedIfHasPermissions($account, [ | ||||
|       'administer block content', | ||||
|     ])); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										31
									
								
								src/BlockContentEvents.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/BlockContentEvents.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content; | ||||
| 
 | ||||
| /** | ||||
|  * Defines events for the block_content module. | ||||
|  * | ||||
|  * @see \Drupal\block_content\Event\BlockContentGetDependencyEvent | ||||
|  * | ||||
|  * @internal | ||||
|  */ | ||||
| final class BlockContentEvents { | ||||
| 
 | ||||
|   /** | ||||
|    * Name of the event when getting the dependency of a non-reusable block. | ||||
|    * | ||||
|    * This event allows modules to provide a dependency for non-reusable block | ||||
|    * access if | ||||
|    * \Drupal\block_content\Access\DependentAccessInterface::getAccessDependency() | ||||
|    * did not return a dependency during access checking. | ||||
|    * | ||||
|    * @Event | ||||
|    * | ||||
|    * @see \Drupal\block_content\Event\BlockContentGetDependencyEvent | ||||
|    * @see \Drupal\block_content\BlockContentAccessControlHandler::checkAccess() | ||||
|    * | ||||
|    * @var string | ||||
|    */ | ||||
|   const BLOCK_CONTENT_GET_DEPENDENCY = 'block_content.get_dependency'; | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										134
									
								
								src/BlockContentForm.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								src/BlockContentForm.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,134 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content; | ||||
| 
 | ||||
| use Drupal\Component\Utility\Html; | ||||
| use Drupal\Core\Entity\ContentEntityForm; | ||||
| use Drupal\Core\Form\FormStateInterface; | ||||
| 
 | ||||
| /** | ||||
|  * Form handler for the content block edit forms. | ||||
|  * | ||||
|  * @internal | ||||
|  */ | ||||
| class BlockContentForm extends ContentEntityForm { | ||||
| 
 | ||||
|   /** | ||||
|    * The block content entity. | ||||
|    * | ||||
|    * @var \Drupal\block_content\BlockContentInterface | ||||
|    */ | ||||
|   protected $entity; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function form(array $form, FormStateInterface $form_state) { | ||||
|     $block = $this->entity; | ||||
|     $form = parent::form($form, $form_state); | ||||
| 
 | ||||
|     if ($this->operation == 'edit') { | ||||
|       $form['#title'] = $this->t('Edit content block %label', ['%label' => $block->label()]); | ||||
|     } | ||||
|     // Override the default CSS class name, since the user-defined content block
 | ||||
|     // type name in 'TYPE-block-form' potentially clashes with third-party class
 | ||||
|     // names.
 | ||||
|     $form['#attributes']['class'][0] = 'block-' . Html::getClass($block->bundle()) . '-form'; | ||||
| 
 | ||||
|     return $form; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function actions(array $form, FormStateInterface $form_state): array { | ||||
|     $element = parent::actions($form, $form_state); | ||||
| 
 | ||||
|     if ($this->getRequest()->query->has('theme')) { | ||||
|       $element['submit']['#value'] = $this->t('Save and configure'); | ||||
|     } | ||||
| 
 | ||||
|     if ($this->currentUser()->hasPermission('administer blocks') && !$this->getRequest()->query->has('theme') && $this->entity->isNew()) { | ||||
|       $element['configure_block'] = [ | ||||
|         '#type' => 'submit', | ||||
|         '#value' => $this->t('Save and configure'), | ||||
|         '#weight' => 20, | ||||
|         '#submit' => array_merge($element['submit']['#submit'], ['::configureBlock']), | ||||
|       ]; | ||||
|     } | ||||
| 
 | ||||
|     return $element; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Form submission handler for the 'configureBlock' action. | ||||
|    * | ||||
|    * @param array $form | ||||
|    *   An associative array containing the structure of the form. | ||||
|    * @param \Drupal\Core\Form\FormStateInterface $form_state | ||||
|    *   The current state of the form. | ||||
|    */ | ||||
|   public function configureBlock(array $form, FormStateInterface $form_state): void { | ||||
|     $block = $this->entity; | ||||
|     if (!$theme = $block->getTheme()) { | ||||
|       $theme = $this->config('system.theme')->get('default'); | ||||
|     } | ||||
|     $form_state->setRedirect( | ||||
|       'block.admin_add', | ||||
|       [ | ||||
|         'plugin_id' => 'block_content:' . $block->uuid(), | ||||
|         'theme' => $theme, | ||||
|       ] | ||||
|     ); | ||||
|     $form_state->setIgnoreDestination(); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function save(array $form, FormStateInterface $form_state) { | ||||
|     $block = $this->entity; | ||||
| 
 | ||||
|     $insert = $block->isNew(); | ||||
|     $block->save(); | ||||
|     $context = ['@type' => $block->bundle(), '%info' => $block->label()]; | ||||
|     $logger = $this->logger('block_content'); | ||||
|     $block_type = $this->getBundleEntity(); | ||||
|     $t_args = ['@type' => $block_type->label(), '%info' => $block->label()]; | ||||
| 
 | ||||
|     if ($insert) { | ||||
|       $logger->info('@type: added %info.', $context); | ||||
|       $this->messenger()->addStatus($this->t('@type %info has been created.', $t_args)); | ||||
|     } | ||||
|     else { | ||||
|       $logger->info('@type: updated %info.', $context); | ||||
|       $this->messenger()->addStatus($this->t('@type %info has been updated.', $t_args)); | ||||
|     } | ||||
| 
 | ||||
|     if ($block->id()) { | ||||
|       $form_state->setValue('id', $block->id()); | ||||
|       $form_state->set('id', $block->id()); | ||||
|       $theme = $block->getTheme(); | ||||
|       if ($insert && $theme) { | ||||
|         $form_state->setRedirect( | ||||
|           'block.admin_add', | ||||
|           [ | ||||
|             'plugin_id' => 'block_content:' . $block->uuid(), | ||||
|             'theme' => $theme, | ||||
|             'region' => $this->getRequest()->query->getString('region'), | ||||
|           ] | ||||
|         ); | ||||
|       } | ||||
|       else { | ||||
|         $form_state->setRedirectUrl($block->toUrl('collection')); | ||||
|       } | ||||
|     } | ||||
|     else { | ||||
|       // In the unlikely case something went wrong on save, the block will be
 | ||||
|       // rebuilt and block form redisplayed.
 | ||||
|       $this->messenger()->addError($this->t('The block could not be saved.')); | ||||
|       $form_state->setRebuild(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										84
									
								
								src/BlockContentInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								src/BlockContentInterface.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,84 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content; | ||||
| 
 | ||||
| use Drupal\block_content\Access\RefinableDependentAccessInterface; | ||||
| use Drupal\Core\Entity\ContentEntityInterface; | ||||
| use Drupal\Core\Entity\EntityChangedInterface; | ||||
| use Drupal\Core\Entity\EntityPublishedInterface; | ||||
| use Drupal\Core\Entity\RevisionLogInterface; | ||||
| 
 | ||||
| /** | ||||
|  * Provides an interface defining a content block entity. | ||||
|  */ | ||||
| interface BlockContentInterface extends ContentEntityInterface, EntityChangedInterface, RevisionLogInterface, EntityPublishedInterface, RefinableDependentAccessInterface { | ||||
| 
 | ||||
|   /** | ||||
|    * Sets the block description. | ||||
|    * | ||||
|    * @param string $info | ||||
|    *   The block description. | ||||
|    * | ||||
|    * @return $this | ||||
|    *   The class instance that this method is called on. | ||||
|    */ | ||||
|   public function setInfo($info); | ||||
| 
 | ||||
|   /** | ||||
|    * Determines if the block is reusable or not. | ||||
|    * | ||||
|    * @return bool | ||||
|    *   Returns TRUE if reusable and FALSE otherwise. | ||||
|    */ | ||||
|   public function isReusable(); | ||||
| 
 | ||||
|   /** | ||||
|    * Sets the block to be reusable. | ||||
|    * | ||||
|    * @return $this | ||||
|    */ | ||||
|   public function setReusable(); | ||||
| 
 | ||||
|   /** | ||||
|    * Sets the block to be non-reusable. | ||||
|    * | ||||
|    * @return $this | ||||
|    */ | ||||
|   public function setNonReusable(); | ||||
| 
 | ||||
|   /** | ||||
|    * Sets the theme value. | ||||
|    * | ||||
|    * When creating a new block content block from the block library, the user is | ||||
|    * redirected to the configure form for that block in the given theme. The | ||||
|    * theme is stored against the block when the block content add form is shown. | ||||
|    * | ||||
|    * @param string $theme | ||||
|    *   The theme name. | ||||
|    * | ||||
|    * @return $this | ||||
|    *   The class instance that this method is called on. | ||||
|    */ | ||||
|   public function setTheme($theme); | ||||
| 
 | ||||
|   /** | ||||
|    * Gets the theme value. | ||||
|    * | ||||
|    * When creating a new block content block from the block library, the user is | ||||
|    * redirected to the configure form for that block in the given theme. The | ||||
|    * theme is stored against the block when the block content add form is shown. | ||||
|    * | ||||
|    * @return string | ||||
|    *   The theme name. | ||||
|    */ | ||||
|   public function getTheme(); | ||||
| 
 | ||||
|   /** | ||||
|    * Gets the configured instances of this content block. | ||||
|    * | ||||
|    * @return array | ||||
|    *   Array of Drupal\block\Core\Plugin\Entity\Block entities. | ||||
|    */ | ||||
|   public function getInstances(); | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										60
									
								
								src/BlockContentListBuilder.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/BlockContentListBuilder.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,60 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content; | ||||
| 
 | ||||
| use Drupal\Core\Entity\EntityInterface; | ||||
| use Drupal\Core\Entity\EntityListBuilder; | ||||
| 
 | ||||
| /** | ||||
|  * Defines a class to build a listing of content block entities. | ||||
|  * | ||||
|  * @see \Drupal\block_content\Entity\BlockContent | ||||
|  */ | ||||
| class BlockContentListBuilder extends EntityListBuilder { | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function buildHeader() { | ||||
|     $header['label'] = $this->t('Block description'); | ||||
|     return $header + parent::buildHeader(); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function buildRow(EntityInterface $entity) { | ||||
|     $row['label'] = $entity->label(); | ||||
|     return $row + parent::buildRow($entity); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function getEntityIds() { | ||||
|     $request = \Drupal::request(); | ||||
|     $query = $this->getStorage()->getQuery()->accessCheck(TRUE); | ||||
|     $foo = $request->get('foo') ?? 0; | ||||
|     if ($foo) { | ||||
|       $query->condition('label', "%" . $foo . "%", 'LIKE'); | ||||
|     } | ||||
|     $query->sort($this->entityType->getKey('id')); | ||||
|     $query->condition('reusable', TRUE); | ||||
|     // Only add the pager if a limit is specified.
 | ||||
|     if ($this->limit) { | ||||
|       $query->pager($this->limit); | ||||
|     } | ||||
|     return $query->execute(); | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function render() { | ||||
|     $build['form'] = \Drupal::formBuilder()->getForm('Drupal\block_content\Form\BlockContentListFiltersForm'); | ||||
|     $build += parent::render(); | ||||
|     return $build; | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										84
									
								
								src/BlockContentPermissions.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								src/BlockContentPermissions.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,84 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content; | ||||
| 
 | ||||
| use Drupal\block_content\Entity\BlockContentType; | ||||
| use Drupal\Core\DependencyInjection\ContainerInjectionInterface; | ||||
| use Drupal\Core\Entity\BundlePermissionHandlerTrait; | ||||
| use Drupal\Core\Entity\EntityTypeManagerInterface; | ||||
| use Drupal\Core\StringTranslation\StringTranslationTrait; | ||||
| use Symfony\Component\DependencyInjection\ContainerInterface; | ||||
| 
 | ||||
| /** | ||||
|  * Provide dynamic permissions for blocks of different types. | ||||
|  */ | ||||
| class BlockContentPermissions implements ContainerInjectionInterface { | ||||
| 
 | ||||
|   use StringTranslationTrait; | ||||
|   use BundlePermissionHandlerTrait; | ||||
| 
 | ||||
|   /** | ||||
|    * Constructs a BlockContentPermissions instance. | ||||
|    * | ||||
|    * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager | ||||
|    *   Entity type manager. | ||||
|    */ | ||||
|   public function __construct( | ||||
|     protected EntityTypeManagerInterface $entityTypeManager, | ||||
|   ) { | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public static function create(ContainerInterface $container) { | ||||
|     return new static( | ||||
|       $container->get('entity_type.manager'), | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Build permissions for each block type. | ||||
|    * | ||||
|    * @return array | ||||
|    *   The block type permissions. | ||||
|    */ | ||||
|   public function blockTypePermissions() { | ||||
|     return $this->generatePermissions($this->entityTypeManager->getStorage('block_content_type')->loadMultiple(), [$this, 'buildPermissions']); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Return all the permissions available for a block type. | ||||
|    * | ||||
|    * @param \Drupal\block_content\Entity\BlockContentType $type | ||||
|    *   The block type. | ||||
|    * | ||||
|    * @return array | ||||
|    *   Permissions available for the given block type. | ||||
|    */ | ||||
|   protected function buildPermissions(BlockContentType $type) { | ||||
|     $type_id = $type->id(); | ||||
|     $type_params = ['%type_name' => $type->label()]; | ||||
|     return [ | ||||
|       "create $type_id block content" => [ | ||||
|         'title' => $this->t('%type_name: Create new content block', $type_params), | ||||
|       ], | ||||
|       "edit any $type_id block content" => [ | ||||
|         'title' => $this->t('%type_name: Edit content block', $type_params), | ||||
|       ], | ||||
|       "delete any $type_id block content" => [ | ||||
|         'title' => $this->t('%type_name: Delete content block', $type_params), | ||||
|       ], | ||||
|       "view any $type_id block content history" => [ | ||||
|         'title' => $this->t('%type_name: View content block history pages', $type_params), | ||||
|       ], | ||||
|       "revert any $type_id block content revisions" => [ | ||||
|         'title' => $this->t('%type_name: Revert content block revisions', $type_params), | ||||
|       ], | ||||
|       "delete any $type_id block content revisions" => [ | ||||
|         'title' => $this->t('%type_name: Delete content block revisions', $type_params), | ||||
|       ], | ||||
|     ]; | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										27
									
								
								src/BlockContentStorageSchema.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/BlockContentStorageSchema.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content; | ||||
| 
 | ||||
| use Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema; | ||||
| use Drupal\Core\Field\FieldStorageDefinitionInterface; | ||||
| 
 | ||||
| /** | ||||
|  * Defines the block content schema handler. | ||||
|  */ | ||||
| class BlockContentStorageSchema extends SqlContentEntityStorageSchema { | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function getSharedTableFieldSchema(FieldStorageDefinitionInterface $storage_definition, $table_name, array $column_mapping): array { | ||||
|     $schema = parent::getSharedTableFieldSchema($storage_definition, $table_name, $column_mapping); | ||||
|     $field_name = $storage_definition->getName(); | ||||
| 
 | ||||
|     if ($table_name === $this->storage->getDataTable() && $field_name === 'reusable') { | ||||
|       $this->addSharedTableFieldIndex($storage_definition, $schema); | ||||
|     } | ||||
| 
 | ||||
|     return $schema; | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										22
									
								
								src/BlockContentTranslationHandler.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/BlockContentTranslationHandler.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content; | ||||
| 
 | ||||
| use Drupal\block_content\Entity\BlockContentType; | ||||
| use Drupal\Core\Entity\EntityInterface; | ||||
| use Drupal\content_translation\ContentTranslationHandler; | ||||
| 
 | ||||
| /** | ||||
|  * Defines the translation handler for content blocks. | ||||
|  */ | ||||
| class BlockContentTranslationHandler extends ContentTranslationHandler { | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function entityFormTitle(EntityInterface $entity) { | ||||
|     $block_type = BlockContentType::load($entity->bundle()); | ||||
|     return $this->t('<em>Edit @type</em> @title', ['@type' => $block_type->label(), '@title' => $entity->label()]); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										128
									
								
								src/BlockContentTypeForm.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								src/BlockContentTypeForm.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,128 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content; | ||||
| 
 | ||||
| use Drupal\Core\Entity\BundleEntityFormBase; | ||||
| use Drupal\Core\Entity\EntityInterface; | ||||
| use Drupal\Core\Entity\EntityTypeInterface; | ||||
| use Drupal\Core\Form\FormStateInterface; | ||||
| use Drupal\language\Entity\ContentLanguageSettings; | ||||
| 
 | ||||
| /** | ||||
|  * The block content type entity form. | ||||
|  * | ||||
|  * @internal | ||||
|  */ | ||||
| class BlockContentTypeForm extends BundleEntityFormBase { | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function form(array $form, FormStateInterface $form_state) { | ||||
|     $form = parent::form($form, $form_state); | ||||
| 
 | ||||
|     /** @var \Drupal\block_content\BlockContentTypeInterface $block_type */ | ||||
|     $block_type = $this->entity; | ||||
| 
 | ||||
|     if ($this->operation == 'add') { | ||||
|       $form['#title'] = $this->t('Add block type'); | ||||
|     } | ||||
|     else { | ||||
|       $form['#title'] = $this->t('Edit %label block type', ['%label' => $block_type->label()]); | ||||
|     } | ||||
| 
 | ||||
|     $form['label'] = [ | ||||
|       '#type' => 'textfield', | ||||
|       '#title' => $this->t('Label'), | ||||
|       '#maxlength' => 255, | ||||
|       '#default_value' => $block_type->label(), | ||||
|       '#description' => $this->t("The human-readable name for this block type, displayed on the <em>Block types</em> page."), | ||||
|       '#required' => TRUE, | ||||
|     ]; | ||||
|     $form['id'] = [ | ||||
|       '#type' => 'machine_name', | ||||
|       '#default_value' => $block_type->id(), | ||||
|       '#machine_name' => [ | ||||
|         'exists' => '\Drupal\block_content\Entity\BlockContentType::load', | ||||
|       ], | ||||
|       '#description' => $this->t("Unique machine-readable name: lowercase letters, numbers, and underscores only."), | ||||
|       '#maxlength' => EntityTypeInterface::BUNDLE_MAX_LENGTH, | ||||
|     ]; | ||||
| 
 | ||||
|     $form['description'] = [ | ||||
|       '#type' => 'textarea', | ||||
|       '#default_value' => $block_type->getDescription(), | ||||
|       '#description' => $this->t('Displays on the <em>Block types</em> page.'), | ||||
|       '#title' => $this->t('Description'), | ||||
|     ]; | ||||
| 
 | ||||
|     $form['revision'] = [ | ||||
|       '#type' => 'checkbox', | ||||
|       '#title' => $this->t('Create new revision'), | ||||
|       '#default_value' => $block_type->shouldCreateNewRevision(), | ||||
|       '#description' => $this->t('Create a new revision by default for this block type.'), | ||||
|     ]; | ||||
| 
 | ||||
|     if ($this->moduleHandler->moduleExists('language')) { | ||||
|       $form['language'] = [ | ||||
|         '#type' => 'details', | ||||
|         '#title' => $this->t('Language settings'), | ||||
|         '#group' => 'additional_settings', | ||||
|       ]; | ||||
| 
 | ||||
|       $language_configuration = ContentLanguageSettings::loadByEntityTypeBundle('block_content', $block_type->id()); | ||||
|       $form['language']['language_configuration'] = [ | ||||
|         '#type' => 'language_configuration', | ||||
|         '#entity_information' => [ | ||||
|           'entity_type' => 'block_content', | ||||
|           'bundle' => $block_type->id(), | ||||
|         ], | ||||
|         '#default_value' => $language_configuration, | ||||
|       ]; | ||||
| 
 | ||||
|       $form['#submit'][] = 'language_configuration_element_submit'; | ||||
|     } | ||||
| 
 | ||||
|     $form['actions'] = ['#type' => 'actions']; | ||||
|     $form['actions']['submit'] = [ | ||||
|       '#type' => 'submit', | ||||
|       '#value' => $this->t('Save'), | ||||
|     ]; | ||||
| 
 | ||||
|     return $this->protectBundleIdElement($form); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function copyFormValuesToEntity(EntityInterface $entity, array $form, FormStateInterface $form_state) { | ||||
|     // An empty description violates config schema.
 | ||||
|     if (trim($form_state->getValue('description', '')) === '') { | ||||
|       $form_state->unsetValue('description'); | ||||
|     } | ||||
|     parent::copyFormValuesToEntity($entity, $form, $form_state); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function save(array $form, FormStateInterface $form_state) { | ||||
|     $block_type = $this->entity; | ||||
|     $status = $block_type->save(); | ||||
| 
 | ||||
|     $edit_link = $this->entity->toLink($this->t('Edit'), 'edit-form')->toString(); | ||||
|     $logger = $this->logger('block_content'); | ||||
|     if ($status == SAVED_UPDATED) { | ||||
|       $this->messenger()->addStatus($this->t('Block type %label has been updated.', ['%label' => $block_type->label()])); | ||||
|       $logger->notice('Block type %label has been updated.', ['%label' => $block_type->label(), 'link' => $edit_link]); | ||||
|     } | ||||
|     else { | ||||
|       block_content_add_body_field($block_type->id()); | ||||
|       $this->messenger()->addStatus($this->t('Block type %label has been added.', ['%label' => $block_type->label()])); | ||||
|       $logger->notice('Block type %label has been added.', ['%label' => $block_type->label(), 'link' => $edit_link]); | ||||
|     } | ||||
| 
 | ||||
|     $form_state->setRedirectUrl($this->entity->toUrl('collection')); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										21
									
								
								src/BlockContentTypeInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/BlockContentTypeInterface.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content; | ||||
| 
 | ||||
| use Drupal\Core\Config\Entity\ConfigEntityInterface; | ||||
| use Drupal\Core\Entity\RevisionableEntityBundleInterface; | ||||
| 
 | ||||
| /** | ||||
|  * Provides an interface defining a block type entity. | ||||
|  */ | ||||
| interface BlockContentTypeInterface extends ConfigEntityInterface, RevisionableEntityBundleInterface { | ||||
| 
 | ||||
|   /** | ||||
|    * Returns the description of the block type. | ||||
|    * | ||||
|    * @return string | ||||
|    *   The description of the type of this block. | ||||
|    */ | ||||
|   public function getDescription(); | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										53
									
								
								src/BlockContentTypeListBuilder.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/BlockContentTypeListBuilder.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,53 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content; | ||||
| 
 | ||||
| use Drupal\Core\Config\Entity\ConfigEntityListBuilder; | ||||
| use Drupal\Core\Entity\EntityInterface; | ||||
| 
 | ||||
| /** | ||||
|  * Defines a class to build a listing of block type entities. | ||||
|  * | ||||
|  * @see \Drupal\block_content\Entity\BlockContentType | ||||
|  */ | ||||
| class BlockContentTypeListBuilder extends ConfigEntityListBuilder { | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function getDefaultOperations(EntityInterface $entity) { | ||||
|     $operations = parent::getDefaultOperations($entity); | ||||
|     // Place the edit operation after the operations added by field_ui.module
 | ||||
|     // which have the weights 15, 20, 25.
 | ||||
|     if (isset($operations['edit'])) { | ||||
|       $operations['edit']['weight'] = 30; | ||||
|     } | ||||
|     return $operations; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function buildHeader() { | ||||
|     $header['type'] = $this->t('Block type'); | ||||
|     $header['description'] = $this->t('Description'); | ||||
|     return $header + parent::buildHeader(); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function buildRow(EntityInterface $entity) { | ||||
|     $row['type'] = $entity->toLink(NULL, 'edit-form')->toString(); | ||||
|     $row['description']['data']['#markup'] = $entity->getDescription(); | ||||
|     return $row + parent::buildRow($entity); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function getTitle() { | ||||
|     return $this->t('Block types'); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										64
									
								
								src/BlockContentUuidLookup.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								src/BlockContentUuidLookup.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,64 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content; | ||||
| 
 | ||||
| use Drupal\Core\Cache\CacheBackendInterface; | ||||
| use Drupal\Core\Cache\CacheCollector; | ||||
| use Drupal\Core\Entity\EntityTypeManagerInterface; | ||||
| use Drupal\Core\Lock\LockBackendInterface; | ||||
| 
 | ||||
| /** | ||||
|  * A cache collector that caches IDs for block_content UUIDs. | ||||
|  * | ||||
|  * As block_content entities are used as block plugin derivatives, it is a | ||||
|  * fairly safe limitation that there are not hundreds of them, a site will | ||||
|  * likely run into problems with too many block content entities in other places | ||||
|  * than a cache that only stores UUID's and IDs. The same assumption is not true | ||||
|  * for other content entities. | ||||
|  * | ||||
|  * @internal | ||||
|  */ | ||||
| class BlockContentUuidLookup extends CacheCollector { | ||||
| 
 | ||||
|   /** | ||||
|    * The entity type manager. | ||||
|    * | ||||
|    * @var \Drupal\Core\Entity\EntityTypeManagerInterface | ||||
|    */ | ||||
|   protected $entityTypeManager; | ||||
| 
 | ||||
|   /** | ||||
|    * Constructs a BlockContentUuidLookup instance. | ||||
|    * | ||||
|    * @param \Drupal\Core\Cache\CacheBackendInterface $cache | ||||
|    *   The cache backend. | ||||
|    * @param \Drupal\Core\Lock\LockBackendInterface $lock | ||||
|    *   The lock backend. | ||||
|    * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager | ||||
|    *   The entity type manager. | ||||
|    */ | ||||
|   public function __construct(CacheBackendInterface $cache, LockBackendInterface $lock, EntityTypeManagerInterface $entity_type_manager) { | ||||
|     parent::__construct('block_content_uuid', $cache, $lock); | ||||
|     $this->entityTypeManager = $entity_type_manager; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function resolveCacheMiss($key) { | ||||
|     $ids = $this->entityTypeManager->getStorage('block_content')->getQuery() | ||||
|       ->accessCheck(FALSE) | ||||
|       ->condition('uuid', $key) | ||||
|       ->execute(); | ||||
| 
 | ||||
|     // Only cache if there is a match, otherwise creating new entities would
 | ||||
|     // require to invalidate the cache.
 | ||||
|     $id = reset($ids); | ||||
|     if ($id) { | ||||
|       $this->storage[$key] = $id; | ||||
|       $this->persist($key); | ||||
|     } | ||||
|     return $id; | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										50
									
								
								src/BlockContentViewBuilder.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/BlockContentViewBuilder.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content; | ||||
| 
 | ||||
| use Drupal\Core\Entity\EntityInterface; | ||||
| use Drupal\Core\Entity\EntityViewBuilder; | ||||
| 
 | ||||
| /** | ||||
|  * View builder handler for content blocks. | ||||
|  * | ||||
|  * Note: Content blocks (block_content entities) are not designed to be displayed | ||||
|  * outside of blocks! This BlockContentViewBuilder class is designed to be used | ||||
|  * by \Drupal\block_content\Plugin\Block\BlockContentBlock::build() and by | ||||
|  * nothing else. | ||||
|  * | ||||
|  * @see \Drupal\block_content\Plugin\Block\BlockContentBlock | ||||
|  */ | ||||
| class BlockContentViewBuilder extends EntityViewBuilder { | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function view(EntityInterface $entity, $view_mode = 'full', $langcode = NULL) { | ||||
|     return $this->viewMultiple([$entity], $view_mode, $langcode)[0]; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function viewMultiple(array $entities = [], $view_mode = 'full', $langcode = NULL) { | ||||
|     $build_list = parent::viewMultiple($entities, $view_mode, $langcode); | ||||
|     // Apply the buildMultiple() #pre_render callback immediately, to make
 | ||||
|     // bubbling of attributes and contextual links to the actual block work.
 | ||||
|     // @see \Drupal\block\BlockViewBuilder::buildBlock()
 | ||||
|     unset($build_list['#pre_render'][0]); | ||||
|     return $this->buildMultiple($build_list); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function getBuildDefaults(EntityInterface $entity, $view_mode) { | ||||
|     $build = parent::getBuildDefaults($entity, $view_mode); | ||||
|     // The content block will be rendered in the wrapped block template already
 | ||||
|     // and thus has no entity template itself.
 | ||||
|     unset($build['#theme']); | ||||
|     return $build; | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										42
									
								
								src/BlockContentViewsData.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/BlockContentViewsData.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,42 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content; | ||||
| 
 | ||||
| use Drupal\views\EntityViewsData; | ||||
| 
 | ||||
| /** | ||||
|  * Provides the views data for the block_content entity type. | ||||
|  */ | ||||
| class BlockContentViewsData extends EntityViewsData { | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function getViewsData() { | ||||
| 
 | ||||
|     $data = parent::getViewsData(); | ||||
| 
 | ||||
|     $data['block_content_field_data']['id']['field']['id'] = 'field'; | ||||
| 
 | ||||
|     $data['block_content_field_data']['info']['field']['id'] = 'field'; | ||||
|     $data['block_content_field_data']['info']['field']['link_to_entity default'] = TRUE; | ||||
| 
 | ||||
|     $data['block_content_field_data']['type']['field']['id'] = 'field'; | ||||
| 
 | ||||
|     $data['block_content_field_data']['table']['wizard_id'] = 'block_content'; | ||||
| 
 | ||||
|     $data['block_content']['block_content_listing_empty'] = [ | ||||
|       'title' => $this->t('Empty block library behavior'), | ||||
|       'help' => $this->t('Provides a link to add a new block.'), | ||||
|       'area' => [ | ||||
|         'id' => 'block_content_listing_empty', | ||||
|       ], | ||||
|     ]; | ||||
|     // Advertise this table as a possible base table.
 | ||||
|     $data['block_content_field_revision']['table']['base']['help'] = $this->t('Block Content revision is a history of changes to block content.'); | ||||
|     $data['block_content_field_revision']['table']['base']['defaults']['title'] = 'info'; | ||||
| 
 | ||||
|     return $data; | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										33
									
								
								src/BlockTypeAccessControlHandler.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/BlockTypeAccessControlHandler.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content; | ||||
| 
 | ||||
| use Drupal\Core\Access\AccessResult; | ||||
| use Drupal\Core\Entity\EntityAccessControlHandler; | ||||
| use Drupal\Core\Entity\EntityInterface; | ||||
| use Drupal\Core\Session\AccountInterface; | ||||
| 
 | ||||
| /** | ||||
|  * Defines the access control handler for the "Block Type" entity type. | ||||
|  * | ||||
|  * @see \Drupal\block_content\Entity\BlockContentType | ||||
|  */ | ||||
| class BlockTypeAccessControlHandler extends EntityAccessControlHandler { | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected $viewLabelOperation = TRUE; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) { | ||||
|     if ($operation === 'view label') { | ||||
|       return AccessResult::allowedIfHasPermission($account, 'access block library') | ||||
|         ->orIf(parent::checkAccess($entity, $operation, $account)); | ||||
|     } | ||||
|     return parent::checkAccess($entity, $operation, $account); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										239
									
								
								src/Controller/BlockContentController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										239
									
								
								src/Controller/BlockContentController.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,239 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content\Controller; | ||||
| 
 | ||||
| use Drupal\Core\Controller\ControllerBase; | ||||
| use Drupal\Core\Entity\EntityStorageInterface; | ||||
| use Drupal\Core\Routing\PathChangedHelper; | ||||
| use Drupal\Core\Routing\RouteMatchInterface; | ||||
| use Drupal\block_content\BlockContentInterface; | ||||
| use Drupal\block_content\BlockContentTypeInterface; | ||||
| use Drupal\Core\Extension\ThemeHandlerInterface; | ||||
| use Drupal\Core\Url; | ||||
| use Symfony\Component\DependencyInjection\ContainerInterface; | ||||
| use Symfony\Component\HttpFoundation\Request; | ||||
| use Symfony\Component\HttpFoundation\RedirectResponse; | ||||
| 
 | ||||
| class BlockContentController extends ControllerBase { | ||||
| 
 | ||||
|   /** | ||||
|    * The content block storage. | ||||
|    * | ||||
|    * @var \Drupal\Core\Entity\EntityStorageInterface | ||||
|    */ | ||||
|   protected $blockContentStorage; | ||||
| 
 | ||||
|   /** | ||||
|    * The content block type storage. | ||||
|    * | ||||
|    * @var \Drupal\Core\Entity\EntityStorageInterface | ||||
|    */ | ||||
|   protected $blockContentTypeStorage; | ||||
| 
 | ||||
|   /** | ||||
|    * The theme handler. | ||||
|    * | ||||
|    * @var \Drupal\Core\Extension\ThemeHandlerInterface | ||||
|    */ | ||||
|   protected $themeHandler; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public static function create(ContainerInterface $container) { | ||||
|     $entity_type_manager = $container->get('entity_type.manager'); | ||||
|     return new static( | ||||
|       $entity_type_manager->getStorage('block_content'), | ||||
|       $entity_type_manager->getStorage('block_content_type'), | ||||
|       $container->get('theme_handler') | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Constructs a BlockContent object. | ||||
|    * | ||||
|    * @param \Drupal\Core\Entity\EntityStorageInterface $block_content_storage | ||||
|    *   The content block storage. | ||||
|    * @param \Drupal\Core\Entity\EntityStorageInterface $block_content_type_storage | ||||
|    *   The block type storage. | ||||
|    * @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler | ||||
|    *   The theme handler. | ||||
|    */ | ||||
|   public function __construct(EntityStorageInterface $block_content_storage, EntityStorageInterface $block_content_type_storage, ThemeHandlerInterface $theme_handler) { | ||||
|     $this->blockContentStorage = $block_content_storage; | ||||
|     $this->blockContentTypeStorage = $block_content_type_storage; | ||||
|     $this->themeHandler = $theme_handler; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Displays add content block links for available types. | ||||
|    * | ||||
|    * @param \Symfony\Component\HttpFoundation\Request $request | ||||
|    *   The current request object. | ||||
|    * | ||||
|    * @return array | ||||
|    *   A render array for a list of the block types that can be added or | ||||
|    *   if there is only one block type defined for the site, the function
 | ||||
|    *   returns the content block add page for that block type. | ||||
|    */ | ||||
|   public function add(Request $request) { | ||||
|     // @todo deprecate see https://www.drupal.org/project/drupal/issues/3346394.
 | ||||
|     $types = []; | ||||
|     // Only use block types the user has access to.
 | ||||
|     foreach ($this->blockContentTypeStorage->loadMultiple() as $type) { | ||||
|       $access = $this->entityTypeManager()->getAccessControlHandler('block_content')->createAccess($type->id(), NULL, [], TRUE); | ||||
|       if ($access->isAllowed()) { | ||||
|         $types[$type->id()] = $type; | ||||
|       } | ||||
|     } | ||||
|     uasort($types, [$this->blockContentTypeStorage->getEntityType()->getClass(), 'sort']); | ||||
|     if ($types && count($types) == 1) { | ||||
|       $type = reset($types); | ||||
|       return $this->addForm($type, $request); | ||||
|     } | ||||
|     if (count($types) === 0) { | ||||
|       return [ | ||||
|         '#markup' => $this->t('You have not created any block types yet. Go to the <a href=":url">block type creation page</a> to add a new block type.', [ | ||||
|           ':url' => Url::fromRoute('block_content.type_add')->toString(), | ||||
|         ]), | ||||
|       ]; | ||||
|     } | ||||
| 
 | ||||
|     return ['#theme' => 'block_content_add_list', '#content' => $types]; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Presents the content block creation form. | ||||
|    * | ||||
|    * @param \Drupal\block_content\BlockContentTypeInterface $block_content_type | ||||
|    *   The block type to add. | ||||
|    * @param \Symfony\Component\HttpFoundation\Request $request | ||||
|    *   The current request object. | ||||
|    * | ||||
|    * @return array | ||||
|    *   A form array as expected by | ||||
|    *   \Drupal\Core\Render\RendererInterface::render(). | ||||
|    */ | ||||
|   public function addForm(BlockContentTypeInterface $block_content_type, Request $request) { | ||||
|     $block = $this->blockContentStorage->create([ | ||||
|       'type' => $block_content_type->id(), | ||||
|     ]); | ||||
|     if (($theme = $request->query->get('theme')) && in_array($theme, array_keys($this->themeHandler->listInfo()))) { | ||||
|       // We have navigated to this page from the block library and will keep track
 | ||||
|       // of the theme for redirecting the user to the configuration page for the
 | ||||
|       // newly created block in the given theme.
 | ||||
|       $block->setTheme($theme); | ||||
|     } | ||||
|     return $this->entityFormBuilder()->getForm($block); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Provides the page title for this controller. | ||||
|    * | ||||
|    * @param \Drupal\block_content\BlockContentTypeInterface $block_content_type | ||||
|    *   The block type being added. | ||||
|    * | ||||
|    * @return string | ||||
|    *   The page title. | ||||
|    */ | ||||
|   public function getAddFormTitle(BlockContentTypeInterface $block_content_type) { | ||||
|     return $this->t('Add %type content block', ['%type' => $block_content_type->label()]); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Provides a redirect to the list of block types. | ||||
|    * | ||||
|    * @param \Drupal\Core\Routing\RouteMatchInterface $route_match | ||||
|    *   A route match object, used for the route name and the parameters. | ||||
|    * @param \Symfony\Component\HttpFoundation\Request $request | ||||
|    *   The current request object. | ||||
|    * | ||||
|    * @return \Symfony\Component\HttpFoundation\RedirectResponse | ||||
|    * | ||||
|    * @deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Use | ||||
|    *   /admin/structure/block-content directly instead of | ||||
|    *   /admin/structure/block/block-content/types. | ||||
|    * | ||||
|    * @see https://www.drupal.org/node/3320855 | ||||
|    */ | ||||
|   public function blockContentTypeRedirect(RouteMatchInterface $route_match, Request $request): RedirectResponse { | ||||
|     @trigger_error('The path /admin/structure/block/block-content/types is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Use /admin/structure/block-content. See https://www.drupal.org/node/3320855', E_USER_DEPRECATED); | ||||
|     $helper = new PathChangedHelper($route_match, $request); | ||||
|     $params = [ | ||||
|       '%old_path' => $helper->oldPath(), | ||||
|       '%new_path' => $helper->newPath(), | ||||
|       '%change_record' => 'https://www.drupal.org/node/3320855', | ||||
|     ]; | ||||
|     $warning_message = $this->t('You have been redirected from %old_path. Update links, shortcuts, and bookmarks to use %new_path.', $params); | ||||
|     $this->messenger()->addWarning($warning_message); | ||||
|     $this->getLogger('block_content')->warning('A user was redirected from %old_path. This redirect will be removed in a future version of Drupal. Update links, shortcuts, and bookmarks to use %new_path. See %change_record for more information.', $params); | ||||
| 
 | ||||
|     return $helper->redirect(); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Provides a redirect to the content block library. | ||||
|    * | ||||
|    * @param \Drupal\Core\Routing\RouteMatchInterface $route_match | ||||
|    *   A route match object, used for the route name and the parameters. | ||||
|    * @param \Symfony\Component\HttpFoundation\Request $request | ||||
|    *   The current request object. | ||||
|    * | ||||
|    * @return \Symfony\Component\HttpFoundation\RedirectResponse | ||||
|    * | ||||
|    * @deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Use | ||||
|    *   /admin/content/block directly instead of | ||||
|    *   /admin/structure/block/block-content. | ||||
|    * | ||||
|    * @see https://www.drupal.org/node/3320855 | ||||
|    */ | ||||
|   public function blockLibraryRedirect(RouteMatchInterface $route_match, Request $request) { | ||||
|     @trigger_error('The path /admin/structure/block/block-content is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Use /admin/content/block. See https://www.drupal.org/node/3320855', E_USER_DEPRECATED); | ||||
|     $helper = new PathChangedHelper($route_match, $request); | ||||
|     $params = [ | ||||
|       '%old_path' => $helper->oldPath(), | ||||
|       '%new_path' => $helper->newPath(), | ||||
|       '%change_record' => 'https://www.drupal.org/node/3320855', | ||||
|     ]; | ||||
|     $warning_message = $this->t('You have been redirected from %old_path. Update links, shortcuts, and bookmarks to use %new_path.', $params); | ||||
|     $this->messenger()->addWarning($warning_message); | ||||
|     $this->getLogger('block_content') | ||||
|       ->warning('A user was redirected from %old_path. This redirect will be removed in a future version of Drupal. Update links, shortcuts, and bookmarks to use %new_path. See %change_record for more information.', $params); | ||||
| 
 | ||||
|     return $helper->redirect(); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Provides a redirect to block edit page. | ||||
|    * | ||||
|    * @param \Drupal\Core\Routing\RouteMatchInterface $route_match | ||||
|    *   A route match object, used for the route name and the parameters. | ||||
|    * @param \Symfony\Component\HttpFoundation\Request $request | ||||
|    *   The current request object. | ||||
|    * @param Drupal\block_content\BlockContentInterface $block_content | ||||
|    *   The block to be edited. | ||||
|    * | ||||
|    * @return \Symfony\Component\HttpFoundation\RedirectResponse | ||||
|    * | ||||
|    * @deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Use | ||||
|    *   /admin/content/block/{block_content} directly instead of | ||||
|    *   /block/{block_content}. | ||||
|    * | ||||
|    * @see https://www.drupal.org/node/3320855 | ||||
|    */ | ||||
|   public function editRedirect(RouteMatchInterface $route_match, Request $request, BlockContentInterface $block_content): RedirectResponse { | ||||
|     @trigger_error('The path /block/{block_content} is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Use /admin/content/block/{block_content}. See https://www.drupal.org/node/3320855', E_USER_DEPRECATED); | ||||
|     $helper = new PathChangedHelper($route_match, $request); | ||||
|     $params = [ | ||||
|       '%old_path' => $helper->oldPath(), | ||||
|       '%new_path' => $helper->newPath(), | ||||
|       '%change_record' => 'https://www.drupal.org/node/3320855', | ||||
|     ]; | ||||
|     $warning_message = $this->t('You have been redirected from %old_path. Update links, shortcuts, and bookmarks to use %new_path.', $params); | ||||
|     $this->messenger()->addWarning($warning_message); | ||||
|     $this->getLogger('block_content')->warning('A user was redirected from %old_path to %new_path. This redirect will be removed in a future version of Drupal. Update links, shortcuts, and bookmarks to use %new_path. See %change_record for more information.', $params); | ||||
| 
 | ||||
|     return $helper->redirect(); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										274
									
								
								src/Entity/BlockContent.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										274
									
								
								src/Entity/BlockContent.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,274 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content\Entity; | ||||
| 
 | ||||
| use Drupal\block_content\Access\RefinableDependentAccessTrait; | ||||
| use Drupal\Core\Entity\EditorialContentEntityBase; | ||||
| use Drupal\Core\Entity\EntityStorageInterface; | ||||
| use Drupal\Core\Entity\EntityTypeInterface; | ||||
| use Drupal\Core\Field\BaseFieldDefinition; | ||||
| use Drupal\block_content\BlockContentInterface; | ||||
| 
 | ||||
| /** | ||||
|  * Defines the content block entity class. | ||||
|  * | ||||
|  * @ContentEntityType( | ||||
|  *   id = "block_content", | ||||
|  *   label = @Translation("Content block"), | ||||
|  *   label_collection = @Translation("Content blocks"), | ||||
|  *   label_singular = @Translation("content block"), | ||||
|  *   label_plural = @Translation("content blocks"), | ||||
|  *   label_count = @PluralTranslation( | ||||
|  *     singular = "@count content block", | ||||
|  *     plural = "@count content blocks", | ||||
|  *   ), | ||||
|  *   bundle_label = @Translation("Block type"), | ||||
|  *   handlers = { | ||||
|  *     "storage" = "Drupal\Core\Entity\Sql\SqlContentEntityStorage", | ||||
|  *     "storage_schema" = "Drupal\block_content\BlockContentStorageSchema", | ||||
|  *     "access" = "Drupal\block_content\BlockContentAccessControlHandler", | ||||
|  *     "list_builder" = "Drupal\block_content\BlockContentListBuilder", | ||||
|  *     "view_builder" = "Drupal\block_content\BlockContentViewBuilder", | ||||
|  *     "views_data" = "Drupal\block_content\BlockContentViewsData", | ||||
|  *     "form" = { | ||||
|  *       "add" = "Drupal\block_content\BlockContentForm", | ||||
|  *       "edit" = "Drupal\block_content\BlockContentForm", | ||||
|  *       "delete" = "Drupal\block_content\Form\BlockContentDeleteForm", | ||||
|  *       "default" = "Drupal\block_content\BlockContentForm", | ||||
|  *       "revision-delete" = \Drupal\Core\Entity\Form\RevisionDeleteForm::class, | ||||
|  *       "revision-revert" = \Drupal\Core\Entity\Form\RevisionRevertForm::class, | ||||
|  *     }, | ||||
|  *     "route_provider" = { | ||||
|  *       "revision" = \Drupal\Core\Entity\Routing\RevisionHtmlRouteProvider::class, | ||||
|  *     }, | ||||
|  *     "translation" = "Drupal\block_content\BlockContentTranslationHandler" | ||||
|  *   }, | ||||
|  *   admin_permission = "administer block content", | ||||
|  *   collection_permission = "access block library", | ||||
|  *   base_table = "block_content", | ||||
|  *   revision_table = "block_content_revision", | ||||
|  *   data_table = "block_content_field_data", | ||||
|  *   revision_data_table = "block_content_field_revision", | ||||
|  *   show_revision_ui = TRUE, | ||||
|  *   links = { | ||||
|  *     "canonical" = "/admin/content/block/{block_content}", | ||||
|  *     "delete-form" = "/admin/content/block/{block_content}/delete", | ||||
|  *     "edit-form" = "/admin/content/block/{block_content}", | ||||
|  *     "collection" = "/admin/content/block", | ||||
|  *     "create" = "/block", | ||||
|  *     "revision-delete-form" = "/admin/content/block/{block_content}/revision/{block_content_revision}/delete", | ||||
|  *     "revision-revert-form" = "/admin/content/block/{block_content}/revision/{block_content_revision}/revert", | ||||
|  *     "version-history" = "/admin/content/block/{block_content}/revisions", | ||||
|  *   }, | ||||
|  *   translatable = TRUE, | ||||
|  *   entity_keys = { | ||||
|  *     "id" = "id", | ||||
|  *     "revision" = "revision_id", | ||||
|  *     "bundle" = "type", | ||||
|  *     "label" = "info", | ||||
|  *     "langcode" = "langcode", | ||||
|  *     "uuid" = "uuid", | ||||
|  *     "published" = "status", | ||||
|  *   }, | ||||
|  *   revision_metadata_keys = { | ||||
|  *     "revision_user" = "revision_user", | ||||
|  *     "revision_created" = "revision_created", | ||||
|  *     "revision_log_message" = "revision_log" | ||||
|  *   }, | ||||
|  *   bundle_entity_type = "block_content_type", | ||||
|  *   field_ui_base_route = "entity.block_content_type.edit_form", | ||||
|  *   render_cache = FALSE, | ||||
|  * ) | ||||
|  * | ||||
|  * Note that render caching of block_content entities is disabled because they | ||||
|  * are always rendered as blocks, and blocks already have their own render | ||||
|  * caching. | ||||
|  * See https://www.drupal.org/node/2284917#comment-9132521 for more information.
 | ||||
|  */ | ||||
| class BlockContent extends EditorialContentEntityBase implements BlockContentInterface { | ||||
| 
 | ||||
|   use RefinableDependentAccessTrait; | ||||
| 
 | ||||
|   /** | ||||
|    * The theme the block is being created in. | ||||
|    * | ||||
|    * When creating a new content block from the block library, the user is | ||||
|    * redirected to the configure form for that block in the given theme. The | ||||
|    * theme is stored against the block when the content block add form is shown. | ||||
|    * | ||||
|    * @var string | ||||
|    */ | ||||
|   protected $theme; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function createDuplicate() { | ||||
|     $duplicate = parent::createDuplicate(); | ||||
|     $duplicate->revision_id->value = NULL; | ||||
|     $duplicate->id->value = NULL; | ||||
|     return $duplicate; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function setTheme($theme) { | ||||
|     $this->theme = $theme; | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function getTheme() { | ||||
|     return $this->theme; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function postSave(EntityStorageInterface $storage, $update = TRUE) { | ||||
|     parent::postSave($storage, $update); | ||||
|     if ($this->isReusable() || (isset($this->original) && $this->original->isReusable())) { | ||||
|       static::invalidateBlockPluginCache(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public static function preDelete(EntityStorageInterface $storage, array $entities) { | ||||
|     parent::preDelete($storage, $entities); | ||||
| 
 | ||||
|     /** @var \Drupal\block_content\BlockContentInterface $block */ | ||||
|     foreach ($entities as $block) { | ||||
|       foreach ($block->getInstances() as $instance) { | ||||
|         $instance->delete(); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public static function postDelete(EntityStorageInterface $storage, array $entities) { | ||||
|     parent::postDelete($storage, $entities); | ||||
|     /** @var \Drupal\block_content\BlockContentInterface $block */ | ||||
|     foreach ($entities as $block) { | ||||
|       if ($block->isReusable()) { | ||||
|         // If any deleted blocks are reusable clear the block cache.
 | ||||
|         static::invalidateBlockPluginCache(); | ||||
|         return; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function getInstances() { | ||||
|     return \Drupal::entityTypeManager()->getStorage('block')->loadByProperties(['plugin' => 'block_content:' . $this->uuid()]); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function preSaveRevision(EntityStorageInterface $storage, \stdClass $record) { | ||||
|     parent::preSaveRevision($storage, $record); | ||||
| 
 | ||||
|     if (!$this->isNewRevision() && isset($this->original) && empty($record->revision_log_message)) { | ||||
|       // If we are updating an existing block_content without adding a new
 | ||||
|       // revision and the user did not supply a revision log, keep the existing
 | ||||
|       // one.
 | ||||
|       $record->revision_log = $this->original->getRevisionLogMessage(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { | ||||
|     /** @var \Drupal\Core\Field\BaseFieldDefinition[] $fields */ | ||||
|     $fields = parent::baseFieldDefinitions($entity_type); | ||||
| 
 | ||||
|     $fields['id']->setLabel(t('Content block ID')) | ||||
|       ->setDescription(t('The content block ID.')); | ||||
| 
 | ||||
|     $fields['uuid']->setDescription(t('The content block UUID.')); | ||||
| 
 | ||||
|     $fields['revision_id']->setDescription(t('The revision ID.')); | ||||
| 
 | ||||
|     $fields['langcode']->setDescription(t('The content block language code.')); | ||||
| 
 | ||||
|     $fields['type']->setLabel(t('Block type')) | ||||
|       ->setDescription(t('The block type.')); | ||||
| 
 | ||||
|     $fields['revision_log']->setDescription(t('The log entry explaining the changes in this revision.')); | ||||
| 
 | ||||
|     $fields['info'] = BaseFieldDefinition::create('string') | ||||
|       ->setLabel(t('Block description')) | ||||
|       ->setDescription(t('A brief description of your block.')) | ||||
|       ->setRevisionable(TRUE) | ||||
|       ->setTranslatable(TRUE) | ||||
|       ->setRequired(TRUE) | ||||
|       ->setDisplayOptions('form', [ | ||||
|         'type' => 'string_textfield', | ||||
|         'weight' => -5, | ||||
|       ]) | ||||
|       ->setDisplayConfigurable('form', TRUE); | ||||
| 
 | ||||
|     $fields['changed'] = BaseFieldDefinition::create('changed') | ||||
|       ->setLabel(t('Changed')) | ||||
|       ->setDescription(t('The time that the content block was last edited.')) | ||||
|       ->setTranslatable(TRUE) | ||||
|       ->setRevisionable(TRUE); | ||||
| 
 | ||||
|     $fields['reusable'] = BaseFieldDefinition::create('boolean') | ||||
|       ->setLabel(t('Reusable')) | ||||
|       ->setDescription(t('A boolean indicating whether this block is reusable.')) | ||||
|       ->setTranslatable(FALSE) | ||||
|       ->setRevisionable(FALSE) | ||||
|       ->setDefaultValue(TRUE); | ||||
| 
 | ||||
|     return $fields; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function setInfo($info) { | ||||
|     $this->set('info', $info); | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function isReusable() { | ||||
|     return (bool) $this->get('reusable')->value; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function setReusable() { | ||||
|     return $this->set('reusable', TRUE); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function setNonReusable() { | ||||
|     return $this->set('reusable', FALSE); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Invalidates the block plugin cache after changes and deletions. | ||||
|    */ | ||||
|   protected static function invalidateBlockPluginCache() { | ||||
|     // Invalidate the block cache to update content block-based derivatives.
 | ||||
|     \Drupal::service('plugin.manager.block')->clearCachedDefinitions(); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										100
									
								
								src/Entity/BlockContentType.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								src/Entity/BlockContentType.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,100 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content\Entity; | ||||
| 
 | ||||
| use Drupal\Core\Config\Entity\ConfigEntityBundleBase; | ||||
| use Drupal\block_content\BlockContentTypeInterface; | ||||
| 
 | ||||
| /** | ||||
|  * Defines the block type entity. | ||||
|  * | ||||
|  * @ConfigEntityType( | ||||
|  *   id = "block_content_type", | ||||
|  *   label = @Translation("Block type"), | ||||
|  *   label_collection = @Translation("Block types"), | ||||
|  *   label_singular = @Translation("block type"), | ||||
|  *   label_plural = @Translation("block types"), | ||||
|  *   label_count = @PluralTranslation( | ||||
|  *     singular = "@count block type", | ||||
|  *     plural = "@count block types", | ||||
|  *   ), | ||||
|  *   handlers = { | ||||
|  *     "access" = "Drupal\block_content\BlockTypeAccessControlHandler", | ||||
|  *     "form" = { | ||||
|  *       "default" = "Drupal\block_content\BlockContentTypeForm", | ||||
|  *       "add" = "Drupal\block_content\BlockContentTypeForm", | ||||
|  *       "edit" = "Drupal\block_content\BlockContentTypeForm", | ||||
|  *       "delete" = "Drupal\block_content\Form\BlockContentTypeDeleteForm" | ||||
|  *     }, | ||||
|  *     "route_provider" = { | ||||
|  *       "html" = "Drupal\Core\Entity\Routing\AdminHtmlRouteProvider", | ||||
|  *       "permissions" = "Drupal\user\Entity\EntityPermissionsRouteProvider", | ||||
|  *     }, | ||||
|  *     "list_builder" = "Drupal\block_content\BlockContentTypeListBuilder" | ||||
|  *   }, | ||||
|  *   admin_permission = "administer block types", | ||||
|  *   config_prefix = "type", | ||||
|  *   bundle_of = "block_content", | ||||
|  *   entity_keys = { | ||||
|  *     "id" = "id", | ||||
|  *     "label" = "label" | ||||
|  *   }, | ||||
|  *   links = { | ||||
|  *     "delete-form" = "/admin/structure/block-content/manage/{block_content_type}/delete", | ||||
|  *     "edit-form" = "/admin/structure/block-content/manage/{block_content_type}", | ||||
|  *     "entity-permissions-form" = "/admin/structure/block-content/manage/{block_content_type}/permissions", | ||||
|  *     "collection" = "/admin/structure/block-content", | ||||
|  *   }, | ||||
|  *   config_export = { | ||||
|  *     "id", | ||||
|  *     "label", | ||||
|  *     "revision", | ||||
|  *     "description", | ||||
|  *   } | ||||
|  * ) | ||||
|  */ | ||||
| class BlockContentType extends ConfigEntityBundleBase implements BlockContentTypeInterface { | ||||
| 
 | ||||
|   /** | ||||
|    * The block type ID. | ||||
|    * | ||||
|    * @var string | ||||
|    */ | ||||
|   protected $id; | ||||
| 
 | ||||
|   /** | ||||
|    * The block type label. | ||||
|    * | ||||
|    * @var string | ||||
|    */ | ||||
|   protected $label; | ||||
| 
 | ||||
|   /** | ||||
|    * The default revision setting for content blocks of this type. | ||||
|    * | ||||
|    * @var bool | ||||
|    */ | ||||
|   protected $revision = FALSE; | ||||
| 
 | ||||
|   /** | ||||
|    * The description of the block type. | ||||
|    * | ||||
|    * @var string|null | ||||
|    */ | ||||
|   protected $description = NULL; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function getDescription() { | ||||
|     return $this->description ?? ''; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function shouldCreateNewRevision() { | ||||
|     return $this->revision; | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										70
									
								
								src/Event/BlockContentGetDependencyEvent.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								src/Event/BlockContentGetDependencyEvent.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,70 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content\Event; | ||||
| 
 | ||||
| use Drupal\block_content\BlockContentInterface; | ||||
| use Drupal\Core\Access\AccessibleInterface; | ||||
| use Drupal\Component\EventDispatcher\Event; | ||||
| 
 | ||||
| /** | ||||
|  * Block content event to allow setting an access dependency. | ||||
|  * | ||||
|  * @internal | ||||
|  */ | ||||
| class BlockContentGetDependencyEvent extends Event { | ||||
| 
 | ||||
|   /** | ||||
|    * The block content entity. | ||||
|    * | ||||
|    * @var \Drupal\block_content\BlockContentInterface | ||||
|    */ | ||||
|   protected $blockContent; | ||||
| 
 | ||||
|   /** | ||||
|    * The dependency. | ||||
|    * | ||||
|    * @var \Drupal\Core\Access\AccessibleInterface | ||||
|    */ | ||||
|   protected $accessDependency; | ||||
| 
 | ||||
|   /** | ||||
|    * BlockContentGetDependencyEvent constructor. | ||||
|    * | ||||
|    * @param \Drupal\block_content\BlockContentInterface $blockContent | ||||
|    *   The block content entity. | ||||
|    */ | ||||
|   public function __construct(BlockContentInterface $blockContent) { | ||||
|     $this->blockContent = $blockContent; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Gets the block content entity. | ||||
|    * | ||||
|    * @return \Drupal\block_content\BlockContentInterface | ||||
|    *   The block content entity. | ||||
|    */ | ||||
|   public function getBlockContentEntity() { | ||||
|     return $this->blockContent; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Gets the access dependency. | ||||
|    * | ||||
|    * @return \Drupal\Core\Access\AccessibleInterface | ||||
|    *   The access dependency. | ||||
|    */ | ||||
|   public function getAccessDependency() { | ||||
|     return $this->accessDependency; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Sets the access dependency. | ||||
|    * | ||||
|    * @param \Drupal\Core\Access\AccessibleInterface $access_dependency | ||||
|    *   The access dependency. | ||||
|    */ | ||||
|   public function setAccessDependency(AccessibleInterface $access_dependency) { | ||||
|     $this->accessDependency = $access_dependency; | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										25
									
								
								src/Form/BlockContentDeleteForm.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/Form/BlockContentDeleteForm.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content\Form; | ||||
| 
 | ||||
| use Drupal\Core\Entity\ContentEntityDeleteForm; | ||||
| 
 | ||||
| /** | ||||
|  * Provides a confirmation form for deleting a content block entity. | ||||
|  * | ||||
|  * @internal | ||||
|  */ | ||||
| class BlockContentDeleteForm extends ContentEntityDeleteForm { | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function getDescription() { | ||||
|     $instances = $this->entity->getInstances(); | ||||
|     if (!empty($instances)) { | ||||
|       return $this->formatPlural(count($instances), 'This will also remove 1 placed block instance. This action cannot be undone.', 'This will also remove @count placed block instances. This action cannot be undone.'); | ||||
|     } | ||||
|     return parent::getDescription(); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										80
									
								
								src/Form/BlockContentListFiltersForm.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								src/Form/BlockContentListFiltersForm.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,80 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content\Form; | ||||
| 
 | ||||
| use Drupal\Core\Form\FormBase; | ||||
| use Drupal\Core\Form\FormStateInterface; | ||||
| 
 | ||||
| /** | ||||
|  * Form for filters on the blocks entity list page. | ||||
|  */ | ||||
| class BlockContentListFiltersForm extends FormBase { | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function getFormId() { | ||||
|     return 'block_content_list_filters_form'; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function buildForm(array $form, FormStateInterface $form_state) { | ||||
|     $request = \Drupal::request(); | ||||
|     $form['filter'] = [ | ||||
|       '#type' => 'container', | ||||
|       '#attributes' => [ | ||||
|         'class' => ['form--inline', 'clearfix'], | ||||
|       ], | ||||
|     ]; | ||||
| 
 | ||||
|     $form['filter']['foo'] = [ | ||||
|       '#type' => 'textfield', | ||||
|       '#title' => 'Search for block', | ||||
|       '#default_value' => $request->get('foo') ?? "", | ||||
|     ]; | ||||
| 
 | ||||
|     $form['actions']['wrapper'] = [ | ||||
|       '#type' => 'container', | ||||
|       '#attributes' => ['class' => ['form-item']], | ||||
|     ]; | ||||
| 
 | ||||
|     $form['actions']['wrapper']['submit'] = [ | ||||
|       '#type' => 'submit', | ||||
|       '#value' => 'Filter', | ||||
|     ]; | ||||
| 
 | ||||
|     if ($request->getQueryString()) { | ||||
|       $form['actions']['wrapper']['reset'] = [ | ||||
|         '#type' => 'submit', | ||||
|         '#value' => 'Reset', | ||||
|         '#submit' => ['::resetForm'], | ||||
|       ]; | ||||
|     } | ||||
| 
 | ||||
|     return $form; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function submitForm(array &$form, FormStateInterface $form_state) { | ||||
|     $query = []; | ||||
| 
 | ||||
|     $foo = $form_state->getValue('foo') ?? 0; | ||||
|     if ($foo) { | ||||
|       $query['foo'] = $foo; | ||||
|     } | ||||
| 
 | ||||
|     $form_state->setRedirect('entity.block_content.collection', $query); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function resetForm(array $form, FormStateInterface &$form_state) { | ||||
|     $form_state->setRedirect('entity.block_content.collection'); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										34
									
								
								src/Form/BlockContentTypeDeleteForm.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/Form/BlockContentTypeDeleteForm.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content\Form; | ||||
| 
 | ||||
| use Drupal\Core\Entity\EntityDeleteForm; | ||||
| use Drupal\Core\Form\FormStateInterface; | ||||
| 
 | ||||
| /** | ||||
|  * Provides a confirmation form for deleting a block type entity. | ||||
|  * | ||||
|  * @internal | ||||
|  */ | ||||
| class BlockContentTypeDeleteForm extends EntityDeleteForm { | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function buildForm(array $form, FormStateInterface $form_state) { | ||||
|     $block_count = $this->entityTypeManager->getStorage('block_content')->getQuery() | ||||
|       ->accessCheck(FALSE) | ||||
|       ->condition('type', $this->entity->id()) | ||||
|       ->count() | ||||
|       ->execute(); | ||||
|     if ($block_count) { | ||||
|       $caption = '<p>' . $this->formatPlural($block_count, '%label is used by 1 content block on your site. You can not remove this block type until you have removed all of the %label blocks.', '%label is used by @count content blocks on your site. You may not remove %label until you have removed all of the %label content blocks.', ['%label' => $this->entity->label()]) . '</p>'; | ||||
|       $form['description'] = ['#markup' => $caption]; | ||||
|       return $form; | ||||
|     } | ||||
|     else { | ||||
|       return parent::buildForm($form, $form_state); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										218
									
								
								src/Plugin/Block/BlockContentBlock.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										218
									
								
								src/Plugin/Block/BlockContentBlock.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,218 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content\Plugin\Block; | ||||
| 
 | ||||
| use Drupal\block_content\BlockContentUuidLookup; | ||||
| use Drupal\block_content\Plugin\Derivative\BlockContent; | ||||
| use Drupal\Core\Access\AccessResult; | ||||
| use Drupal\Core\Block\Attribute\Block; | ||||
| use Drupal\Core\Block\BlockBase; | ||||
| use Drupal\Core\Block\BlockManagerInterface; | ||||
| use Drupal\Core\Entity\EntityDisplayRepositoryInterface; | ||||
| use Drupal\Core\Entity\EntityTypeManagerInterface; | ||||
| use Drupal\Core\Form\FormStateInterface; | ||||
| use Drupal\Core\Plugin\ContainerFactoryPluginInterface; | ||||
| use Drupal\Core\Routing\UrlGeneratorInterface; | ||||
| use Drupal\Core\Session\AccountInterface; | ||||
| use Drupal\Core\StringTranslation\TranslatableMarkup; | ||||
| use Symfony\Component\DependencyInjection\ContainerInterface; | ||||
| 
 | ||||
| /** | ||||
|  * Defines a generic block type. | ||||
|  */ | ||||
| #[Block(
 | ||||
|   id: "block_content", | ||||
|   admin_label: new TranslatableMarkup("Content block"), | ||||
|   category: new TranslatableMarkup("Content block"), | ||||
|   deriver: BlockContent::class | ||||
| )] | ||||
| class BlockContentBlock extends BlockBase implements ContainerFactoryPluginInterface { | ||||
| 
 | ||||
|   /** | ||||
|    * The Plugin Block Manager. | ||||
|    * | ||||
|    * @var \Drupal\Core\Block\BlockManagerInterface | ||||
|    */ | ||||
|   protected $blockManager; | ||||
| 
 | ||||
|   /** | ||||
|    * The entity type manager service. | ||||
|    * | ||||
|    * @var \Drupal\Core\Entity\EntityTypeManagerInterface | ||||
|    */ | ||||
|   protected $entityTypeManager; | ||||
| 
 | ||||
|   /** | ||||
|    * The Drupal account to use for checking for access to block. | ||||
|    * | ||||
|    * @var \Drupal\Core\Session\AccountInterface | ||||
|    */ | ||||
|   protected $account; | ||||
| 
 | ||||
|   /** | ||||
|    * The block content entity. | ||||
|    * | ||||
|    * @var \Drupal\block_content\BlockContentInterface | ||||
|    */ | ||||
|   protected $blockContent; | ||||
| 
 | ||||
|   /** | ||||
|    * The URL generator. | ||||
|    * | ||||
|    * @var \Drupal\Core\Routing\UrlGeneratorInterface | ||||
|    */ | ||||
|   protected $urlGenerator; | ||||
| 
 | ||||
|   /** | ||||
|    * The block content UUID lookup service. | ||||
|    * | ||||
|    * @var \Drupal\block_content\BlockContentUuidLookup | ||||
|    */ | ||||
|   protected $uuidLookup; | ||||
| 
 | ||||
|   /** | ||||
|    * The entity display repository. | ||||
|    * | ||||
|    * @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface | ||||
|    */ | ||||
|   protected $entityDisplayRepository; | ||||
| 
 | ||||
|   /** | ||||
|    * Constructs a new BlockContentBlock. | ||||
|    * | ||||
|    * @param array $configuration | ||||
|    *   A configuration array containing information about the plugin instance. | ||||
|    * @param string $plugin_id | ||||
|    *   The plugin ID for the plugin instance. | ||||
|    * @param mixed $plugin_definition | ||||
|    *   The plugin implementation definition. | ||||
|    * @param \Drupal\Core\Block\BlockManagerInterface $block_manager | ||||
|    *   The Plugin Block Manager. | ||||
|    * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager | ||||
|    *   The entity type manager service. | ||||
|    * @param \Drupal\Core\Session\AccountInterface $account | ||||
|    *   The account for which view access should be checked. | ||||
|    * @param \Drupal\Core\Routing\UrlGeneratorInterface $url_generator | ||||
|    *   The URL generator. | ||||
|    * @param \Drupal\block_content\BlockContentUuidLookup $uuid_lookup | ||||
|    *   The block content UUID lookup service. | ||||
|    * @param \Drupal\Core\Entity\EntityDisplayRepositoryInterface $entity_display_repository | ||||
|    *   The entity display repository. | ||||
|    */ | ||||
|   public function __construct(array $configuration, $plugin_id, $plugin_definition, BlockManagerInterface $block_manager, EntityTypeManagerInterface $entity_type_manager, AccountInterface $account, UrlGeneratorInterface $url_generator, BlockContentUuidLookup $uuid_lookup, EntityDisplayRepositoryInterface $entity_display_repository) { | ||||
|     parent::__construct($configuration, $plugin_id, $plugin_definition); | ||||
| 
 | ||||
|     $this->blockManager = $block_manager; | ||||
|     $this->entityTypeManager = $entity_type_manager; | ||||
|     $this->account = $account; | ||||
|     $this->urlGenerator = $url_generator; | ||||
|     $this->uuidLookup = $uuid_lookup; | ||||
|     $this->entityDisplayRepository = $entity_display_repository; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { | ||||
|     return new static( | ||||
|       $configuration, | ||||
|       $plugin_id, | ||||
|       $plugin_definition, | ||||
|       $container->get('plugin.manager.block'), | ||||
|       $container->get('entity_type.manager'), | ||||
|       $container->get('current_user'), | ||||
|       $container->get('url_generator'), | ||||
|       $container->get('block_content.uuid_lookup'), | ||||
|       $container->get('entity_display.repository') | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function defaultConfiguration() { | ||||
|     return [ | ||||
|       'status' => TRUE, | ||||
|       'info' => '', | ||||
|       'view_mode' => 'full', | ||||
|     ]; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Overrides \Drupal\Core\Block\BlockBase::blockForm(). | ||||
|    * | ||||
|    * Adds body and description fields to the block configuration form. | ||||
|    */ | ||||
|   public function blockForm($form, FormStateInterface $form_state) { | ||||
|     $block = $this->getEntity(); | ||||
|     if (!$block) { | ||||
|       return $form; | ||||
|     } | ||||
|     $options = $this->entityDisplayRepository->getViewModeOptionsByBundle('block_content', $block->bundle()); | ||||
| 
 | ||||
|     $form['view_mode'] = [ | ||||
|       '#type' => 'select', | ||||
|       '#options' => $options, | ||||
|       '#title' => $this->t('View mode'), | ||||
|       '#description' => $this->t('Output the block in this view mode.'), | ||||
|       '#default_value' => $this->configuration['view_mode'], | ||||
|       '#access' => (count($options) > 1), | ||||
|     ]; | ||||
|     $form['title']['#description'] = $this->t('The title of the block as shown to the user.'); | ||||
|     return $form; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function blockSubmit($form, FormStateInterface $form_state) { | ||||
|     // Invalidate the block cache to update content block-based derivatives.
 | ||||
|     $this->configuration['view_mode'] = $form_state->getValue('view_mode'); | ||||
|     $this->blockManager->clearCachedDefinitions(); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function blockAccess(AccountInterface $account) { | ||||
|     if ($this->getEntity()) { | ||||
|       return $this->getEntity()->access('view', $account, TRUE); | ||||
|     } | ||||
|     return AccessResult::forbidden(); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function build() { | ||||
|     if ($block = $this->getEntity()) { | ||||
|       return $this->entityTypeManager->getViewBuilder($block->getEntityTypeId())->view($block, $this->configuration['view_mode']); | ||||
|     } | ||||
|     else { | ||||
|       return [ | ||||
|         '#markup' => $this->t('Block with uuid %uuid does not exist. <a href=":url">Add content block</a>.', [ | ||||
|           '%uuid' => $this->getDerivativeId(), | ||||
|           ':url' => $this->urlGenerator->generate('block_content.add_page'), | ||||
|         ]), | ||||
|         '#access' => $this->account->hasPermission('administer blocks'), | ||||
|       ]; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Loads the block content entity of the block. | ||||
|    * | ||||
|    * @return \Drupal\block_content\BlockContentInterface|null | ||||
|    *   The block content entity. | ||||
|    */ | ||||
|   protected function getEntity() { | ||||
|     if (!isset($this->blockContent)) { | ||||
|       $uuid = $this->getDerivativeId(); | ||||
|       if ($id = $this->uuidLookup->get($uuid)) { | ||||
|         $this->blockContent = $this->entityTypeManager->getStorage('block_content')->load($id); | ||||
|       } | ||||
|     } | ||||
|     return $this->blockContent; | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										60
									
								
								src/Plugin/Derivative/BlockContent.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/Plugin/Derivative/BlockContent.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,60 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content\Plugin\Derivative; | ||||
| 
 | ||||
| use Drupal\Component\Plugin\Derivative\DeriverBase; | ||||
| use Drupal\Core\Entity\EntityStorageInterface; | ||||
| use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface; | ||||
| use Symfony\Component\DependencyInjection\ContainerInterface; | ||||
| 
 | ||||
| /** | ||||
|  * Retrieves block plugin definitions for all content blocks. | ||||
|  */ | ||||
| class BlockContent extends DeriverBase implements ContainerDeriverInterface { | ||||
| 
 | ||||
|   /** | ||||
|    * The content block storage. | ||||
|    * | ||||
|    * @var \Drupal\Core\Entity\EntityStorageInterface | ||||
|    */ | ||||
|   protected $blockContentStorage; | ||||
| 
 | ||||
|   /** | ||||
|    * Constructs a BlockContent object. | ||||
|    * | ||||
|    * @param \Drupal\Core\Entity\EntityStorageInterface $block_content_storage | ||||
|    *   The content block storage. | ||||
|    */ | ||||
|   public function __construct(EntityStorageInterface $block_content_storage) { | ||||
|     $this->blockContentStorage = $block_content_storage; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public static function create(ContainerInterface $container, $base_plugin_id) { | ||||
|     $entity_type_manager = $container->get('entity_type.manager'); | ||||
|     return new static( | ||||
|       $entity_type_manager->getStorage('block_content') | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function getDerivativeDefinitions($base_plugin_definition) { | ||||
|     $block_contents = $this->blockContentStorage->loadByProperties(['reusable' => TRUE]); | ||||
|     // Reset the discovered definitions.
 | ||||
|     $this->derivatives = []; | ||||
|     /** @var \Drupal\block_content\Entity\BlockContent $block_content */ | ||||
|     foreach ($block_contents as $block_content) { | ||||
|       $this->derivatives[$block_content->uuid()] = $base_plugin_definition; | ||||
|       $this->derivatives[$block_content->uuid()]['admin_label'] = $block_content->label() ?? ($block_content->type->entity->label() . ': ' . $block_content->id()); | ||||
|       $this->derivatives[$block_content->uuid()]['config_dependencies']['content'] = [ | ||||
|         $block_content->getConfigDependencyName(), | ||||
|       ]; | ||||
|     } | ||||
|     return parent::getDerivativeDefinitions($base_plugin_definition); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										65
									
								
								src/Plugin/Menu/LocalAction/BlockContentAddLocalAction.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								src/Plugin/Menu/LocalAction/BlockContentAddLocalAction.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,65 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content\Plugin\Menu\LocalAction; | ||||
| 
 | ||||
| use Drupal\Core\Menu\LocalActionDefault; | ||||
| use Drupal\Core\Routing\RouteMatchInterface; | ||||
| use Drupal\Core\Routing\RouteProviderInterface; | ||||
| use Drupal\Core\Url; | ||||
| use Symfony\Component\DependencyInjection\ContainerInterface; | ||||
| use Symfony\Component\HttpFoundation\RequestStack; | ||||
| 
 | ||||
| /** | ||||
|  * Modifies the 'Add content block' local action. | ||||
|  */ | ||||
| class BlockContentAddLocalAction extends LocalActionDefault { | ||||
| 
 | ||||
|   /** | ||||
|    * Constructs a BlockContentAddLocalAction object. | ||||
|    */ | ||||
|   public function __construct( | ||||
|     array $configuration, | ||||
|     $plugin_id, | ||||
|     $plugin_definition, | ||||
|     RouteProviderInterface $routeProvider, | ||||
|     protected RequestStack $requestStack, | ||||
|   ) { | ||||
|     parent::__construct($configuration, $plugin_id, $plugin_definition, $routeProvider); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { | ||||
|     return new static( | ||||
|       $configuration, | ||||
|       $plugin_id, | ||||
|       $plugin_definition, | ||||
|       $container->get('router.route_provider'), | ||||
|       $container->get('request_stack'), | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function getOptions(RouteMatchInterface $route_match) { | ||||
|     $options = parent::getOptions($route_match); | ||||
|     // If the route specifies a theme, append it to the query string.
 | ||||
|     if ($theme = $route_match->getParameter('theme')) { | ||||
|       $options['query']['theme'] = $theme; | ||||
|     } | ||||
| 
 | ||||
|     // If the current request has a region, append it to the query string.
 | ||||
|     if ($region = $this->requestStack->getCurrentRequest()->query->getString('region')) { | ||||
|       $options['query']['region'] = $region; | ||||
|     } | ||||
| 
 | ||||
|     // Adds a destination on content block listing.
 | ||||
|     if ($route_match->getRouteName() == 'entity.block_content.collection') { | ||||
|       $options['query']['destination'] = Url::fromRoute('<current>')->toString(); | ||||
|     } | ||||
|     return $options; | ||||
|   } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,18 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content\Plugin\Validation\Constraint; | ||||
| 
 | ||||
| use Drupal\Core\Entity\Plugin\Validation\Constraint\EntityChangedConstraint; | ||||
| use Drupal\Core\StringTranslation\TranslatableMarkup; | ||||
| use Drupal\Core\Validation\Attribute\Constraint; | ||||
| 
 | ||||
| /** | ||||
|  * Validation constraint for the block content entity changed timestamp. | ||||
|  */ | ||||
| #[Constraint(
 | ||||
|   id: 'BlockContentEntityChanged', | ||||
|   label: new TranslatableMarkup('Block content entity changed', [], ['context' => 'Validation']), | ||||
|   type: ['entity'] | ||||
| )] | ||||
| class BlockContentEntityChangedConstraint extends EntityChangedConstraint { | ||||
| } | ||||
| @ -0,0 +1,30 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content\Plugin\Validation\Constraint; | ||||
| 
 | ||||
| use Drupal\block_content\BlockContentInterface; | ||||
| use Drupal\Core\Entity\Plugin\Validation\Constraint\EntityChangedConstraintValidator; | ||||
| use Symfony\Component\Validator\Constraint; | ||||
| 
 | ||||
| /** | ||||
|  * Validates the BlockContentEntityChanged constraint. | ||||
|  */ | ||||
| class BlockContentEntityChangedConstraintValidator extends EntityChangedConstraintValidator { | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function validate($entity, Constraint $constraint) { | ||||
|     // This prevents saving an update to the block via a host entity's form if
 | ||||
|     // the host entity has had other changes made via the API instead of the
 | ||||
|     // entity form, such as a revision revert. This is safe, for example, in the
 | ||||
|     // Layout Builder the inline blocks are not saved until the whole layout is
 | ||||
|     // saved, in which case Layout Builder forces a new revision for the block.
 | ||||
|     // @see \Drupal\layout_builder\InlineBlockEntityOperations::handlePreSave.
 | ||||
|     if ($entity instanceof BlockContentInterface && !$entity->isReusable()) { | ||||
|       return; | ||||
|     } | ||||
|     parent::validate($entity, $constraint); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										53
									
								
								src/Plugin/migrate/source/d6/Box.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/Plugin/migrate/source/d6/Box.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,53 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content\Plugin\migrate\source\d6; | ||||
| 
 | ||||
| use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase; | ||||
| 
 | ||||
| /** | ||||
|  * Drupal 6 block source from database. | ||||
|  * | ||||
|  * For available configuration keys, refer to the parent classes. | ||||
|  * | ||||
|  * @see \Drupal\migrate\Plugin\migrate\source\SqlBase | ||||
|  * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase | ||||
|  * | ||||
|  * @MigrateSource( | ||||
|  *   id = "d6_box", | ||||
|  *   source_module = "block" | ||||
|  * ) | ||||
|  */ | ||||
| class Box extends DrupalSqlBase { | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function query() { | ||||
|     $query = $this->select('boxes', 'b') | ||||
|       ->fields('b', ['bid', 'body', 'info', 'format']); | ||||
|     $query->orderBy('b.bid'); | ||||
| 
 | ||||
|     return $query; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function fields() { | ||||
|     return [ | ||||
|       'bid' => $this->t('The numeric identifier of the block/box'), | ||||
|       'body' => $this->t('The block/box content'), | ||||
|       'info' => $this->t('Admin title of the block/box.'), | ||||
|       'format' => $this->t('Input format of the content block/box content.'), | ||||
|     ]; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function getIds() { | ||||
|     $ids['bid']['type'] = 'integer'; | ||||
|     return $ids; | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										28
									
								
								src/Plugin/migrate/source/d6/BoxTranslation.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/Plugin/migrate/source/d6/BoxTranslation.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content\Plugin\migrate\source\d6; | ||||
| 
 | ||||
| use Drupal\block_content\Plugin\migrate\source\d7\BlockCustomTranslation as D7BlockCustomTranslation; | ||||
| 
 | ||||
| /** | ||||
|  * Drupal 6 i18n content block translations source from database. | ||||
|  * | ||||
|  * For available configuration keys, refer to the parent classes. | ||||
|  * | ||||
|  * @see \Drupal\migrate\Plugin\migrate\source\SqlBase | ||||
|  * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase | ||||
|  * | ||||
|  * @MigrateSource( | ||||
|  *   id = "d6_box_translation", | ||||
|  *   source_module = "i18nblocks" | ||||
|  * ) | ||||
|  */ | ||||
| class BoxTranslation extends D7BlockCustomTranslation { | ||||
| 
 | ||||
|   /** | ||||
|    * Drupal 6 table names. | ||||
|    */ | ||||
|   const CUSTOM_BLOCK_TABLE = 'boxes'; | ||||
|   const I18N_STRING_TABLE = 'i18n_strings'; | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										49
									
								
								src/Plugin/migrate/source/d7/BlockCustom.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/Plugin/migrate/source/d7/BlockCustom.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,49 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content\Plugin\migrate\source\d7; | ||||
| 
 | ||||
| use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase; | ||||
| 
 | ||||
| /** | ||||
|  * Drupal 7 content block source from database. | ||||
|  * | ||||
|  * For available configuration keys, refer to the parent classes. | ||||
|  * | ||||
|  * @see \Drupal\migrate\Plugin\migrate\source\SqlBase | ||||
|  * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase | ||||
|  * | ||||
|  * @MigrateSource( | ||||
|  *   id = "d7_block_custom", | ||||
|  *   source_module = "block" | ||||
|  * ) | ||||
|  */ | ||||
| class BlockCustom extends DrupalSqlBase { | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function query() { | ||||
|     return $this->select('block_custom', 'b')->fields('b'); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function fields() { | ||||
|     return [ | ||||
|       'bid' => $this->t('The numeric identifier of the block/box'), | ||||
|       'body' => $this->t('The block/box content'), | ||||
|       'info' => $this->t('Admin title of the block/box.'), | ||||
|       'format' => $this->t('Input format of the content block/box content.'), | ||||
|     ]; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function getIds() { | ||||
|     $ids['bid']['type'] = 'integer'; | ||||
|     return $ids; | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										105
									
								
								src/Plugin/migrate/source/d7/BlockCustomTranslation.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								src/Plugin/migrate/source/d7/BlockCustomTranslation.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,105 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content\Plugin\migrate\source\d7; | ||||
| 
 | ||||
| use Drupal\migrate\Row; | ||||
| use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase; | ||||
| use Drupal\content_translation\Plugin\migrate\source\I18nQueryTrait; | ||||
| 
 | ||||
| /** | ||||
|  * Drupal 7 i18n content block translations source from database. | ||||
|  * | ||||
|  * For available configuration keys, refer to the parent classes. | ||||
|  * | ||||
|  * @see \Drupal\migrate\Plugin\migrate\source\SqlBase | ||||
|  * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase | ||||
|  * | ||||
|  * @MigrateSource( | ||||
|  *   id = "d7_block_custom_translation", | ||||
|  *   source_module = "i18n_block" | ||||
|  * ) | ||||
|  */ | ||||
| class BlockCustomTranslation extends DrupalSqlBase { | ||||
| 
 | ||||
|   use I18nQueryTrait; | ||||
| 
 | ||||
|   /** | ||||
|    * Drupal 7 table names. | ||||
|    */ | ||||
|   const CUSTOM_BLOCK_TABLE = 'block_custom'; | ||||
|   const I18N_STRING_TABLE = 'i18n_string'; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function query() { | ||||
|     // Build a query based on blockCustomTable table where each row has the
 | ||||
|     // translation for only one property, either title or description. The
 | ||||
|     // method prepareRow() is then used to obtain the translation for the
 | ||||
|     // other property.
 | ||||
|     $query = $this->select(static::CUSTOM_BLOCK_TABLE, 'b') | ||||
|       ->fields('b', ['bid', 'format', 'body']) | ||||
|       ->fields('i18n', ['property']) | ||||
|       ->fields('lt', ['lid', 'translation', 'language']) | ||||
|       ->orderBy('b.bid'); | ||||
| 
 | ||||
|     // Use 'title' for the info field to match the property name in
 | ||||
|     // i18nStringTable.
 | ||||
|     $query->addField('b', 'info', 'title'); | ||||
| 
 | ||||
|     // Add in the property, which is either title or body. Cast the bid to text
 | ||||
|     // so PostgreSQL can make the join.
 | ||||
|     $query->leftJoin(static::I18N_STRING_TABLE, 'i18n', '[i18n].[objectid] = CAST([b].[bid] AS CHAR(255))'); | ||||
|     $query->condition('i18n.type', 'block'); | ||||
| 
 | ||||
|     // Add in the translation for the property.
 | ||||
|     $query->innerJoin('locales_target', 'lt', '[lt].[lid] = [i18n].[lid]'); | ||||
|     return $query; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function prepareRow(Row $row) { | ||||
|     if (!parent::prepareRow($row)) { | ||||
|       return FALSE; | ||||
|     } | ||||
|     // Set the i18n string table for use in I18nQueryTrait.
 | ||||
|     $this->i18nStringTable = static::I18N_STRING_TABLE; | ||||
|     // Save the translation for this property.
 | ||||
|     $property_in_row = $row->getSourceProperty('property'); | ||||
|     // Get the translation for the property not already in the row and save it
 | ||||
|     // in the row.
 | ||||
|     $property_not_in_row = ($property_in_row === 'title') ? 'body' : 'title'; | ||||
|     return $this->getPropertyNotInRowTranslation($row, $property_not_in_row, 'bid', $this->idMap); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function fields() { | ||||
|     return [ | ||||
|       'bid' => $this->t('The block numeric identifier.'), | ||||
|       'format' => $this->t('Input format of the content block/box content.'), | ||||
|       'lid' => $this->t('i18n_string table id'), | ||||
|       'language' => $this->t('Language for this field.'), | ||||
|       'property' => $this->t('Block property'), | ||||
|       'translation' => $this->t('The translation of the value of "property".'), | ||||
|       'title' => $this->t('Block title.'), | ||||
|       'title_translated' => $this->t('Block title translation.'), | ||||
|       'body' => $this->t('Block body.'), | ||||
|       'body_translated' => $this->t('Block body translation.'), | ||||
|     ]; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function getIds() { | ||||
|     $ids['bid']['type'] = 'integer'; | ||||
|     $ids['bid']['alias'] = 'b'; | ||||
|     $ids['language']['type'] = 'string'; | ||||
|     return $ids; | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										89
									
								
								src/Plugin/views/area/ListingEmpty.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								src/Plugin/views/area/ListingEmpty.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,89 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content\Plugin\views\area; | ||||
| 
 | ||||
| use Drupal\Core\Access\AccessManagerInterface; | ||||
| use Drupal\Core\Session\AccountInterface; | ||||
| use Drupal\Core\Url; | ||||
| use Drupal\views\Attribute\ViewsArea; | ||||
| use Drupal\views\Plugin\views\area\AreaPluginBase; | ||||
| use Symfony\Component\DependencyInjection\ContainerInterface; | ||||
| 
 | ||||
| /** | ||||
|  * Defines an area plugin to display a block add link. | ||||
|  * | ||||
|  * @ingroup views_area_handlers | ||||
|  */ | ||||
| #[ViewsArea("block_content_listing_empty")]
 | ||||
| class ListingEmpty extends AreaPluginBase { | ||||
| 
 | ||||
|   /** | ||||
|    * The access manager. | ||||
|    * | ||||
|    * @var \Drupal\Core\Access\AccessManagerInterface | ||||
|    */ | ||||
|   protected $accessManager; | ||||
| 
 | ||||
|   /** | ||||
|    * The current user. | ||||
|    * | ||||
|    * @var \Drupal\Core\Session\AccountInterface | ||||
|    */ | ||||
|   protected $currentUser; | ||||
| 
 | ||||
|   /** | ||||
|    * Constructs a new ListingEmpty. | ||||
|    * | ||||
|    * @param array $configuration | ||||
|    *   A configuration array containing information about the plugin instance. | ||||
|    * @param string $plugin_id | ||||
|    *   The plugin ID for the plugin instance. | ||||
|    * @param mixed $plugin_definition | ||||
|    *   The plugin implementation definition. | ||||
|    * @param \Drupal\Core\Access\AccessManagerInterface $access_manager | ||||
|    *   The access manager. | ||||
|    * @param \Drupal\Core\Session\AccountInterface $current_user | ||||
|    *   The current user. | ||||
|    */ | ||||
|   public function __construct(array $configuration, $plugin_id, $plugin_definition, AccessManagerInterface $access_manager, AccountInterface $current_user) { | ||||
|     parent::__construct($configuration, $plugin_id, $plugin_definition); | ||||
| 
 | ||||
|     $this->accessManager = $access_manager; | ||||
|     $this->currentUser = $current_user; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { | ||||
|     return new static( | ||||
|       $configuration, | ||||
|       $plugin_id, | ||||
|       $plugin_definition, | ||||
|       $container->get('access_manager'), | ||||
|       $container->get('current_user') | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function render($empty = FALSE) { | ||||
|     if (!$empty || !empty($this->options['empty'])) { | ||||
|       /** @var \Drupal\Core\Access\AccessResultInterface|\Drupal\Core\Cache\CacheableDependencyInterface $access_result */ | ||||
|       $access_result = $this->accessManager->checkNamedRoute('block_content.add_page', [], $this->currentUser, TRUE); | ||||
|       $element = [ | ||||
|         '#markup' => $this->t('Add a <a href=":url">content block</a>.', [':url' => Url::fromRoute('block_content.add_page')->toString()]), | ||||
|         '#access' => $access_result->isAllowed(), | ||||
|         '#cache' => [ | ||||
|           'contexts' => $access_result->getCacheContexts(), | ||||
|           'tags' => $access_result->getCacheTags(), | ||||
|           'max-age' => $access_result->getCacheMaxAge(), | ||||
|         ], | ||||
|       ]; | ||||
|       return $element; | ||||
|     } | ||||
|     return []; | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										36
									
								
								src/Plugin/views/wizard/BlockContent.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/Plugin/views/wizard/BlockContent.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content\Plugin\views\wizard; | ||||
| 
 | ||||
| use Drupal\Core\StringTranslation\TranslatableMarkup; | ||||
| use Drupal\views\Attribute\ViewsWizard; | ||||
| use Drupal\views\Plugin\views\wizard\WizardPluginBase; | ||||
| 
 | ||||
| /** | ||||
|  * Used for creating 'block_content' views with the wizard. | ||||
|  */ | ||||
| #[ViewsWizard(
 | ||||
|   id: 'block_content', | ||||
|   title: new TranslatableMarkup('Content Block'), | ||||
|   base_table: 'block_content_field_data' | ||||
| )] | ||||
| class BlockContent extends WizardPluginBase { | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function getFilters() { | ||||
|     $filters = parent::getFilters(); | ||||
|     $filters['reusable'] = [ | ||||
|       'id' => 'reusable', | ||||
|       'plugin_id' => 'boolean', | ||||
|       'table' => $this->base_table, | ||||
|       'field' => 'reusable', | ||||
|       'value' => '1', | ||||
|       'entity_type' => $this->entityTypeId, | ||||
|       'entity_field' => 'reusable', | ||||
|     ]; | ||||
|     return $filters; | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										237
									
								
								src/Routing/RouteSubscriber.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										237
									
								
								src/Routing/RouteSubscriber.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,237 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content\Routing; | ||||
| 
 | ||||
| use Drupal\Core\Entity\EntityTypeInterface; | ||||
| use Drupal\Core\Entity\EntityTypeManagerInterface; | ||||
| use Drupal\Core\Extension\ModuleHandlerInterface; | ||||
| use Drupal\Core\Routing\RouteBuildEvent; | ||||
| use Drupal\Core\Routing\RouteSubscriberBase; | ||||
| use Drupal\Core\Routing\RoutingEvents; | ||||
| use Symfony\Component\Routing\RouteCollection; | ||||
| 
 | ||||
| /** | ||||
|  * Subscriber for Block content BC routes. | ||||
|  */ | ||||
| class RouteSubscriber extends RouteSubscriberBase { | ||||
| 
 | ||||
|   /** | ||||
|    * The entity type manager service. | ||||
|    * | ||||
|    * @var \Drupal\Core\Entity\EntityTypeManagerInterface | ||||
|    */ | ||||
|   protected $entityTypeManager; | ||||
| 
 | ||||
|   /** | ||||
|    * The module handler. | ||||
|    * | ||||
|    * @var \Drupal\Core\Extension\ModuleHandlerInterface | ||||
|    */ | ||||
|   protected $moduleHandler; | ||||
| 
 | ||||
|   /** | ||||
|    * The route collection for adding routes. | ||||
|    * | ||||
|    * @var \Symfony\Component\Routing\RouteCollection | ||||
|    */ | ||||
|   protected $collection; | ||||
| 
 | ||||
|   /** | ||||
|    * The current base path. | ||||
|    * | ||||
|    * @var string | ||||
|    */ | ||||
|   protected $basePath; | ||||
| 
 | ||||
|   /** | ||||
|    * The BC base path. | ||||
|    * | ||||
|    * @var string | ||||
|    */ | ||||
|   protected $basePathBc; | ||||
| 
 | ||||
|   /** | ||||
|    * The redirect controller. | ||||
|    * | ||||
|    * @var string | ||||
|    */ | ||||
|   protected $controller; | ||||
| 
 | ||||
|   /** | ||||
|    * Constructs a RouteSubscriber object. | ||||
|    * | ||||
|    * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager | ||||
|    *   The entity type manager service. | ||||
|    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler | ||||
|    *   The module handler. | ||||
|    */ | ||||
|   public function __construct(EntityTypeManagerInterface $entity_type_manager, ModuleHandlerInterface $module_handler) { | ||||
|     $this->entityTypeManager = $entity_type_manager; | ||||
|     $this->moduleHandler = $module_handler; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function alterRoutes(RouteCollection $collection) { | ||||
|     $this->collection = $collection; | ||||
| 
 | ||||
|     // @see block_content.routing.yml
 | ||||
|     if ($this->setUpBaseRoute('entity.block_content_type.collection')) { | ||||
|       $this->addRedirectRoute('block_content.type_add'); | ||||
|     } | ||||
| 
 | ||||
|     $entity_type = $this->entityTypeManager->getDefinition('block_content'); | ||||
|     if ($this->setUpBaseRoute($entity_type->get('field_ui_base_route'))) { | ||||
|       foreach ($this->childRoutes($entity_type) as $route_name) { | ||||
|         $this->addRedirectRoute($route_name); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Gets parameters from a base route and saves them in class variables. | ||||
|    * | ||||
|    * @param string $base_route_name | ||||
|    *   The name of a base route that already has a BC variant. | ||||
|    * | ||||
|    * @return bool | ||||
|    *   TRUE if all parameters are set, FALSE if not. | ||||
|    */ | ||||
|   protected function setUpBaseRoute(string $base_route_name): bool { | ||||
|     $base_route = $this->collection->get($base_route_name); | ||||
|     $base_route_bc = $this->collection->get("$base_route_name.bc"); | ||||
|     if (empty($base_route) || empty($base_route_bc)) { | ||||
|       return FALSE; | ||||
|     } | ||||
| 
 | ||||
|     $this->basePath = $base_route->getPath(); | ||||
|     $this->basePathBc = $base_route_bc->getPath(); | ||||
|     $this->controller = $base_route_bc->getDefault('_controller'); | ||||
|     if (empty($this->basePath) || empty($this->basePathBc) || empty($this->controller) || $this->basePathBc === $this->basePath) { | ||||
|       return FALSE; | ||||
|     } | ||||
| 
 | ||||
|     return TRUE; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Adds a redirect route. | ||||
|    * | ||||
|    * @param string $route_name | ||||
|    *   The name of a route whose path has changed. | ||||
|    */ | ||||
|   protected function addRedirectRoute(string $route_name): void { | ||||
|     // Exit early if the BC route is already there.
 | ||||
|     if (!empty($this->collection->get("$route_name.bc"))) { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     $route = $this->collection->get($route_name); | ||||
|     if (empty($route)) { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     $new_path = $route->getPath(); | ||||
|     if (!str_starts_with($new_path, $this->basePath)) { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     $bc_route = clone $route; | ||||
|     // Set the path to what it was in earlier versions of Drupal.
 | ||||
|     $bc_route->setPath($this->basePathBc . substr($new_path, strlen($this->basePath))); | ||||
|     if ($bc_route->getPath() === $route->getPath()) { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     // Replace the handler with the stored redirect controller.
 | ||||
|     $defaults = array_diff_key($route->getDefaults(), array_flip([ | ||||
|       '_entity_form', | ||||
|       '_entity_list', | ||||
|       '_entity_view', | ||||
|       '_form', | ||||
|     ])); | ||||
|     $defaults['_controller'] = $this->controller; | ||||
|     $bc_route->setDefaults($defaults); | ||||
| 
 | ||||
|     $this->collection->add("$route_name.bc", $bc_route); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Creates a list of routes that need BC redirects. | ||||
|    * | ||||
|    * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type | ||||
|    *   The entity type. | ||||
|    * | ||||
|    * @return string[] | ||||
|    *   A list of route names. | ||||
|    */ | ||||
|   protected function childRoutes(EntityTypeInterface $entity_type): array { | ||||
|     $route_names = []; | ||||
| 
 | ||||
|     if ($field_ui_base_route = $entity_type->get('field_ui_base_route')) { | ||||
|       $updated_routes = new RouteCollection(); | ||||
|       $updated_routes->add($field_ui_base_route, $this->collection->get($field_ui_base_route)); | ||||
|       $event = new RouteBuildEvent($updated_routes); | ||||
| 
 | ||||
|       // Apply route subscribers that add routes based on field_ui_base_route,
 | ||||
|       // in the order of their weights.
 | ||||
|       $subscribers = [ | ||||
|         'field_ui' => 'field_ui.subscriber', | ||||
|         'content_translation' => 'content_translation.subscriber', | ||||
|       ]; | ||||
|       foreach ($subscribers as $module_name => $service_name) { | ||||
|         if ($this->moduleHandler->moduleExists($module_name)) { | ||||
|           \Drupal::service($service_name)->onAlterRoutes($event); | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       $updated_routes->remove($field_ui_base_route); | ||||
|       $route_names = array_merge($route_names, array_keys($updated_routes->all())); | ||||
|       $route_names = array_merge($route_names, [ | ||||
|         // @see \Drupal\config_translation\Routing\RouteSubscriber::alterRoutes()
 | ||||
|         "config_translation.item.add.{$field_ui_base_route}", | ||||
|         "config_translation.item.edit.{$field_ui_base_route}", | ||||
|         "config_translation.item.delete.{$field_ui_base_route}", | ||||
|       ]); | ||||
|     } | ||||
| 
 | ||||
|     if ($entity_type_id = $entity_type->getBundleEntityType()) { | ||||
|       $route_names = array_merge($route_names, [ | ||||
|         // @see \Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider::getRoutes()
 | ||||
|         "entity.{$entity_type_id}.delete_form", | ||||
|         // @see \Drupal\config_translation\Routing\RouteSubscriber::alterRoutes()
 | ||||
|         "entity.{$entity_type_id}.config_translation_overview", | ||||
|         // @see \Drupal\user\Entity\EntityPermissionsRouteProvider::getRoutes()
 | ||||
|         "entity.{$entity_type_id}.entity_permissions_form", | ||||
|       ]); | ||||
|     } | ||||
| 
 | ||||
|     if ($entity_id = $entity_type->id()) { | ||||
|       $route_names = array_merge($route_names, [ | ||||
|         // @see \Drupal\config_translation\Routing\RouteSubscriber::alterRoutes()
 | ||||
|         "entity.field_config.config_translation_overview.{$entity_id}", | ||||
|         "config_translation.item.add.entity.field_config.{$entity_id}_field_edit_form", | ||||
|         "config_translation.item.edit.entity.field_config.{$entity_id}_field_edit_form", | ||||
|         "config_translation.item.delete.entity.field_config.{$entity_id}_field_edit_form", | ||||
|         // @see \Drupal\layout_builder\Plugin\SectionStorage\DefaultsSectionStorage::buildRoutes()
 | ||||
|         "layout_builder.defaults.{$entity_id}.disable", | ||||
|         "layout_builder.defaults.{$entity_id}.discard_changes", | ||||
|         "layout_builder.defaults.{$entity_id}.view", | ||||
|       ]); | ||||
|     } | ||||
| 
 | ||||
|     return $route_names; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public static function getSubscribedEvents(): array { | ||||
|     $events = parent::getSubscribedEvents(); | ||||
|     // Go after ContentTranslationRouteSubscriber.
 | ||||
|     $events[RoutingEvents::ALTER] = ['onAlterRoutes', -300]; | ||||
|     return $events; | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										24
									
								
								templates/block-content-add-list.html.twig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								templates/block-content-add-list.html.twig
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | ||||
| {# | ||||
| /** | ||||
|  * @file | ||||
|  * Default theme implementation to present a list of block types. | ||||
|  * | ||||
|  * Available variables: | ||||
|  * - types: A collection of all the available content block types. | ||||
|  *   Each block type contains the following: | ||||
|  *   - link: A link to add a block of this type. | ||||
|  *   - description: A description of this block type. | ||||
|  * | ||||
|  * @see template_preprocess_block_content_add_list() | ||||
|  * | ||||
|  * @ingroup themeable | ||||
|  */ | ||||
| #} | ||||
| {% apply spaceless %} | ||||
|   <dl> | ||||
|     {% for type in types %} | ||||
|       <dt>{{ type.link }}</dt> | ||||
|       <dd>{{ type.description }}</dd> | ||||
|     {% endfor %} | ||||
|   </dl> | ||||
| {% endapply %} | ||||
							
								
								
									
										540
									
								
								tests/fixtures/update/views.view.block_content_2862564.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										540
									
								
								tests/fixtures/update/views.view.block_content_2862564.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,540 @@ | ||||
| langcode: en | ||||
| status: true | ||||
| dependencies: | ||||
|   module: | ||||
|     - block_content | ||||
|     - user | ||||
| id: block_content | ||||
| label: 'Content blocks' | ||||
| module: views | ||||
| description: 'Create and edit content blocks.' | ||||
| tag: default | ||||
| base_table: block_content_field_data | ||||
| base_field: id | ||||
| display: | ||||
|   default: | ||||
|     id: default | ||||
|     display_title: Default | ||||
|     display_plugin: default | ||||
|     position: 0 | ||||
|     display_options: | ||||
|       title: 'Content blocks' | ||||
|       fields: | ||||
|         info: | ||||
|           id: info | ||||
|           table: block_content_field_data | ||||
|           field: info | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           entity_type: null | ||||
|           entity_field: info | ||||
|           plugin_id: field | ||||
|           label: 'Block description' | ||||
|           exclude: false | ||||
|           alter: | ||||
|             alter_text: false | ||||
|             text: '' | ||||
|             make_link: false | ||||
|             path: '' | ||||
|             absolute: false | ||||
|             external: false | ||||
|             replace_spaces: false | ||||
|             path_case: none | ||||
|             trim_whitespace: false | ||||
|             alt: '' | ||||
|             rel: '' | ||||
|             link_class: '' | ||||
|             prefix: '' | ||||
|             suffix: '' | ||||
|             target: '' | ||||
|             nl2br: false | ||||
|             max_length: 0 | ||||
|             word_boundary: true | ||||
|             ellipsis: true | ||||
|             more_link: false | ||||
|             more_link_text: '' | ||||
|             more_link_path: '' | ||||
|             strip_tags: false | ||||
|             trim: false | ||||
|             preserve_tags: '' | ||||
|             html: false | ||||
|           element_type: '' | ||||
|           element_class: '' | ||||
|           element_label_type: '' | ||||
|           element_label_class: '' | ||||
|           element_label_colon: true | ||||
|           element_wrapper_type: '' | ||||
|           element_wrapper_class: '' | ||||
|           element_default_classes: true | ||||
|           empty: '' | ||||
|           hide_empty: false | ||||
|           empty_zero: false | ||||
|           hide_alter_empty: true | ||||
|           click_sort_column: value | ||||
|           type: string | ||||
|           settings: | ||||
|             link_to_entity: true | ||||
|           group_column: value | ||||
|           group_columns: {  } | ||||
|           group_rows: true | ||||
|           delta_limit: 0 | ||||
|           delta_offset: 0 | ||||
|           delta_reversed: false | ||||
|           delta_first_last: false | ||||
|           multi_type: separator | ||||
|           separator: ', ' | ||||
|           field_api_classes: false | ||||
|         type: | ||||
|           id: type | ||||
|           table: block_content_field_data | ||||
|           field: type | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           entity_type: block_content | ||||
|           entity_field: type | ||||
|           plugin_id: field | ||||
|           label: 'Block type' | ||||
|           exclude: false | ||||
|           alter: | ||||
|             alter_text: false | ||||
|             text: '' | ||||
|             make_link: false | ||||
|             path: '' | ||||
|             absolute: false | ||||
|             external: false | ||||
|             replace_spaces: false | ||||
|             path_case: none | ||||
|             trim_whitespace: false | ||||
|             alt: '' | ||||
|             rel: '' | ||||
|             link_class: '' | ||||
|             prefix: '' | ||||
|             suffix: '' | ||||
|             target: '' | ||||
|             nl2br: false | ||||
|             max_length: 0 | ||||
|             word_boundary: true | ||||
|             ellipsis: true | ||||
|             more_link: false | ||||
|             more_link_text: '' | ||||
|             more_link_path: '' | ||||
|             strip_tags: false | ||||
|             trim: false | ||||
|             preserve_tags: '' | ||||
|             html: false | ||||
|           element_type: '' | ||||
|           element_class: '' | ||||
|           element_label_type: '' | ||||
|           element_label_class: '' | ||||
|           element_label_colon: true | ||||
|           element_wrapper_type: '' | ||||
|           element_wrapper_class: '' | ||||
|           element_default_classes: true | ||||
|           empty: '' | ||||
|           hide_empty: false | ||||
|           empty_zero: false | ||||
|           hide_alter_empty: true | ||||
|           click_sort_column: target_id | ||||
|           type: entity_reference_label | ||||
|           settings: | ||||
|             link: false | ||||
|           group_column: target_id | ||||
|           group_columns: {  } | ||||
|           group_rows: true | ||||
|           delta_limit: 0 | ||||
|           delta_offset: 0 | ||||
|           delta_reversed: false | ||||
|           delta_first_last: false | ||||
|           multi_type: separator | ||||
|           separator: ', ' | ||||
|           field_api_classes: false | ||||
|         changed: | ||||
|           id: changed | ||||
|           table: block_content_field_data | ||||
|           field: changed | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           entity_type: block_content | ||||
|           entity_field: changed | ||||
|           plugin_id: field | ||||
|           label: Updated | ||||
|           exclude: false | ||||
|           alter: | ||||
|             alter_text: false | ||||
|             text: '' | ||||
|             make_link: false | ||||
|             path: '' | ||||
|             absolute: false | ||||
|             external: false | ||||
|             replace_spaces: false | ||||
|             path_case: none | ||||
|             trim_whitespace: false | ||||
|             alt: '' | ||||
|             rel: '' | ||||
|             link_class: '' | ||||
|             prefix: '' | ||||
|             suffix: '' | ||||
|             target: '' | ||||
|             nl2br: false | ||||
|             max_length: 0 | ||||
|             word_boundary: true | ||||
|             ellipsis: true | ||||
|             more_link: false | ||||
|             more_link_text: '' | ||||
|             more_link_path: '' | ||||
|             strip_tags: false | ||||
|             trim: false | ||||
|             preserve_tags: '' | ||||
|             html: false | ||||
|           element_type: '' | ||||
|           element_class: '' | ||||
|           element_label_type: '' | ||||
|           element_label_class: '' | ||||
|           element_label_colon: true | ||||
|           element_wrapper_type: '' | ||||
|           element_wrapper_class: '' | ||||
|           element_default_classes: true | ||||
|           empty: '' | ||||
|           hide_empty: false | ||||
|           empty_zero: false | ||||
|           hide_alter_empty: true | ||||
|           type: timestamp | ||||
|           settings: | ||||
|             date_format: short | ||||
|             custom_date_format: '' | ||||
|             timezone: '' | ||||
|         operations: | ||||
|           id: operations | ||||
|           table: block_content | ||||
|           field: operations | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           entity_type: block_content | ||||
|           plugin_id: entity_operations | ||||
|           label: Operations | ||||
|           exclude: false | ||||
|           alter: | ||||
|             alter_text: false | ||||
|             text: '' | ||||
|             make_link: false | ||||
|             path: '' | ||||
|             absolute: false | ||||
|             external: false | ||||
|             replace_spaces: false | ||||
|             path_case: none | ||||
|             trim_whitespace: false | ||||
|             alt: '' | ||||
|             rel: '' | ||||
|             link_class: '' | ||||
|             prefix: '' | ||||
|             suffix: '' | ||||
|             target: '' | ||||
|             nl2br: false | ||||
|             max_length: 0 | ||||
|             word_boundary: true | ||||
|             ellipsis: true | ||||
|             more_link: false | ||||
|             more_link_text: '' | ||||
|             more_link_path: '' | ||||
|             strip_tags: false | ||||
|             trim: false | ||||
|             preserve_tags: '' | ||||
|             html: false | ||||
|           element_type: '' | ||||
|           element_class: '' | ||||
|           element_label_type: '' | ||||
|           element_label_class: '' | ||||
|           element_label_colon: true | ||||
|           element_wrapper_type: '' | ||||
|           element_wrapper_class: '' | ||||
|           element_default_classes: true | ||||
|           empty: '' | ||||
|           hide_empty: false | ||||
|           empty_zero: false | ||||
|           hide_alter_empty: true | ||||
|           destination: true | ||||
|       pager: | ||||
|         type: mini | ||||
|         options: | ||||
|           offset: 0 | ||||
|           items_per_page: 50 | ||||
|           total_pages: null | ||||
|           id: 0 | ||||
|           tags: | ||||
|             next: 'Next ›' | ||||
|             previous: '‹ Previous' | ||||
|           expose: | ||||
|             items_per_page: false | ||||
|             items_per_page_label: 'Items per page' | ||||
|             items_per_page_options: '5, 10, 25, 50' | ||||
|             items_per_page_options_all: false | ||||
|             items_per_page_options_all_label: '- All -' | ||||
|             offset: false | ||||
|             offset_label: Offset | ||||
|       exposed_form: | ||||
|         type: basic | ||||
|         options: | ||||
|           submit_button: Apply | ||||
|           reset_button: true | ||||
|           reset_button_label: Reset | ||||
|           exposed_sorts_label: 'Sort by' | ||||
|           expose_sort_order: true | ||||
|           sort_asc_label: Asc | ||||
|           sort_desc_label: Desc | ||||
|       access: | ||||
|         type: perm | ||||
|         options: | ||||
|           perm: 'administer blocks' | ||||
|       cache: | ||||
|         type: tag | ||||
|         options: {  } | ||||
|       empty: | ||||
|         area_text_custom: | ||||
|           id: area_text_custom | ||||
|           table: views | ||||
|           field: area_text_custom | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           plugin_id: text_custom | ||||
|           empty: true | ||||
|           content: 'There are no content blocks available.' | ||||
|           tokenize: false | ||||
|         block_content_listing_empty: | ||||
|           id: block_content_listing_empty | ||||
|           table: block_content | ||||
|           field: block_content_listing_empty | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           entity_type: block_content | ||||
|           plugin_id: block_content_listing_empty | ||||
|           label: '' | ||||
|           empty: true | ||||
|       sorts: {  } | ||||
|       arguments: {  } | ||||
|       filters: | ||||
|         info: | ||||
|           id: info | ||||
|           table: block_content_field_data | ||||
|           field: info | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           entity_type: block_content | ||||
|           entity_field: info | ||||
|           plugin_id: string | ||||
|           operator: contains | ||||
|           value: '' | ||||
|           group: 1 | ||||
|           exposed: true | ||||
|           expose: | ||||
|             operator_id: info_op | ||||
|             label: 'Block description' | ||||
|             description: '' | ||||
|             use_operator: false | ||||
|             operator: info_op | ||||
|             operator_limit_selection: false | ||||
|             operator_list: {  } | ||||
|             identifier: info | ||||
|             required: false | ||||
|             remember: false | ||||
|             multiple: false | ||||
|             remember_roles: | ||||
|               authenticated: authenticated | ||||
|               anonymous: '0' | ||||
|               administrator: '0' | ||||
|           is_grouped: false | ||||
|           group_info: | ||||
|             label: '' | ||||
|             description: '' | ||||
|             identifier: '' | ||||
|             optional: true | ||||
|             widget: select | ||||
|             multiple: false | ||||
|             remember: false | ||||
|             default_group: All | ||||
|             default_group_multiple: {  } | ||||
|             group_items: {  } | ||||
|         type: | ||||
|           id: type | ||||
|           table: block_content_field_data | ||||
|           field: type | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           entity_type: block_content | ||||
|           entity_field: type | ||||
|           plugin_id: bundle | ||||
|           operator: in | ||||
|           value: {  } | ||||
|           group: 1 | ||||
|           exposed: true | ||||
|           expose: | ||||
|             operator_id: type_op | ||||
|             label: 'Block type' | ||||
|             description: '' | ||||
|             use_operator: false | ||||
|             operator: type_op | ||||
|             operator_limit_selection: false | ||||
|             operator_list: {  } | ||||
|             identifier: type | ||||
|             required: false | ||||
|             remember: false | ||||
|             multiple: false | ||||
|             remember_roles: | ||||
|               authenticated: authenticated | ||||
|               anonymous: '0' | ||||
|               administrator: '0' | ||||
|             reduce: false | ||||
|           is_grouped: false | ||||
|           group_info: | ||||
|             label: '' | ||||
|             description: '' | ||||
|             identifier: '' | ||||
|             optional: true | ||||
|             widget: select | ||||
|             multiple: false | ||||
|             remember: false | ||||
|             default_group: All | ||||
|             default_group_multiple: {  } | ||||
|             group_items: {  } | ||||
|         reusable: | ||||
|           id: reusable | ||||
|           table: block_content_field_data | ||||
|           field: reusable | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           entity_type: block_content | ||||
|           entity_field: reusable | ||||
|           plugin_id: boolean | ||||
|           operator: '=' | ||||
|           value: '1' | ||||
|           group: 1 | ||||
|           exposed: false | ||||
|           expose: | ||||
|             operator_id: '' | ||||
|             label: '' | ||||
|             description: '' | ||||
|             use_operator: false | ||||
|             operator: '' | ||||
|             operator_limit_selection: false | ||||
|             operator_list: {  } | ||||
|             identifier: '' | ||||
|             required: false | ||||
|             remember: false | ||||
|             multiple: false | ||||
|             remember_roles: | ||||
|               authenticated: authenticated | ||||
|           is_grouped: false | ||||
|           group_info: | ||||
|             label: '' | ||||
|             description: '' | ||||
|             identifier: '' | ||||
|             optional: true | ||||
|             widget: select | ||||
|             multiple: false | ||||
|             remember: false | ||||
|             default_group: All | ||||
|             default_group_multiple: {  } | ||||
|             group_items: {  } | ||||
|       style: | ||||
|         type: table | ||||
|         options: | ||||
|           grouping: {  } | ||||
|           row_class: '' | ||||
|           default_row_class: true | ||||
|           columns: | ||||
|             info: info | ||||
|             type: type | ||||
|             changed: changed | ||||
|             operations: operations | ||||
|           default: changed | ||||
|           info: | ||||
|             info: | ||||
|               sortable: true | ||||
|               default_sort_order: asc | ||||
|               align: '' | ||||
|               separator: '' | ||||
|               empty_column: false | ||||
|               responsive: '' | ||||
|             type: | ||||
|               sortable: true | ||||
|               default_sort_order: asc | ||||
|               align: '' | ||||
|               separator: '' | ||||
|               empty_column: false | ||||
|               responsive: '' | ||||
|             changed: | ||||
|               sortable: true | ||||
|               default_sort_order: desc | ||||
|               align: '' | ||||
|               separator: '' | ||||
|               empty_column: false | ||||
|               responsive: '' | ||||
|             operations: | ||||
|               sortable: false | ||||
|               default_sort_order: asc | ||||
|               align: '' | ||||
|               separator: '' | ||||
|               empty_column: false | ||||
|               responsive: '' | ||||
|           override: true | ||||
|           sticky: false | ||||
|           summary: '' | ||||
|           empty_table: true | ||||
|           caption: '' | ||||
|           description: '' | ||||
|       row: | ||||
|         type: fields | ||||
|       query: | ||||
|         type: views_query | ||||
|         options: | ||||
|           query_comment: '' | ||||
|           disable_sql_rewrite: false | ||||
|           distinct: false | ||||
|           replica: false | ||||
|           query_tags: {  } | ||||
|       relationships: {  } | ||||
|       header: {  } | ||||
|       footer: {  } | ||||
|       display_extenders: {  } | ||||
|     cache_metadata: | ||||
|       max-age: -1 | ||||
|       contexts: | ||||
|         - 'languages:language_content' | ||||
|         - 'languages:language_interface' | ||||
|         - url | ||||
|         - url.query_args | ||||
|         - user.permissions | ||||
|       tags: {  } | ||||
|   page_1: | ||||
|     id: page_1 | ||||
|     display_title: Page | ||||
|     display_plugin: page | ||||
|     position: 1 | ||||
|     display_options: | ||||
|       display_extenders: {  } | ||||
|       path: admin/content/block | ||||
|       menu: | ||||
|         type: tab | ||||
|         title: 'Blocks' | ||||
|         description: 'Create and edit content blocks.' | ||||
|         weight: 0 | ||||
|         menu_name: admin | ||||
|         parent: system.admin_content | ||||
|         context: '0' | ||||
|     cache_metadata: | ||||
|       max-age: -1 | ||||
|       contexts: | ||||
|         - 'languages:language_content' | ||||
|         - 'languages:language_interface' | ||||
|         - url | ||||
|         - url.query_args | ||||
|         - user.permissions | ||||
|       tags: {  } | ||||
| @ -0,0 +1,7 @@ | ||||
| name: "Content Block module tests" | ||||
| type: module | ||||
| description: "Support module for content block related testing." | ||||
| package: Testing | ||||
| version: VERSION | ||||
| dependencies: | ||||
|   - drupal:block_content | ||||
							
								
								
									
										69
									
								
								tests/modules/block_content_test/block_content_test.module
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								tests/modules/block_content_test/block_content_test.module
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,69 @@ | ||||
| <?php | ||||
| 
 | ||||
| /** | ||||
|  * @file | ||||
|  * A dummy module for testing content block related hooks. | ||||
|  * | ||||
|  * This is a dummy module that implements content block related hooks to test API | ||||
|  * interaction with the block_content module. | ||||
|  */ | ||||
| 
 | ||||
| use Drupal\block_content\Entity\BlockContent; | ||||
| 
 | ||||
| /** | ||||
|  * Implements hook_block_content_view(). | ||||
|  */ | ||||
| function block_content_test_block_content_view(array &$build, BlockContent $block_content, $view_mode) { | ||||
|   // Add extra content. | ||||
|   $build['extra_content'] = [ | ||||
|     '#markup' => '<blink>Wow</blink>', | ||||
|   ]; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Implements hook_block_content_presave(). | ||||
|  */ | ||||
| function block_content_test_block_content_presave(BlockContent $block_content) { | ||||
|   if ($block_content->label() == 'testing_block_content_presave') { | ||||
|     $block_content->setInfo($block_content->label() . '_presave'); | ||||
|   } | ||||
|   // Determine changes. | ||||
|   if (!empty($block_content->original) && $block_content->original->label() == 'test_changes') { | ||||
|     if ($block_content->original->label() != $block_content->label()) { | ||||
|       $block_content->setInfo($block_content->label() . '_presave'); | ||||
|       // Drupal 1.0 release. | ||||
|       $block_content->changed = 979534800; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Implements hook_block_content_update(). | ||||
|  */ | ||||
| function block_content_test_block_content_update(BlockContent $block_content) { | ||||
|   // Determine changes on update. | ||||
|   if (!empty($block_content->original) && $block_content->original->label() == 'test_changes') { | ||||
|     if ($block_content->original->label() != $block_content->label()) { | ||||
|       $block_content->setInfo($block_content->label() . '_update'); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Implements hook_block_content_insert(). | ||||
|  * | ||||
|  * This tests saving a block_content on block_content insert. | ||||
|  * | ||||
|  * @see \Drupal\block_content\Tests\BlockContentSaveTest::testBlockContentSaveOnInsert() | ||||
|  */ | ||||
| function block_content_test_block_content_insert(BlockContent $block_content) { | ||||
|   // Set the block_content title to the block_content ID and save. | ||||
|   if ($block_content->label() == 'new') { | ||||
|     $block_content->setInfo('BlockContent ' . $block_content->id()); | ||||
|     $block_content->setNewRevision(FALSE); | ||||
|     $block_content->save(); | ||||
|   } | ||||
|   if ($block_content->label() == 'fail_creation') { | ||||
|     throw new Exception('Test exception for rollback.'); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,6 @@ | ||||
| block_content_test.block_content_view: | ||||
|   path: '/block-content/{block_content}' | ||||
|   defaults: | ||||
|     _entity_view: 'block_content' | ||||
|   requirements: | ||||
|     _entity_access: 'block_content.view' | ||||
| @ -0,0 +1,26 @@ | ||||
| langcode: en | ||||
| status: true | ||||
| dependencies: | ||||
|   module: | ||||
|     - block_content | ||||
|   theme: | ||||
|     - stark | ||||
| id: foobar_gorilla | ||||
| theme: stark | ||||
| region: content | ||||
| weight: null | ||||
| provider: null | ||||
| plugin: 'block_content:fb5e8434-3617-4a1d-a252-8273e95ec30e' | ||||
| settings: | ||||
|   id: 'block_content:fb5e8434-3617-4a1d-a252-8273e95ec30e' | ||||
|   label: 'Foobar Gorilla' | ||||
|   label_display: visible | ||||
|   provider: block_content | ||||
|   status: true | ||||
|   info: '' | ||||
|   view_mode: default | ||||
| visibility: | ||||
|   request_path: | ||||
|     id: request_path | ||||
|     negate: false | ||||
|     pages: '' | ||||
| @ -0,0 +1,80 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Drupal\block_content_test\Plugin\EntityReferenceSelection; | ||||
| 
 | ||||
| use Drupal\Core\Entity\Plugin\EntityReferenceSelection\DefaultSelection; | ||||
| 
 | ||||
| /** | ||||
|  * Test EntityReferenceSelection with conditions on the 'reusable' field. | ||||
|  */ | ||||
| class TestSelection extends DefaultSelection { | ||||
| 
 | ||||
|   /** | ||||
|    * The condition type. | ||||
|    * | ||||
|    * @var string | ||||
|    */ | ||||
|   protected $conditionType; | ||||
| 
 | ||||
|   /** | ||||
|    * Whether to set the condition for reusable or non-reusable blocks. | ||||
|    * | ||||
|    * @var bool | ||||
|    */ | ||||
|   protected $isReusable; | ||||
| 
 | ||||
|   /** | ||||
|    * Sets the test mode. | ||||
|    * | ||||
|    * @param string $condition_type | ||||
|    *   The condition type. | ||||
|    * @param bool $is_reusable | ||||
|    *   Whether to set the condition for reusable or non-reusable blocks. | ||||
|    */ | ||||
|   public function setTestMode($condition_type = NULL, $is_reusable = NULL) { | ||||
|     $this->conditionType = $condition_type; | ||||
|     $this->isReusable = $is_reusable; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') { | ||||
|     $query = parent::buildEntityQuery($match, $match_operator); | ||||
|     if ($this->conditionType) { | ||||
|       /** @var \Drupal\Core\Database\Query\ConditionInterface $add_condition */ | ||||
|       $add_condition = NULL; | ||||
|       switch ($this->conditionType) { | ||||
|         case 'base': | ||||
|           $add_condition = $query; | ||||
|           break; | ||||
| 
 | ||||
|         case 'group': | ||||
|           $group = $query->andConditionGroup() | ||||
|             ->exists('type'); | ||||
|           $add_condition = $group; | ||||
|           $query->condition($group); | ||||
|           break; | ||||
| 
 | ||||
|         case "nested_group": | ||||
|           $query->exists('type'); | ||||
|           $sub_group = $query->andConditionGroup() | ||||
|             ->exists('type'); | ||||
|           $add_condition = $sub_group; | ||||
|           $group = $query->andConditionGroup() | ||||
|             ->exists('type') | ||||
|             ->condition($sub_group); | ||||
|           $query->condition($group); | ||||
|           break; | ||||
|       } | ||||
|       if ($this->isReusable) { | ||||
|         $add_condition->condition('reusable', 1); | ||||
|       } | ||||
|       else { | ||||
|         $add_condition->condition('reusable', 0); | ||||
|       } | ||||
|     } | ||||
|     return $query; | ||||
|   } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,8 @@ | ||||
| name: 'Block Content test views' | ||||
| type: module | ||||
| description: 'Provides default views for views block_content tests.' | ||||
| package: Testing | ||||
| version: VERSION | ||||
| dependencies: | ||||
|   - drupal:block_content | ||||
|   - drupal:views | ||||
| @ -0,0 +1,233 @@ | ||||
| langcode: en | ||||
| status: true | ||||
| dependencies: | ||||
|   module: | ||||
|     - block_content | ||||
| id: test_block_content_redirect_destination | ||||
| label: 'Redirect destination' | ||||
| module: views | ||||
| description: '' | ||||
| tag: '' | ||||
| base_table: block_content_field_data | ||||
| base_field: id | ||||
| display: | ||||
|   default: | ||||
|     display_plugin: default | ||||
|     id: default | ||||
|     display_title: Default | ||||
|     position: 0 | ||||
|     display_options: | ||||
|       access: | ||||
|         type: none | ||||
|         options: {  } | ||||
|       cache: | ||||
|         type: tag | ||||
|         options: {  } | ||||
|       query: | ||||
|         type: views_query | ||||
|         options: | ||||
|           disable_sql_rewrite: false | ||||
|           distinct: false | ||||
|           replica: false | ||||
|           query_comment: '' | ||||
|           query_tags: {  } | ||||
|       exposed_form: | ||||
|         type: basic | ||||
|         options: | ||||
|           submit_button: Apply | ||||
|           reset_button: false | ||||
|           reset_button_label: Reset | ||||
|           exposed_sorts_label: 'Sort by' | ||||
|           expose_sort_order: true | ||||
|           sort_asc_label: Asc | ||||
|           sort_desc_label: Desc | ||||
|       pager: | ||||
|         type: mini | ||||
|         options: | ||||
|           items_per_page: 10 | ||||
|           offset: 0 | ||||
|           id: 0 | ||||
|           total_pages: null | ||||
|           expose: | ||||
|             items_per_page: false | ||||
|             items_per_page_label: 'Items per page' | ||||
|             items_per_page_options: '5, 10, 25, 50' | ||||
|             items_per_page_options_all: false | ||||
|             items_per_page_options_all_label: '- All -' | ||||
|             offset: false | ||||
|             offset_label: Offset | ||||
|           tags: | ||||
|             previous: ‹‹ | ||||
|             next: ›› | ||||
|       style: | ||||
|         type: table | ||||
|         options: | ||||
|           grouping: {  } | ||||
|           row_class: '' | ||||
|           default_row_class: true | ||||
|           override: true | ||||
|           sticky: false | ||||
|           caption: '' | ||||
|           summary: '' | ||||
|           description: '' | ||||
|           columns: | ||||
|             info: info | ||||
|           info: | ||||
|             info: | ||||
|               sortable: false | ||||
|               default_sort_order: asc | ||||
|               align: '' | ||||
|               separator: '' | ||||
|               empty_column: false | ||||
|               responsive: '' | ||||
|           default: '-1' | ||||
|           empty_table: false | ||||
|       row: | ||||
|         type: 'entity:block_content' | ||||
|       fields: | ||||
|         info: | ||||
|           table: block_content_field_data | ||||
|           field: info | ||||
|           id: info | ||||
|           entity_type: null | ||||
|           entity_field: info | ||||
|           plugin_id: field | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           label: '' | ||||
|           exclude: false | ||||
|           alter: | ||||
|             alter_text: false | ||||
|             text: '' | ||||
|             make_link: false | ||||
|             path: '' | ||||
|             absolute: false | ||||
|             external: false | ||||
|             replace_spaces: false | ||||
|             path_case: none | ||||
|             trim_whitespace: false | ||||
|             alt: '' | ||||
|             rel: '' | ||||
|             link_class: '' | ||||
|             prefix: '' | ||||
|             suffix: '' | ||||
|             target: '' | ||||
|             nl2br: false | ||||
|             max_length: 0 | ||||
|             word_boundary: true | ||||
|             ellipsis: true | ||||
|             more_link: false | ||||
|             more_link_text: '' | ||||
|             more_link_path: '' | ||||
|             strip_tags: false | ||||
|             trim: false | ||||
|             preserve_tags: '' | ||||
|             html: false | ||||
|           element_type: '' | ||||
|           element_class: '' | ||||
|           element_label_type: '' | ||||
|           element_label_class: '' | ||||
|           element_label_colon: true | ||||
|           element_wrapper_type: '' | ||||
|           element_wrapper_class: '' | ||||
|           element_default_classes: true | ||||
|           empty: '' | ||||
|           hide_empty: false | ||||
|           empty_zero: false | ||||
|           hide_alter_empty: true | ||||
|           click_sort_column: value | ||||
|           type: string | ||||
|           settings: {  } | ||||
|           group_column: value | ||||
|           group_columns: {  } | ||||
|           group_rows: true | ||||
|           delta_limit: 0 | ||||
|           delta_offset: 0 | ||||
|           delta_reversed: false | ||||
|           delta_first_last: false | ||||
|           multi_type: separator | ||||
|           separator: ', ' | ||||
|           field_api_classes: false | ||||
|         operations: | ||||
|           id: operations | ||||
|           table: block_content | ||||
|           field: operations | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           label: 'Operations links' | ||||
|           exclude: false | ||||
|           alter: | ||||
|             alter_text: false | ||||
|             text: '' | ||||
|             make_link: false | ||||
|             path: '' | ||||
|             absolute: false | ||||
|             external: false | ||||
|             replace_spaces: false | ||||
|             path_case: none | ||||
|             trim_whitespace: false | ||||
|             alt: '' | ||||
|             rel: '' | ||||
|             link_class: '' | ||||
|             prefix: '' | ||||
|             suffix: '' | ||||
|             target: '' | ||||
|             nl2br: false | ||||
|             max_length: 0 | ||||
|             word_boundary: true | ||||
|             ellipsis: true | ||||
|             more_link: false | ||||
|             more_link_text: '' | ||||
|             more_link_path: '' | ||||
|             strip_tags: false | ||||
|             trim: false | ||||
|             preserve_tags: '' | ||||
|             html: false | ||||
|           element_type: '' | ||||
|           element_class: '' | ||||
|           element_label_type: '' | ||||
|           element_label_class: '' | ||||
|           element_label_colon: true | ||||
|           element_wrapper_type: '' | ||||
|           element_wrapper_class: '' | ||||
|           element_default_classes: true | ||||
|           empty: '' | ||||
|           hide_empty: false | ||||
|           empty_zero: false | ||||
|           hide_alter_empty: true | ||||
|           destination: true | ||||
|           entity_type: block_content | ||||
|           plugin_id: entity_operations | ||||
|       filters: {  } | ||||
|       sorts: {  } | ||||
|       title: 'Redirect destination' | ||||
|       header: {  } | ||||
|       footer: {  } | ||||
|       empty: {  } | ||||
|       relationships: {  } | ||||
|       arguments: {  } | ||||
|       display_extenders: {  } | ||||
|     cache_metadata: | ||||
|       max-age: 0 | ||||
|       contexts: | ||||
|         - 'languages:language_content' | ||||
|         - 'languages:language_interface' | ||||
|         - url.query_args | ||||
|       tags: {  } | ||||
|   page_1: | ||||
|     display_plugin: page | ||||
|     id: page_1 | ||||
|     display_title: Page | ||||
|     position: 1 | ||||
|     display_options: | ||||
|       display_extenders: {  } | ||||
|       path: /admin/content/redirect_destination | ||||
|     cache_metadata: | ||||
|       max-age: 0 | ||||
|       contexts: | ||||
|         - 'languages:language_content' | ||||
|         - 'languages:language_interface' | ||||
|         - url.query_args | ||||
|       tags: {  } | ||||
| @ -0,0 +1,66 @@ | ||||
| langcode: en | ||||
| status: true | ||||
| dependencies: | ||||
|   module: | ||||
|     - block_content | ||||
| id: test_block_content_revision_id | ||||
| label: test_block_content_revision_id | ||||
| module: views | ||||
| description: '' | ||||
| tag: '' | ||||
| base_table: block_content_field_revision | ||||
| base_field: revision_id | ||||
| display: | ||||
|   default: | ||||
|     display_options: | ||||
|       relationships: | ||||
|         id: | ||||
|           id: id | ||||
|           table: block_content_field_revision | ||||
|           field: id | ||||
|           required: true | ||||
|           plugin_id: standard | ||||
|       fields: | ||||
|         revision_id: | ||||
|           id: revision_id | ||||
|           table: block_content_field_revision | ||||
|           field: revision_id | ||||
|           plugin_id: field | ||||
|           entity_type: block_content | ||||
|           entity_field: revision_id | ||||
|         id_1: | ||||
|           id: id_1 | ||||
|           table: block_content_field_revision | ||||
|           field: id | ||||
|           plugin_id: field | ||||
|           entity_type: block_content | ||||
|           entity_field: id | ||||
|         id: | ||||
|           id: id | ||||
|           table: block_content_field_data | ||||
|           field: id | ||||
|           relationship: id | ||||
|           plugin_id: field | ||||
|           entity_type: block_content | ||||
|           entity_field: id | ||||
|       arguments: | ||||
|         id: | ||||
|           id: id | ||||
|           table: block_content_field_revision | ||||
|           field: id | ||||
|           plugin_id: numeric | ||||
|           entity_type: block_content | ||||
|           entity_field: id | ||||
|       sorts: | ||||
|         revision_id: | ||||
|           id: revision_id | ||||
|           table: block_content_field_revision | ||||
|           field: revision_id | ||||
|           order: ASC | ||||
|           plugin_id: field | ||||
|           entity_type: block_content | ||||
|           entity_field: revision_id | ||||
|     display_plugin: default | ||||
|     display_title: Default | ||||
|     id: default | ||||
|     position: 0 | ||||
| @ -0,0 +1,69 @@ | ||||
| langcode: en | ||||
| status: true | ||||
| dependencies: | ||||
|   module: | ||||
|     - block_content | ||||
| id: test_block_content_revision_revision_id | ||||
| label: test_block_content_revision_revision_id | ||||
| module: views | ||||
| description: '' | ||||
| tag: '' | ||||
| base_table: block_content_field_revision | ||||
| base_field: revision_id | ||||
| display: | ||||
|   default: | ||||
|     display_options: | ||||
|       relationships: | ||||
|         revision_id: | ||||
|           id: revision_id | ||||
|           table: block_content_field_revision | ||||
|           field: revision_id | ||||
|           required: true | ||||
|           entity_type: block_content | ||||
|           entity_field: revision_id | ||||
|           plugin_id: standard | ||||
|       fields: | ||||
|         revision_id: | ||||
|           id: revision_id | ||||
|           table: block_content_field_revision | ||||
|           field: revision_id | ||||
|           plugin_id: field | ||||
|           entity_type: block_content | ||||
|           entity_field: revision_id | ||||
|         id_1: | ||||
|           id: id_1 | ||||
|           table: block_content_field_revision | ||||
|           field: id | ||||
|           plugin_id: field | ||||
|           entity_type: block_content | ||||
|           entity_field: id | ||||
|         id: | ||||
|           id: id | ||||
|           table: block_content_field_data | ||||
|           field: id | ||||
|           relationship: revision_id | ||||
|           plugin_id: field | ||||
|           entity_type: block_content | ||||
|           entity_field: id | ||||
|       arguments: | ||||
|         id: | ||||
|           id: id | ||||
|           table: block_content_field_revision | ||||
|           field: id | ||||
|           plugin_id: block_content_id | ||||
|           entity_type: block_content | ||||
|           entity_field: id | ||||
|       sorts: | ||||
|         revision_id: | ||||
|           id: revision_id | ||||
|           table: block_content_field_revision | ||||
|           field: revision_id | ||||
|           order: ASC | ||||
|           plugin_id: field | ||||
|           entity_type: block_content | ||||
|           entity_field: revision_id | ||||
|       display_extenders: {  } | ||||
|     display_plugin: default | ||||
|     display_title: Default | ||||
|     id: default | ||||
|     position: 0 | ||||
| @ -0,0 +1,324 @@ | ||||
| langcode: en | ||||
| status: true | ||||
| dependencies: | ||||
|   module: | ||||
|     - block_content | ||||
|     - user | ||||
| id: test_block_content_revision_user | ||||
| label: 'Test block content revision user' | ||||
| module: views | ||||
| description: '' | ||||
| tag: '' | ||||
| base_table: block_content_field_data | ||||
| base_field: id | ||||
| display: | ||||
|   default: | ||||
|     display_plugin: default | ||||
|     id: default | ||||
|     display_title: Default | ||||
|     position: 0 | ||||
|     display_options: | ||||
|       access: | ||||
|         type: perm | ||||
|         options: | ||||
|           perm: 'access content' | ||||
|       cache: | ||||
|         type: tag | ||||
|         options: {  } | ||||
|       query: | ||||
|         type: views_query | ||||
|         options: | ||||
|           disable_sql_rewrite: false | ||||
|           distinct: false | ||||
|           replica: false | ||||
|           query_comment: '' | ||||
|           query_tags: {  } | ||||
|       exposed_form: | ||||
|         type: basic | ||||
|         options: | ||||
|           submit_button: Apply | ||||
|           reset_button: false | ||||
|           reset_button_label: Reset | ||||
|           exposed_sorts_label: 'Sort by' | ||||
|           expose_sort_order: true | ||||
|           sort_asc_label: Asc | ||||
|           sort_desc_label: Desc | ||||
|       pager: | ||||
|         type: none | ||||
|         options: | ||||
|           offset: 0 | ||||
|       style: | ||||
|         type: default | ||||
|         options: | ||||
|           grouping: {  } | ||||
|           row_class: '' | ||||
|           default_row_class: true | ||||
|           uses_fields: false | ||||
|       row: | ||||
|         type: fields | ||||
|         options: | ||||
|           inline: {  } | ||||
|           separator: '' | ||||
|           hide_empty: false | ||||
|           default_field_elements: true | ||||
|       fields: | ||||
|         id: | ||||
|           id: id | ||||
|           table: block_content_field_revision | ||||
|           field: id | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           label: '' | ||||
|           exclude: false | ||||
|           alter: | ||||
|             alter_text: false | ||||
|             text: '' | ||||
|             make_link: false | ||||
|             path: '' | ||||
|             absolute: false | ||||
|             external: false | ||||
|             replace_spaces: false | ||||
|             path_case: none | ||||
|             trim_whitespace: false | ||||
|             alt: '' | ||||
|             rel: '' | ||||
|             link_class: '' | ||||
|             prefix: '' | ||||
|             suffix: '' | ||||
|             target: '' | ||||
|             nl2br: false | ||||
|             max_length: 0 | ||||
|             word_boundary: true | ||||
|             ellipsis: true | ||||
|             more_link: false | ||||
|             more_link_text: '' | ||||
|             more_link_path: '' | ||||
|             strip_tags: false | ||||
|             trim: false | ||||
|             preserve_tags: '' | ||||
|             html: false | ||||
|           element_type: '' | ||||
|           element_class: '' | ||||
|           element_label_type: '' | ||||
|           element_label_class: '' | ||||
|           element_label_colon: false | ||||
|           element_wrapper_type: '' | ||||
|           element_wrapper_class: '' | ||||
|           element_default_classes: true | ||||
|           empty: '' | ||||
|           hide_empty: false | ||||
|           empty_zero: false | ||||
|           hide_alter_empty: true | ||||
|           click_sort_column: value | ||||
|           type: number_integer | ||||
|           settings: | ||||
|             thousand_separator: '' | ||||
|             prefix_suffix: false | ||||
|           group_column: value | ||||
|           group_columns: {  } | ||||
|           group_rows: true | ||||
|           delta_limit: 0 | ||||
|           delta_offset: 0 | ||||
|           delta_reversed: false | ||||
|           delta_first_last: false | ||||
|           multi_type: separator | ||||
|           separator: ', ' | ||||
|           field_api_classes: false | ||||
|           entity_type: block_content | ||||
|           entity_field: id | ||||
|           plugin_id: field | ||||
|         revision_id: | ||||
|           id: revision_id | ||||
|           table: block_content_field_revision | ||||
|           field: revision_id | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           label: '' | ||||
|           exclude: false | ||||
|           alter: | ||||
|             alter_text: false | ||||
|             text: '' | ||||
|             make_link: false | ||||
|             path: '' | ||||
|             absolute: false | ||||
|             external: false | ||||
|             replace_spaces: false | ||||
|             path_case: none | ||||
|             trim_whitespace: false | ||||
|             alt: '' | ||||
|             rel: '' | ||||
|             link_class: '' | ||||
|             prefix: '' | ||||
|             suffix: '' | ||||
|             target: '' | ||||
|             nl2br: false | ||||
|             max_length: 0 | ||||
|             word_boundary: true | ||||
|             ellipsis: true | ||||
|             more_link: false | ||||
|             more_link_text: '' | ||||
|             more_link_path: '' | ||||
|             strip_tags: false | ||||
|             trim: false | ||||
|             preserve_tags: '' | ||||
|             html: false | ||||
|           element_type: '' | ||||
|           element_class: '' | ||||
|           element_label_type: '' | ||||
|           element_label_class: '' | ||||
|           element_label_colon: false | ||||
|           element_wrapper_type: '' | ||||
|           element_wrapper_class: '' | ||||
|           element_default_classes: true | ||||
|           empty: '' | ||||
|           hide_empty: false | ||||
|           empty_zero: false | ||||
|           hide_alter_empty: true | ||||
|           click_sort_column: value | ||||
|           type: number_integer | ||||
|           settings: | ||||
|             thousand_separator: '' | ||||
|             prefix_suffix: false | ||||
|           group_column: value | ||||
|           group_columns: {  } | ||||
|           group_rows: true | ||||
|           delta_limit: 0 | ||||
|           delta_offset: 0 | ||||
|           delta_reversed: false | ||||
|           delta_first_last: false | ||||
|           multi_type: separator | ||||
|           separator: ', ' | ||||
|           field_api_classes: false | ||||
|           entity_type: block_content | ||||
|           entity_field: revision_id | ||||
|           plugin_id: field | ||||
|         revision_user: | ||||
|           id: revision_user | ||||
|           table: block_content_revision | ||||
|           field: revision_user | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           label: '' | ||||
|           exclude: false | ||||
|           alter: | ||||
|             alter_text: false | ||||
|             text: '' | ||||
|             make_link: false | ||||
|             path: '' | ||||
|             absolute: false | ||||
|             external: false | ||||
|             replace_spaces: false | ||||
|             path_case: none | ||||
|             trim_whitespace: false | ||||
|             alt: '' | ||||
|             rel: '' | ||||
|             link_class: '' | ||||
|             prefix: '' | ||||
|             suffix: '' | ||||
|             target: '' | ||||
|             nl2br: false | ||||
|             max_length: 0 | ||||
|             word_boundary: true | ||||
|             ellipsis: true | ||||
|             more_link: false | ||||
|             more_link_text: '' | ||||
|             more_link_path: '' | ||||
|             strip_tags: false | ||||
|             trim: false | ||||
|             preserve_tags: '' | ||||
|             html: false | ||||
|           element_type: '' | ||||
|           element_class: '' | ||||
|           element_label_type: '' | ||||
|           element_label_class: '' | ||||
|           element_label_colon: false | ||||
|           element_wrapper_type: '' | ||||
|           element_wrapper_class: '' | ||||
|           element_default_classes: true | ||||
|           empty: '' | ||||
|           hide_empty: false | ||||
|           empty_zero: false | ||||
|           hide_alter_empty: true | ||||
|           click_sort_column: target_id | ||||
|           type: entity_reference_label | ||||
|           settings: | ||||
|             link: false | ||||
|           group_column: target_id | ||||
|           group_columns: {  } | ||||
|           group_rows: true | ||||
|           delta_limit: 0 | ||||
|           delta_offset: 0 | ||||
|           delta_reversed: false | ||||
|           delta_first_last: false | ||||
|           multi_type: separator | ||||
|           separator: ', ' | ||||
|           field_api_classes: false | ||||
|           entity_type: block_content | ||||
|           entity_field: revision_user | ||||
|           plugin_id: field | ||||
|       filters: | ||||
|         revision_user: | ||||
|           id: revision_user | ||||
|           table: block_content_revision | ||||
|           field: revision_user | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           operator: in | ||||
|           value: {  } | ||||
|           group: 1 | ||||
|           exposed: true | ||||
|           expose: | ||||
|             operator_id: revision_user_op | ||||
|             label: 'Revision user' | ||||
|             description: '' | ||||
|             use_operator: false | ||||
|             operator: revision_user_op | ||||
|             operator_limit_selection: false | ||||
|             operator_list: {  } | ||||
|             identifier: revision_user | ||||
|             required: false | ||||
|             remember: false | ||||
|             multiple: false | ||||
|             remember_roles: | ||||
|               authenticated: authenticated | ||||
|               anonymous: '0' | ||||
|               administrator: '0' | ||||
|             reduce: false | ||||
|           is_grouped: false | ||||
|           group_info: | ||||
|             label: '' | ||||
|             description: '' | ||||
|             identifier: '' | ||||
|             optional: true | ||||
|             widget: select | ||||
|             multiple: false | ||||
|             remember: false | ||||
|             default_group: All | ||||
|             default_group_multiple: {  } | ||||
|             group_items: {  } | ||||
|           entity_type: block_content | ||||
|           entity_field: revision_user | ||||
|           plugin_id: user_name | ||||
|       sorts: {  } | ||||
|       header: {  } | ||||
|       footer: {  } | ||||
|       empty: {  } | ||||
|       relationships: {  } | ||||
|       arguments: {  } | ||||
|       display_extenders: {  } | ||||
|       filter_groups: | ||||
|         operator: AND | ||||
|         groups: {  } | ||||
|     cache_metadata: | ||||
|       max-age: -1 | ||||
|       contexts: | ||||
|         - 'languages:language_content' | ||||
|         - 'languages:language_interface' | ||||
|         - url | ||||
|         - 'user.block_content_grants:view' | ||||
|         - user.permissions | ||||
|       tags: {  } | ||||
| @ -0,0 +1,190 @@ | ||||
| langcode: en | ||||
| status: true | ||||
| dependencies: | ||||
|   module: | ||||
|     - block_content | ||||
| id: test_block_content_view | ||||
| label: test_block_content_view | ||||
| module: views | ||||
| description: '' | ||||
| tag: '' | ||||
| base_table: block_content_field_data | ||||
| base_field: id | ||||
| display: | ||||
|   default: | ||||
|     display_plugin: default | ||||
|     id: default | ||||
|     display_title: Default | ||||
|     position: null | ||||
|     display_options: | ||||
|       access: | ||||
|         type: none | ||||
|         options: {  } | ||||
|       cache: | ||||
|         type: tag | ||||
|         options: {  } | ||||
|       query: | ||||
|         type: views_query | ||||
|         options: | ||||
|           disable_sql_rewrite: false | ||||
|           distinct: false | ||||
|           replica: false | ||||
|           query_comment: '' | ||||
|           query_tags: {  } | ||||
|       exposed_form: | ||||
|         type: basic | ||||
|         options: | ||||
|           submit_button: Apply | ||||
|           reset_button: false | ||||
|           reset_button_label: Reset | ||||
|           exposed_sorts_label: 'Sort by' | ||||
|           expose_sort_order: true | ||||
|           sort_asc_label: Asc | ||||
|           sort_desc_label: Desc | ||||
|       pager: | ||||
|         type: full | ||||
|         options: | ||||
|           items_per_page: 10 | ||||
|           offset: 0 | ||||
|           id: 0 | ||||
|           total_pages: null | ||||
|           expose: | ||||
|             items_per_page: false | ||||
|             items_per_page_label: 'Items per page' | ||||
|             items_per_page_options: '5, 10, 25, 50' | ||||
|             items_per_page_options_all: false | ||||
|             items_per_page_options_all_label: '- All -' | ||||
|             offset: false | ||||
|             offset_label: Offset | ||||
|           tags: | ||||
|             previous: '‹ Previous' | ||||
|             next: 'Next ›' | ||||
|             first: '« First' | ||||
|             last: 'Last »' | ||||
|           quantity: 9 | ||||
|       style: | ||||
|         type: default | ||||
|       row: | ||||
|         type: fields | ||||
|       fields: | ||||
|         id: | ||||
|           id: id | ||||
|           table: block_content_field_data | ||||
|           field: id | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           label: Id | ||||
|           exclude: false | ||||
|           alter: | ||||
|             alter_text: false | ||||
|             text: '' | ||||
|             make_link: false | ||||
|             path: '' | ||||
|             absolute: false | ||||
|             external: false | ||||
|             replace_spaces: false | ||||
|             path_case: none | ||||
|             trim_whitespace: false | ||||
|             alt: '' | ||||
|             rel: '' | ||||
|             link_class: '' | ||||
|             prefix: '' | ||||
|             suffix: '' | ||||
|             target: '' | ||||
|             nl2br: false | ||||
|             max_length: 0 | ||||
|             word_boundary: true | ||||
|             ellipsis: true | ||||
|             more_link: false | ||||
|             more_link_text: '' | ||||
|             more_link_path: '' | ||||
|             strip_tags: false | ||||
|             trim: false | ||||
|             preserve_tags: '' | ||||
|             html: false | ||||
|           element_type: '' | ||||
|           element_class: '' | ||||
|           element_label_type: '' | ||||
|           element_label_class: '' | ||||
|           element_label_colon: true | ||||
|           element_wrapper_type: '' | ||||
|           element_wrapper_class: '' | ||||
|           element_default_classes: true | ||||
|           empty: '' | ||||
|           hide_empty: false | ||||
|           empty_zero: false | ||||
|           hide_alter_empty: true | ||||
|           plugin_id: field | ||||
|           entity_type: block_content | ||||
|           entity_field: id | ||||
|       sorts: | ||||
|         id: | ||||
|           id: id | ||||
|           table: block_content_field_data | ||||
|           field: id | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           order: ASC | ||||
|           exposed: false | ||||
|           expose: | ||||
|             label: '' | ||||
|           entity_type: block_content | ||||
|           entity_field: id | ||||
|           plugin_id: standard | ||||
|       title: test_block_content_view | ||||
|       header: {  } | ||||
|       footer: {  } | ||||
|       empty: {  } | ||||
|       relationships: {  } | ||||
|       display_extenders: {  } | ||||
|       arguments: | ||||
|         type: | ||||
|           id: type | ||||
|           table: block_content_field_data | ||||
|           field: type | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           default_action: 'not found' | ||||
|           exception: | ||||
|             value: all | ||||
|             title_enable: false | ||||
|             title: All | ||||
|           title_enable: false | ||||
|           title: '' | ||||
|           default_argument_type: fixed | ||||
|           default_argument_options: | ||||
|             argument: '' | ||||
|           summary_options: | ||||
|             base_path: '' | ||||
|             count: true | ||||
|             items_per_page: 25 | ||||
|             override: false | ||||
|           summary: | ||||
|             sort_order: asc | ||||
|             number_of_records: 0 | ||||
|             format: default_summary | ||||
|           specify_validation: false | ||||
|           validate: | ||||
|             type: none | ||||
|             fail: 'not found' | ||||
|           validate_options: {  } | ||||
|           glossary: false | ||||
|           limit: 0 | ||||
|           case: none | ||||
|           path_case: none | ||||
|           transform_dash: false | ||||
|           break_phrase: false | ||||
|           entity_type: block_content | ||||
|           entity_field: type | ||||
|           plugin_id: string | ||||
|   page_1: | ||||
|     display_plugin: page | ||||
|     id: page_1 | ||||
|     display_title: Page | ||||
|     position: null | ||||
|     display_options: | ||||
|       path: test-block_content-view | ||||
|       display_extenders: {  } | ||||
| @ -0,0 +1,338 @@ | ||||
| langcode: en | ||||
| status: true | ||||
| dependencies: | ||||
|   module: | ||||
|     - block_content | ||||
| id: test_field_filters | ||||
| label: 'Test field filters' | ||||
| module: views | ||||
| description: '' | ||||
| tag: '' | ||||
| base_table: block_content_field_data | ||||
| base_field: id | ||||
| display: | ||||
|   default: | ||||
|     display_plugin: default | ||||
|     id: default | ||||
|     display_title: Default | ||||
|     position: 0 | ||||
|     display_options: | ||||
|       access: | ||||
|         type: none | ||||
|         options: {  } | ||||
|       cache: | ||||
|         type: tag | ||||
|         options: {  } | ||||
|       query: | ||||
|         type: views_query | ||||
|         options: | ||||
|           disable_sql_rewrite: false | ||||
|           distinct: false | ||||
|           replica: false | ||||
|           query_comment: '' | ||||
|           query_tags: {  } | ||||
|       exposed_form: | ||||
|         type: basic | ||||
|         options: | ||||
|           submit_button: Apply | ||||
|           reset_button: false | ||||
|           reset_button_label: Reset | ||||
|           exposed_sorts_label: 'Sort by' | ||||
|           expose_sort_order: true | ||||
|           sort_asc_label: Asc | ||||
|           sort_desc_label: Desc | ||||
|       pager: | ||||
|         type: none | ||||
|         options: | ||||
|           items_per_page: 0 | ||||
|           offset: 0 | ||||
|       style: | ||||
|         type: default | ||||
|       row: | ||||
|         type: 'entity:block_content' | ||||
|         options: | ||||
|           relationship: none | ||||
|           view_mode: default | ||||
|       fields: | ||||
|         info: | ||||
|           id: info | ||||
|           table: block_content_field_data | ||||
|           field: info | ||||
|           label: '' | ||||
|           alter: | ||||
|             alter_text: false | ||||
|             make_link: false | ||||
|             absolute: false | ||||
|             trim: false | ||||
|             word_boundary: false | ||||
|             ellipsis: false | ||||
|             strip_tags: false | ||||
|             html: false | ||||
|           hide_empty: false | ||||
|           empty_zero: false | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           exclude: false | ||||
|           element_type: '' | ||||
|           element_class: '' | ||||
|           element_label_type: '' | ||||
|           element_label_class: '' | ||||
|           element_label_colon: true | ||||
|           element_wrapper_type: '' | ||||
|           element_wrapper_class: '' | ||||
|           element_default_classes: true | ||||
|           empty: '' | ||||
|           hide_alter_empty: true | ||||
|           entity_type: block_content | ||||
|           type: string | ||||
|           settings: | ||||
|             link_to_entity: true | ||||
|           entity_field: title | ||||
|           plugin_id: field | ||||
|       filters: | ||||
|         info: | ||||
|           id: info | ||||
|           table: block_content_field_data | ||||
|           field: info | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           operator: contains | ||||
|           value: Paris | ||||
|           group: 1 | ||||
|           exposed: false | ||||
|           expose: | ||||
|             operator_id: '' | ||||
|             label: '' | ||||
|             description: '' | ||||
|             use_operator: false | ||||
|             operator: '' | ||||
|             identifier: '' | ||||
|             required: false | ||||
|             remember: false | ||||
|             multiple: false | ||||
|             remember_roles: | ||||
|               authenticated: authenticated | ||||
|           is_grouped: false | ||||
|           group_info: | ||||
|             label: '' | ||||
|             description: '' | ||||
|             identifier: '' | ||||
|             optional: true | ||||
|             widget: select | ||||
|             multiple: false | ||||
|             remember: false | ||||
|             default_group: All | ||||
|             default_group_multiple: {  } | ||||
|             group_items: {  } | ||||
|           plugin_id: string | ||||
|           entity_type: block_content | ||||
|           entity_field: info | ||||
|       sorts: | ||||
|         changed: | ||||
|           id: changed | ||||
|           table: block_content_field_data | ||||
|           field: changed | ||||
|           order: DESC | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           exposed: false | ||||
|           expose: | ||||
|             label: '' | ||||
|           granularity: second | ||||
|           plugin_id: date | ||||
|           entity_type: block_content | ||||
|           entity_field: changed | ||||
|       title: 'Test field filters' | ||||
|       header: {  } | ||||
|       footer: {  } | ||||
|       empty: {  } | ||||
|       relationships: {  } | ||||
|       arguments: {  } | ||||
|       rendering_language: '***LANGUAGE_entity_translation***' | ||||
|       display_extenders: {  } | ||||
|   page_bf: | ||||
|     display_plugin: page | ||||
|     id: page_bf | ||||
|     display_title: 'Body filter page' | ||||
|     position: 1 | ||||
|     display_options: | ||||
|       path: test-body-filter | ||||
|       display_description: '' | ||||
|       title: 'Test body filters' | ||||
|       defaults: | ||||
|         title: false | ||||
|         filters: false | ||||
|         filter_groups: false | ||||
|       filters: | ||||
|         body_value: | ||||
|           id: body_value | ||||
|           table: block_content__body | ||||
|           field: body_value | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           operator: contains | ||||
|           value: Comida | ||||
|           group: 1 | ||||
|           exposed: false | ||||
|           expose: | ||||
|             operator_id: '' | ||||
|             label: '' | ||||
|             description: '' | ||||
|             use_operator: false | ||||
|             operator: '' | ||||
|             identifier: '' | ||||
|             required: false | ||||
|             remember: false | ||||
|             multiple: false | ||||
|             remember_roles: | ||||
|               authenticated: authenticated | ||||
|           is_grouped: false | ||||
|           group_info: | ||||
|             label: '' | ||||
|             description: '' | ||||
|             identifier: '' | ||||
|             optional: true | ||||
|             widget: select | ||||
|             multiple: false | ||||
|             remember: false | ||||
|             default_group: All | ||||
|             default_group_multiple: {  } | ||||
|             group_items: {  } | ||||
|           plugin_id: string | ||||
|           entity_type: block_content | ||||
|           entity_field: body | ||||
|       filter_groups: | ||||
|         operator: AND | ||||
|         groups: | ||||
|           1: AND | ||||
|       display_extenders: {  } | ||||
|   page_bfp: | ||||
|     display_plugin: page | ||||
|     id: page_bfp | ||||
|     display_title: 'Body filter page Paris' | ||||
|     position: 1 | ||||
|     display_options: | ||||
|       path: test-body-paris | ||||
|       display_description: '' | ||||
|       title: 'Test body filters' | ||||
|       defaults: | ||||
|         title: false | ||||
|         filters: false | ||||
|         filter_groups: false | ||||
|       filters: | ||||
|         body_value: | ||||
|           id: body_value | ||||
|           table: block_content__body | ||||
|           field: body_value | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           operator: contains | ||||
|           value: Paris | ||||
|           group: 1 | ||||
|           exposed: false | ||||
|           expose: | ||||
|             operator_id: '' | ||||
|             label: '' | ||||
|             description: '' | ||||
|             use_operator: false | ||||
|             operator: '' | ||||
|             identifier: '' | ||||
|             required: false | ||||
|             remember: false | ||||
|             multiple: false | ||||
|             remember_roles: | ||||
|               authenticated: authenticated | ||||
|           is_grouped: false | ||||
|           group_info: | ||||
|             label: '' | ||||
|             description: '' | ||||
|             identifier: '' | ||||
|             optional: true | ||||
|             widget: select | ||||
|             multiple: false | ||||
|             remember: false | ||||
|             default_group: All | ||||
|             default_group_multiple: {  } | ||||
|             group_items: {  } | ||||
|           plugin_id: string | ||||
|           entity_type: block_content | ||||
|           entity_field: body | ||||
|       filter_groups: | ||||
|         operator: AND | ||||
|         groups: | ||||
|           1: AND | ||||
|       display_extenders: {  } | ||||
|   page_if: | ||||
|     display_plugin: page | ||||
|     id: page_if | ||||
|     display_title: 'Info filter page' | ||||
|     position: 1 | ||||
|     display_options: | ||||
|       path: test-info-filter | ||||
|       display_description: '' | ||||
|       title: 'Test info filter' | ||||
|       defaults: | ||||
|         title: false | ||||
|         filters: false | ||||
|         filter_groups: false | ||||
|       filters: | ||||
|         info: | ||||
|           id: info | ||||
|           table: block_content_field_data | ||||
|           field: info | ||||
|           relationship: none | ||||
|           group_type: group | ||||
|           admin_label: '' | ||||
|           operator: contains | ||||
|           value: Comida | ||||
|           group: 1 | ||||
|           exposed: false | ||||
|           expose: | ||||
|             operator_id: '' | ||||
|             label: '' | ||||
|             description: '' | ||||
|             use_operator: false | ||||
|             operator: '' | ||||
|             identifier: '' | ||||
|             required: false | ||||
|             remember: false | ||||
|             multiple: false | ||||
|             remember_roles: | ||||
|               authenticated: authenticated | ||||
|           is_grouped: false | ||||
|           group_info: | ||||
|             label: '' | ||||
|             description: '' | ||||
|             identifier: '' | ||||
|             optional: true | ||||
|             widget: select | ||||
|             multiple: false | ||||
|             remember: false | ||||
|             default_group: All | ||||
|             default_group_multiple: {  } | ||||
|             group_items: {  } | ||||
|           plugin_id: string | ||||
|           entity_type: block_content | ||||
|           entity_field: info | ||||
|       filter_groups: | ||||
|         operator: AND | ||||
|         groups: | ||||
|           1: AND | ||||
|       display_extenders: {  } | ||||
|   page_ifp: | ||||
|     display_plugin: page | ||||
|     id: page_ifp | ||||
|     display_title: 'Info filter page Paris' | ||||
|     position: 1 | ||||
|     display_options: | ||||
|       path: test-info-paris | ||||
|       display_description: '' | ||||
|       title: 'Test info filter' | ||||
|       defaults: | ||||
|         title: false | ||||
|       display_extenders: {  } | ||||
| @ -0,0 +1,27 @@ | ||||
| langcode: en | ||||
| status: true | ||||
| dependencies: | ||||
|   module: | ||||
|     - block_content | ||||
| id: test_field_type | ||||
| label: test_field_type | ||||
| module: views | ||||
| description: '' | ||||
| tag: '' | ||||
| base_table: block_content_field_data | ||||
| base_field: id | ||||
| display: | ||||
|   default: | ||||
|     display_options: | ||||
|       fields: | ||||
|         type: | ||||
|           field: type | ||||
|           id: type | ||||
|           table: block_content_field_data | ||||
|           plugin_id: field | ||||
|           entity_type: block_content | ||||
|           entity_field: type | ||||
|     display_plugin: default | ||||
|     display_title: Default | ||||
|     id: default | ||||
|     position: 0 | ||||
							
								
								
									
										100
									
								
								tests/src/Functional/BlockContentCacheTagsTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								tests/src/Functional/BlockContentCacheTagsTest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,100 @@ | ||||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace Drupal\Tests\block_content\Functional; | ||||
| 
 | ||||
| use Drupal\block_content\Entity\BlockContent; | ||||
| use Drupal\block_content\Entity\BlockContentType; | ||||
| use Drupal\Core\Cache\Cache; | ||||
| use Drupal\Core\Cache\CacheableMetadata; | ||||
| use Drupal\Core\Entity\EntityInterface; | ||||
| use Drupal\Tests\system\Functional\Entity\EntityCacheTagsTestBase; | ||||
| 
 | ||||
| /** | ||||
|  * Tests the Content Block entity's cache tags. | ||||
|  * | ||||
|  * @group block_content | ||||
|  */ | ||||
| class BlockContentCacheTagsTest extends EntityCacheTagsTestBase { | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected static $modules = ['block_content']; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected $defaultTheme = 'stark'; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function createEntity() { | ||||
|     $block_content_type = BlockContentType::create([ | ||||
|       'id' => 'basic', | ||||
|       'label' => 'basic', | ||||
|       'revision' => FALSE, | ||||
|     ]); | ||||
|     $block_content_type->save(); | ||||
|     block_content_add_body_field($block_content_type->id()); | ||||
| 
 | ||||
|     // Create a "Llama" content block.
 | ||||
|     $block_content = BlockContent::create([ | ||||
|       'info' => 'Llama', | ||||
|       'type' => 'basic', | ||||
|       'body' => [ | ||||
|         'value' => 'The name "llama" was adopted by European settlers from native Peruvians.', | ||||
|         'format' => 'plain_text', | ||||
|       ], | ||||
|     ]); | ||||
|     $block_content->save(); | ||||
| 
 | ||||
|     return $block_content; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    * | ||||
|    * @see \Drupal\block_content\BlockContentAccessControlHandler::checkAccess() | ||||
|    */ | ||||
|   protected function getAccessCacheContextsForEntity(EntityInterface $entity) { | ||||
|     return []; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    * | ||||
|    * Each comment must have a comment body, which always has a text format. | ||||
|    */ | ||||
|   protected function getAdditionalCacheTagsForEntity(EntityInterface $entity) { | ||||
|     return ['config:filter.format.plain_text']; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Tests that the block is cached with the correct contexts and tags. | ||||
|    */ | ||||
|   public function testBlock(): void { | ||||
|     $block = $this->drupalPlaceBlock('block_content:' . $this->entity->uuid()); | ||||
|     $build = $this->container->get('entity_type.manager')->getViewBuilder('block')->view($block, 'block'); | ||||
| 
 | ||||
|     // Render the block.
 | ||||
|     $this->container->get('renderer')->renderRoot($build); | ||||
| 
 | ||||
|     // Expected keys, contexts, and tags for the block.
 | ||||
|     // @see \Drupal\block\BlockViewBuilder::viewMultiple()
 | ||||
|     $expected_block_cache_keys = ['entity_view', 'block', $block->id()]; | ||||
|     $expected_block_cache_tags = Cache::mergeTags(['block_view', 'rendered'], $block->getCacheTags()); | ||||
|     $expected_block_cache_tags = Cache::mergeTags($expected_block_cache_tags, $block->getPlugin()->getCacheTags()); | ||||
| 
 | ||||
|     // Expected contexts and tags for the BlockContent entity.
 | ||||
|     // @see \Drupal\Core\Entity\EntityViewBuilder::getBuildDefaults().
 | ||||
|     $expected_entity_cache_tags = Cache::mergeTags(['block_content_view'], $this->entity->getCacheTags()); | ||||
|     $expected_entity_cache_tags = Cache::mergeTags($expected_entity_cache_tags, $this->getAdditionalCacheTagsForEntity($this->entity)); | ||||
| 
 | ||||
|     // Verify that what was render cached matches the above expectations.
 | ||||
|     $this->verifyRenderCache($expected_block_cache_keys, Cache::mergeTags($expected_block_cache_tags, $expected_entity_cache_tags), CacheableMetadata::createFromRenderArray($build)); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										44
									
								
								tests/src/Functional/BlockContentContextualLinksTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								tests/src/Functional/BlockContentContextualLinksTest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,44 @@ | ||||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace Drupal\Tests\block_content\Functional; | ||||
| 
 | ||||
| /** | ||||
|  * Tests views contextual links on block content. | ||||
|  * | ||||
|  * @group block_content | ||||
|  */ | ||||
| class BlockContentContextualLinksTest extends BlockContentTestBase { | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected static $modules = [ | ||||
|     'contextual', | ||||
|   ]; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected $defaultTheme = 'stark'; | ||||
| 
 | ||||
|   /** | ||||
|    * Tests contextual links. | ||||
|    */ | ||||
|   public function testBlockContentContextualLinks(): void { | ||||
|     $block_content = $this->createBlockContent(); | ||||
| 
 | ||||
|     $block = $this->placeBlock('block_content:' . $block_content->uuid()); | ||||
| 
 | ||||
|     $user = $this->drupalCreateUser([ | ||||
|       'administer blocks', | ||||
|       'access contextual links', | ||||
|     ]); | ||||
|     $this->drupalLogin($user); | ||||
| 
 | ||||
|     $this->drupalGet('<front>'); | ||||
|     $this->assertSession()->elementAttributeContains('css', 'div[data-contextual-id]', 'data-contextual-id', 'block:block=' . $block->id() . ':langcode=en|block_content:block_content=' . $block_content->id() . ':'); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										336
									
								
								tests/src/Functional/BlockContentCreationTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										336
									
								
								tests/src/Functional/BlockContentCreationTest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,336 @@ | ||||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace Drupal\Tests\block_content\Functional; | ||||
| 
 | ||||
| use Drupal\block_content\BlockContentInterface; | ||||
| use Drupal\block_content\Entity\BlockContent; | ||||
| use Drupal\Core\Database\Database; | ||||
| 
 | ||||
| // cspell:ignore testblock
 | ||||
| 
 | ||||
| /** | ||||
|  * Create a block and test saving it. | ||||
|  * | ||||
|  * @group block_content | ||||
|  */ | ||||
| class BlockContentCreationTest extends BlockContentTestBase { | ||||
| 
 | ||||
|   /** | ||||
|    * Modules to install. | ||||
|    * | ||||
|    * Enable dummy module that implements hook_block_insert() for exceptions and | ||||
|    * field_ui to edit display settings. | ||||
|    * | ||||
|    * @var array | ||||
|    */ | ||||
|   protected static $modules = ['block_content_test', 'dblog', 'field_ui']; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected $defaultTheme = 'stark'; | ||||
| 
 | ||||
|   /** | ||||
|    * Permissions to grant admin user. | ||||
|    * | ||||
|    * @var array | ||||
|    */ | ||||
|   protected $permissions = [ | ||||
|     'administer blocks', | ||||
|     'administer block_content display', | ||||
|     'access block library', | ||||
|     'administer block content', | ||||
|   ]; | ||||
| 
 | ||||
|   /** | ||||
|    * Sets the test up. | ||||
|    */ | ||||
|   protected function setUp(): void { | ||||
|     parent::setUp(); | ||||
|     $this->drupalLogin($this->adminUser); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Creates a "Basic block" block and verifies its consistency in the database. | ||||
|    */ | ||||
|   public function testBlockContentCreation(): void { | ||||
|     $this->drupalLogin($this->adminUser); | ||||
| 
 | ||||
|     // Create a block.
 | ||||
|     $edit = []; | ||||
|     $edit['info[0][value]'] = 'Test Block'; | ||||
|     $edit['body[0][value]'] = $this->randomMachineName(16); | ||||
|     $this->drupalGet('block/add/basic'); | ||||
|     $this->submitForm($edit, 'Save'); | ||||
| 
 | ||||
|     // Check that the Basic block has been created.
 | ||||
|     $this->assertSession()->pageTextContains('basic ' . $edit['info[0][value]'] . ' has been created.'); | ||||
| 
 | ||||
|     // Check that the view mode setting is hidden because only one exists.
 | ||||
|     $this->assertSession()->fieldNotExists('settings[view_mode]'); | ||||
| 
 | ||||
|     // Check that the block exists in the database.
 | ||||
|     $block = $this->getBlockByLabel($edit['info[0][value]']); | ||||
|     $this->assertNotEmpty($block, 'Content Block found in database.'); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Creates a "Basic page" block with multiple view modes. | ||||
|    */ | ||||
|   public function testBlockContentCreationMultipleViewModes(): void { | ||||
|     // Add a new view mode and verify if it is selected as expected.
 | ||||
|     $this->drupalLogin($this->drupalCreateUser(['administer display modes'])); | ||||
|     $this->drupalGet('admin/structure/display-modes/view/add/block_content'); | ||||
|     $edit = [ | ||||
|       'id' => 'test_view_mode', | ||||
|       'label' => 'Test View Mode', | ||||
|     ]; | ||||
|     $this->submitForm($edit, 'Save'); | ||||
|     $this->assertSession()->pageTextContains('Saved the ' . $edit['label'] . ' view mode.'); | ||||
| 
 | ||||
|     $this->drupalLogin($this->adminUser); | ||||
| 
 | ||||
|     // Create a block.
 | ||||
|     $edit = []; | ||||
|     $edit['info[0][value]'] = 'Test Block'; | ||||
|     $edit['body[0][value]'] = $this->randomMachineName(16); | ||||
|     $this->drupalGet('block/add/basic'); | ||||
|     $this->submitForm($edit, 'Save and configure'); | ||||
| 
 | ||||
|     // Save our block permanently
 | ||||
|     $this->submitForm(['region' => 'content'], 'Save block'); | ||||
| 
 | ||||
|     // Set test_view_mode as a custom display to be available on the list.
 | ||||
|     $this->drupalGet('admin/structure/block-content/manage/basic/display'); | ||||
|     $custom_view_mode = [ | ||||
|       'display_modes_custom[test_view_mode]' => 1, | ||||
|     ]; | ||||
|     $this->submitForm($custom_view_mode, 'Save'); | ||||
| 
 | ||||
|     // Go to the configure page and change the view mode.
 | ||||
|     $this->drupalGet('admin/structure/block/manage/stark_testblock'); | ||||
| 
 | ||||
|     // Test the available view mode options.
 | ||||
|     // Verify that the default view mode is available.
 | ||||
|     $this->assertSession()->optionExists('edit-settings-view-mode', 'default'); | ||||
|     // Verify that the test view mode is available.
 | ||||
|     $this->assertSession()->optionExists('edit-settings-view-mode', 'test_view_mode'); | ||||
| 
 | ||||
|     $view_mode['settings[view_mode]'] = 'test_view_mode'; | ||||
|     $this->submitForm($view_mode, 'Save block'); | ||||
| 
 | ||||
|     // Check that the view mode setting is shown because more than one exists.
 | ||||
|     $this->drupalGet('admin/structure/block/manage/stark_testblock'); | ||||
|     $this->assertSession()->fieldExists('settings[view_mode]'); | ||||
| 
 | ||||
|     // Change the view mode.
 | ||||
|     $view_mode['region'] = 'content'; | ||||
|     $view_mode['settings[view_mode]'] = 'test_view_mode'; | ||||
|     $this->submitForm($view_mode, 'Save block'); | ||||
| 
 | ||||
|     // Go to the configure page and verify the view mode has changed.
 | ||||
|     $this->drupalGet('admin/structure/block/manage/stark_testblock'); | ||||
|     $this->assertSession()->fieldValueEquals('settings[view_mode]', 'test_view_mode'); | ||||
| 
 | ||||
|     // Check that the block exists in the database.
 | ||||
|     $block = $this->getBlockByLabel($edit['info[0][value]']); | ||||
|     $this->assertNotEmpty($block, 'Content Block found in database.'); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Tests the redirect workflow of creating a block_content and block. | ||||
|    */ | ||||
|   public function testBlockContentFormSubmitHandlers(): void { | ||||
|     $this->drupalLogin($this->adminUser); | ||||
| 
 | ||||
|     // Create a block and place in block layout.
 | ||||
|     $this->drupalGet('/admin/content/block'); | ||||
|     $this->clickLink('Add content block'); | ||||
|     // Verify destination URL, when clicking "Save and configure" this
 | ||||
|     // destination will be ignored.
 | ||||
|     $base = base_path(); | ||||
|     $url = 'block/add?destination=' . $base . 'admin/content/block'; | ||||
|     $this->assertSession()->addressEquals($url); | ||||
|     $edit = []; | ||||
|     $edit['info[0][value]'] = 'Test Block'; | ||||
|     $edit['body[0][value]'] = $this->randomMachineName(16); | ||||
|     $this->submitForm($edit, 'Save and configure'); | ||||
|     $this->assertSession()->pageTextContains('basic ' . $edit['info[0][value]'] . ' has been created.'); | ||||
|     $this->assertSession()->pageTextContains('Configure block'); | ||||
| 
 | ||||
|     // Verify when editing a block "Save and configure" does not appear.
 | ||||
|     $this->drupalGet('/admin/content/block/1'); | ||||
|     $this->assertSession()->buttonNotExists('Save and configure'); | ||||
| 
 | ||||
|     // Create a block but go back to block library.
 | ||||
|     $edit = []; | ||||
|     $edit['info[0][value]'] = 'Test Block'; | ||||
|     $edit['body[0][value]'] = $this->randomMachineName(16); | ||||
|     $this->drupalGet('block/add/basic'); | ||||
|     $this->submitForm($edit, 'Save'); | ||||
|     // Check that the Basic block has been created.
 | ||||
|     $this->assertSession()->pageTextContains('basic ' . $edit['info[0][value]'] . ' has been created.'); | ||||
|     $this->assertSession()->addressEquals('/admin/content/block'); | ||||
| 
 | ||||
|     // Check that the user is redirected to the block library on edit.
 | ||||
|     $block = $this->getBlockByLabel($edit['info[0][value]']); | ||||
|     $this->drupalGet($block->toUrl('edit-form')); | ||||
|     $this->submitForm([ | ||||
|       'info[0][value]' => 'Test Block Updated', | ||||
|     ], 'Save'); | ||||
|     $this->assertSession()->addressEquals('admin/content/block'); | ||||
| 
 | ||||
|     // Test with user who doesn't have permission to place a block.
 | ||||
|     $this->drupalLogin($this->drupalCreateUser(['administer block content'])); | ||||
|     $this->drupalGet('block/add/basic'); | ||||
|     $this->assertSession()->buttonNotExists('Save and configure'); | ||||
| 
 | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Create a default content block. | ||||
|    * | ||||
|    * Creates a content block from defaults and ensures that the 'basic block' | ||||
|    * type is being used. | ||||
|    */ | ||||
|   public function testDefaultBlockContentCreation(): void { | ||||
|     $edit = []; | ||||
|     $edit['info[0][value]'] = $this->randomMachineName(8); | ||||
|     $edit['body[0][value]'] = $this->randomMachineName(16); | ||||
|     // Don't pass the content block type in the URL so the default is forced.
 | ||||
|     $this->drupalGet('block/add'); | ||||
|     $this->submitForm($edit, 'Save'); | ||||
| 
 | ||||
|     // Check that the block has been created and that it is a basic block.
 | ||||
|     $this->assertSession()->pageTextContains('basic ' . $edit['info[0][value]'] . ' has been created.'); | ||||
| 
 | ||||
|     // Check that the block exists in the database.
 | ||||
|     $block = $this->getBlockByLabel($edit['info[0][value]']); | ||||
|     $this->assertNotEmpty($block, 'Default Content Block found in database.'); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Verifies that a transaction rolls back the failed creation. | ||||
|    */ | ||||
|   public function testFailedBlockCreation(): void { | ||||
|     // Create a block.
 | ||||
|     try { | ||||
|       $this->createBlockContent('fail_creation'); | ||||
|       $this->fail('Expected exception has not been thrown.'); | ||||
|     } | ||||
|     catch (\Exception $e) { | ||||
|       // Expected exception; just continue testing.
 | ||||
|     } | ||||
| 
 | ||||
|     $connection = Database::getConnection(); | ||||
| 
 | ||||
|     // Check that the block does not exist in the database.
 | ||||
|     $id = $connection->select('block_content_field_data', 'b') | ||||
|       ->fields('b', ['id']) | ||||
|       ->condition('info', 'fail_creation') | ||||
|       ->execute() | ||||
|       ->fetchField(); | ||||
|     $this->assertFalse($id); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Tests deleting a block. | ||||
|    */ | ||||
|   public function testBlockDelete(): void { | ||||
|     // Create a block.
 | ||||
|     $edit = []; | ||||
|     $edit['info[0][value]'] = $this->randomMachineName(8); | ||||
|     $body = $this->randomMachineName(16); | ||||
|     $edit['body[0][value]'] = $body; | ||||
|     $this->drupalGet('block/add/basic'); | ||||
|     $this->submitForm($edit, 'Save'); | ||||
| 
 | ||||
|     // Place the block.
 | ||||
|     $instance = [ | ||||
|       'id' => mb_strtolower($edit['info[0][value]']), | ||||
|       'settings[label]' => $edit['info[0][value]'], | ||||
|       'region' => 'sidebar_first', | ||||
|     ]; | ||||
|     $block = BlockContent::load(1); | ||||
|     $url = 'admin/structure/block/add/block_content:' . $block->uuid() . '/' . $this->config('system.theme')->get('default'); | ||||
|     $this->drupalGet($url); | ||||
|     $this->submitForm($instance, 'Save block'); | ||||
| 
 | ||||
|     $block = BlockContent::load(1); | ||||
| 
 | ||||
|     // Test getInstances method.
 | ||||
|     $this->assertCount(1, $block->getInstances()); | ||||
| 
 | ||||
|     // Navigate to home page.
 | ||||
|     $this->drupalGet(''); | ||||
|     $this->assertSession()->pageTextContains($body); | ||||
| 
 | ||||
|     // Delete the block.
 | ||||
|     $this->drupalGet('admin/content/block/1/delete'); | ||||
|     $this->assertSession()->pageTextContains('This will also remove 1 placed block instance.'); | ||||
| 
 | ||||
|     $this->submitForm([], 'Delete'); | ||||
|     $this->assertSession()->pageTextContains('The content block ' . $edit['info[0][value]'] . ' has been deleted.'); | ||||
| 
 | ||||
|     // Create another block and force the plugin cache to flush.
 | ||||
|     $edit2 = []; | ||||
|     $edit2['info[0][value]'] = $this->randomMachineName(8); | ||||
|     $body2 = $this->randomMachineName(16); | ||||
|     $edit2['body[0][value]'] = $body2; | ||||
|     $this->drupalGet('block/add/basic'); | ||||
|     $this->submitForm($edit2, 'Save'); | ||||
| 
 | ||||
|     $this->assertSession()->responseNotContains('Error message'); | ||||
| 
 | ||||
|     // Create another block with no instances, and test we don't get a
 | ||||
|     // confirmation message about deleting instances.
 | ||||
|     $edit3 = []; | ||||
|     $edit3['info[0][value]'] = $this->randomMachineName(8); | ||||
|     $body = $this->randomMachineName(16); | ||||
|     $edit3['body[0][value]'] = $body; | ||||
|     $this->drupalGet('block/add/basic'); | ||||
|     $this->submitForm($edit3, 'Save'); | ||||
| 
 | ||||
|     // Show the delete confirm form.
 | ||||
|     $this->drupalGet('admin/content/block/3/delete'); | ||||
|     $this->assertSession()->pageTextNotContains('This will also remove'); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Tests placed content blocks create a dependency in the block placement. | ||||
|    */ | ||||
|   public function testConfigDependencies(): void { | ||||
|     $block = $this->createBlockContent(); | ||||
|     // Place the block.
 | ||||
|     $block_placement_id = mb_strtolower($block->label()); | ||||
|     $instance = [ | ||||
|       'id' => $block_placement_id, | ||||
|       'settings[label]' => $block->label(), | ||||
|       'region' => 'sidebar_first', | ||||
|     ]; | ||||
|     $block = BlockContent::load(1); | ||||
|     $url = 'admin/structure/block/add/block_content:' . $block->uuid() . '/' . $this->config('system.theme')->get('default'); | ||||
|     $this->drupalGet($url); | ||||
|     $this->submitForm($instance, 'Save block'); | ||||
| 
 | ||||
|     $dependencies = \Drupal::service('config.manager')->findConfigEntityDependenciesAsEntities('content', [$block->getConfigDependencyName()]); | ||||
|     $block_placement = reset($dependencies); | ||||
|     $this->assertEquals($block_placement_id, $block_placement->id(), "The block placement config entity has a dependency on the block content entity."); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Load a block based on the label. | ||||
|    */ | ||||
|   private function getBlockByLabel(string $label): ?BlockContentInterface { | ||||
|     $blocks = \Drupal::entityTypeManager() | ||||
|       ->getStorage('block_content') | ||||
|       ->loadByProperties(['info' => $label]); | ||||
|     if (empty($blocks)) { | ||||
|       return NULL; | ||||
|     } | ||||
|     return reset($blocks); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										203
									
								
								tests/src/Functional/BlockContentListTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								tests/src/Functional/BlockContentListTest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,203 @@ | ||||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace Drupal\Tests\block_content\Functional; | ||||
| 
 | ||||
| use Drupal\block_content\Entity\BlockContent; | ||||
| 
 | ||||
| /** | ||||
|  * Tests the listing of content blocks. | ||||
|  * | ||||
|  * Tests the fallback block content list when Views is disabled. | ||||
|  * | ||||
|  * @group block_content | ||||
|  * @see \Drupal\block\BlockContentListBuilder | ||||
|  * @see \Drupal\block_content\Tests\BlockContentListViewsTest | ||||
|  */ | ||||
| class BlockContentListTest extends BlockContentTestBase { | ||||
| 
 | ||||
|   /** | ||||
|    * A user with 'access block library' permission. | ||||
|    * | ||||
|    * @var \Drupal\user\UserInterface | ||||
|    */ | ||||
|   protected $baseUser1; | ||||
| 
 | ||||
|   /** | ||||
|    * A user with access to create and edit custom basic blocks. | ||||
|    * | ||||
|    * @var \Drupal\user\UserInterface | ||||
|    */ | ||||
|   protected $baseUser2; | ||||
| 
 | ||||
|   /** | ||||
|    * Permissions to grant admin user. | ||||
|    * | ||||
|    * @var array | ||||
|    */ | ||||
|   protected $permissions = [ | ||||
|     'administer blocks', | ||||
|     'access block library', | ||||
|     'create basic block content', | ||||
|     'edit any basic block content', | ||||
|     'delete any basic block content', | ||||
|     'translate configuration', | ||||
|   ]; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected static $modules = ['block', 'block_content', 'config_translation']; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected $defaultTheme = 'stark'; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function setUp(): void { | ||||
|     parent::setUp(); | ||||
|     $this->baseUser1 = $this->drupalCreateUser(['access block library']); | ||||
|     $this->baseUser2 = $this->drupalCreateUser([ | ||||
|       'access block library', | ||||
|       'create basic block content', | ||||
|       'edit any basic block content', | ||||
|       'delete any basic block content', | ||||
|     ]); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Tests the region value when a new block is saved. | ||||
|    */ | ||||
|   public function testBlockRegionPlacement(): void { | ||||
|     $this->drupalLogin($this->drupalCreateUser($this->permissions)); | ||||
|     $this->drupalGet("admin/structure/block/library/stark", ['query' => ['region' => 'content']]); | ||||
| 
 | ||||
|     $this->clickLink('Add content block'); | ||||
|     $this->assertSession()->statusCodeEquals(200); | ||||
|     $edit = [ | ||||
|       'info[0][value]' => 'foo', | ||||
|     ]; | ||||
|     $this->submitForm($edit, 'Save'); | ||||
|     $this->assertSession()->fieldValueEquals('region', 'content'); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Tests the content block listing page with different permissions. | ||||
|    */ | ||||
|   public function testListing(): void { | ||||
|     // Test with the admin user.
 | ||||
|     $this->drupalLogin($this->drupalCreateUser(['access block library', 'administer block content'])); | ||||
|     $this->drupalGet('admin/content/block'); | ||||
| 
 | ||||
|     // Test for the page title.
 | ||||
|     $this->assertSession()->titleEquals('Content blocks | Drupal'); | ||||
| 
 | ||||
|     // Test for the table.
 | ||||
|     $this->assertSession()->elementExists('xpath', '//div[@class="layout-content"]//table'); | ||||
| 
 | ||||
|     // Test the table header, two cells should be present.
 | ||||
|     $this->assertSession()->elementsCount('xpath', '//div[@class="layout-content"]//table/thead/tr/th', 2); | ||||
| 
 | ||||
|     // Test the contents of each th cell.
 | ||||
|     $this->assertSession()->elementTextEquals('xpath', '//div[@class="layout-content"]//table/thead/tr/th[1]', 'Block description'); | ||||
|     $this->assertSession()->elementTextEquals('xpath', '//div[@class="layout-content"]//table/thead/tr/th[2]', 'Operations'); | ||||
| 
 | ||||
|     $label = 'Antelope'; | ||||
|     $new_label = 'Albatross'; | ||||
|     // Add a new entity using the operations link.
 | ||||
|     $this->clickLink('Add content block'); | ||||
|     $this->assertSession()->statusCodeEquals(200); | ||||
|     $edit = []; | ||||
|     $edit['info[0][value]'] = $label; | ||||
|     $edit['body[0][value]'] = $this->randomMachineName(16); | ||||
|     $this->submitForm($edit, 'Save'); | ||||
| 
 | ||||
|     // Confirm that once the user returns to the listing, the text of the label
 | ||||
|     // (versus elsewhere on the page).
 | ||||
|     $this->assertSession()->elementTextContains('xpath', '//td', $label); | ||||
| 
 | ||||
|     // Check the number of table row cells.
 | ||||
|     $this->assertSession()->elementsCount('xpath', '//div[@class="layout-content"]//table/tbody/tr[1]/td', 2); | ||||
|     // Check the contents of the row. The first cell contains the label,
 | ||||
|     // and the second contains the operations list.
 | ||||
|     $this->assertSession()->elementTextEquals('xpath', '//div[@class="layout-content"]//table/tbody/tr[1]/td[1]', $label); | ||||
| 
 | ||||
|     // Edit the entity using the operations link.
 | ||||
|     $blocks = $this->container | ||||
|       ->get('entity_type.manager') | ||||
|       ->getStorage('block_content') | ||||
|       ->loadByProperties(['info' => $label]); | ||||
|     $block = reset($blocks); | ||||
|     if (!empty($block)) { | ||||
|       $this->assertSession()->linkByHrefExists('admin/content/block/' . $block->id()); | ||||
|       $this->clickLink('Edit'); | ||||
|       $this->assertSession()->statusCodeEquals(200); | ||||
|       $this->assertSession()->titleEquals("Edit content block $label | Drupal"); | ||||
|       $edit = ['info[0][value]' => $new_label]; | ||||
|       $this->submitForm($edit, 'Save'); | ||||
|     } | ||||
|     else { | ||||
|       $this->fail('Did not find Albatross block in the database.'); | ||||
|     } | ||||
| 
 | ||||
|     // Confirm that once the user returns to the listing, the text of the label
 | ||||
|     // (versus elsewhere on the page).
 | ||||
|     $this->assertSession()->elementTextContains('xpath', '//td', $new_label); | ||||
| 
 | ||||
|     // Delete the added entity using the operations link.
 | ||||
|     $this->assertSession()->linkByHrefExists('admin/content/block/' . $block->id() . '/delete'); | ||||
|     $this->clickLink('Delete'); | ||||
|     $this->assertSession()->statusCodeEquals(200); | ||||
|     $this->assertSession()->titleEquals("Are you sure you want to delete the content block $new_label? | Drupal"); | ||||
|     $this->submitForm([], 'Delete'); | ||||
| 
 | ||||
|     // Verify that the text of the label and machine name does not appear in
 | ||||
|     // the list (though it may appear elsewhere on the page).
 | ||||
|     $this->assertSession()->elementTextNotContains('xpath', '//td', $new_label); | ||||
| 
 | ||||
|     // Confirm that the empty text is displayed.
 | ||||
|     $this->assertSession()->pageTextContains('There are no content blocks yet.'); | ||||
| 
 | ||||
|     $block_content = BlockContent::create([ | ||||
|       'info' => 'Non-reusable block', | ||||
|       'type' => 'basic', | ||||
|       'reusable' => FALSE, | ||||
|     ]); | ||||
|     $block_content->save(); | ||||
| 
 | ||||
|     $this->drupalGet('admin/content/block'); | ||||
|     // Confirm that the empty text is displayed.
 | ||||
|     $this->assertSession()->pageTextContains('There are no content blocks yet.'); | ||||
|     // Confirm the non-reusable block is not on the page.
 | ||||
|     $this->assertSession()->pageTextNotContains('Non-reusable block'); | ||||
| 
 | ||||
|     $this->drupalLogout(); | ||||
| 
 | ||||
|     // Create test block for other user tests.
 | ||||
|     $test_block = $this->createBlockContent($label); | ||||
| 
 | ||||
|     $link_text = t('Add content block'); | ||||
|     // Test as a user with view only permissions.
 | ||||
|     $this->drupalLogin($this->baseUser1); | ||||
|     $this->drupalGet('admin/content/block'); | ||||
|     $this->assertSession()->statusCodeEquals(200); | ||||
|     $this->assertSession()->linkNotExists($link_text); | ||||
|     $this->assertSession()->linkByHrefNotExists('admin/content/block/' . $test_block->id()); | ||||
|     $this->assertSession()->linkByHrefNotExists('admin/content/block/' . $test_block->id() . '/delete'); | ||||
| 
 | ||||
|     $this->drupalLogout(); | ||||
| 
 | ||||
|     // Test as a user with permission to create/edit/delete basic blocks.
 | ||||
|     $this->drupalLogin($this->baseUser2); | ||||
|     $this->drupalGet('admin/content/block'); | ||||
|     $this->assertSession()->statusCodeEquals(200); | ||||
|     $this->assertSession()->linkExists($link_text); | ||||
|     $this->assertSession()->linkByHrefExists('admin/content/block/' . $test_block->id()); | ||||
|     $this->assertSession()->linkByHrefExists('admin/content/block/' . $test_block->id() . '/delete'); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										205
									
								
								tests/src/Functional/BlockContentListViewsTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										205
									
								
								tests/src/Functional/BlockContentListViewsTest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,205 @@ | ||||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace Drupal\Tests\block_content\Functional; | ||||
| 
 | ||||
| use Drupal\block_content\Entity\BlockContent; | ||||
| 
 | ||||
| /** | ||||
|  * Tests the Views-powered listing of content blocks. | ||||
|  * | ||||
|  * @group block_content | ||||
|  * @see \Drupal\block\BlockContentListBuilder | ||||
|  * @see \Drupal\block_content\Tests\BlockContentListTest | ||||
|  */ | ||||
| class BlockContentListViewsTest extends BlockContentTestBase { | ||||
| 
 | ||||
|   /** | ||||
|    * A user with 'access block library' permission. | ||||
|    * | ||||
|    * @var \Drupal\user\UserInterface | ||||
|    */ | ||||
|   protected $baseUser1; | ||||
| 
 | ||||
|   /** | ||||
|    * A user with access to create and edit custom basic blocks. | ||||
|    * | ||||
|    * @var \Drupal\user\UserInterface | ||||
|    */ | ||||
|   protected $baseUser2; | ||||
| 
 | ||||
|   /** | ||||
|    * Permissions to grant admin user. | ||||
|    * | ||||
|    * @var array | ||||
|    */ | ||||
|   protected $permissions = [ | ||||
|     'administer blocks', | ||||
|     'access block library', | ||||
|     'create basic block content', | ||||
|     'edit any basic block content', | ||||
|     'delete any basic block content', | ||||
|     'translate configuration', | ||||
|   ]; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected static $modules = [ | ||||
|     'block', | ||||
|     'block_content', | ||||
|     'config_translation', | ||||
|     'views', | ||||
|   ]; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected $defaultTheme = 'stark'; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function setUp(): void { | ||||
|     parent::setUp(); | ||||
|     $this->baseUser1 = $this->drupalCreateUser(['access block library']); | ||||
|     $this->baseUser2 = $this->drupalCreateUser([ | ||||
|       'access block library', | ||||
|       'create basic block content', | ||||
|       'edit any basic block content', | ||||
|       'delete any basic block content', | ||||
|     ]); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Tests the content block listing page. | ||||
|    */ | ||||
|   public function testListing(): void { | ||||
|     // Test with an admin user.
 | ||||
|     $this->drupalLogin($this->adminUser); | ||||
|     $this->drupalGet('admin/content/block'); | ||||
| 
 | ||||
|     // Test for the page title.
 | ||||
|     $this->assertSession()->titleEquals('Content blocks | Drupal'); | ||||
| 
 | ||||
|     // Test for the exposed filters.
 | ||||
|     $this->assertSession()->fieldExists('info'); | ||||
|     $this->assertSession()->fieldExists('type'); | ||||
| 
 | ||||
|     // Test for the table.
 | ||||
|     $this->assertSession()->elementExists('xpath', '//div[@class="layout-content"]//table'); | ||||
| 
 | ||||
|     // Test the table header, four cells should be present.
 | ||||
|     $this->assertSession()->elementsCount('xpath', '//div[@class="layout-content"]//table/thead/tr/th', 4); | ||||
| 
 | ||||
|     // Test the contents of each th cell.
 | ||||
|     $this->assertSession()->elementTextEquals('xpath', '//div[@class="layout-content"]//table/thead/tr/th[1]', 'Block description'); | ||||
|     $this->assertSession()->elementTextEquals('xpath', '//div[@class="layout-content"]//table/thead/tr/th[2]', 'Block type'); | ||||
|     $this->assertSession()->elementTextEquals('xpath', '//div[@class="layout-content"]//table/thead/tr/th[3]', 'Updated Sort ascending'); | ||||
|     $this->assertSession()->elementTextEquals('xpath', '//div[@class="layout-content"]//table/thead/tr/th[4]', 'Operations'); | ||||
| 
 | ||||
|     $label = 'Antelope'; | ||||
|     $new_label = 'Albatross'; | ||||
|     // Add a new entity using the operations link.
 | ||||
|     $this->clickLink('Add content block'); | ||||
|     $this->assertSession()->statusCodeEquals(200); | ||||
|     $edit = []; | ||||
|     $edit['info[0][value]'] = $label; | ||||
|     $edit['body[0][value]'] = $this->randomMachineName(16); | ||||
|     $this->submitForm($edit, 'Save'); | ||||
| 
 | ||||
|     // Confirm that once the user returns to the listing, the text of the label
 | ||||
|     // (versus elsewhere on the page).
 | ||||
|     $this->assertSession()->elementTextContains('xpath', '//td/a', $label); | ||||
| 
 | ||||
|     // Check the number of table row cells.
 | ||||
|     $this->assertSession()->elementsCount('xpath', '//div[@class="layout-content"]//table/tbody/tr/td', 4); | ||||
|     // Check the contents of each row cell. The first cell contains the label,
 | ||||
|     // the second contains the machine name, and the third contains the
 | ||||
|     // operations list.
 | ||||
|     $this->assertSession()->elementTextEquals('xpath', '//div[@class="layout-content"]//table/tbody/tr/td/a', $label); | ||||
| 
 | ||||
|     // Edit the entity using the operations link.
 | ||||
|     $blocks = $this->container | ||||
|       ->get('entity_type.manager') | ||||
|       ->getStorage('block_content') | ||||
|       ->loadByProperties(['info' => $label]); | ||||
|     $block = reset($blocks); | ||||
|     if (!empty($block)) { | ||||
|       $this->assertSession()->linkByHrefExists('admin/content/block/' . $block->id()); | ||||
|       $this->clickLink('Edit'); | ||||
|       $this->assertSession()->statusCodeEquals(200); | ||||
|       $this->assertSession()->titleEquals("Edit content block $label | Drupal"); | ||||
|       $edit = ['info[0][value]' => $new_label]; | ||||
|       $this->submitForm($edit, 'Save'); | ||||
|     } | ||||
|     else { | ||||
|       $this->fail('Did not find Albatross block in the database.'); | ||||
|     } | ||||
| 
 | ||||
|     // Confirm that once the user returns to the listing, the text of the label
 | ||||
|     // (versus elsewhere on the page).
 | ||||
|     $this->assertSession()->elementTextContains('xpath', '//td/a', $new_label); | ||||
| 
 | ||||
|     // Delete the added entity using the operations link.
 | ||||
|     $this->assertSession()->linkByHrefExists('admin/content/block/' . $block->id() . '/delete'); | ||||
|     $this->clickLink('Delete'); | ||||
|     $this->assertSession()->statusCodeEquals(200); | ||||
|     $this->assertSession()->titleEquals("Are you sure you want to delete the content block $new_label? | Drupal"); | ||||
|     $this->submitForm([], 'Delete'); | ||||
| 
 | ||||
|     // Verify that the text of the label and machine name does not appear in
 | ||||
|     // the list (though it may appear elsewhere on the page).
 | ||||
|     $this->assertSession()->elementTextNotContains('xpath', '//td', $new_label); | ||||
| 
 | ||||
|     // Confirm that the empty text is displayed.
 | ||||
|     $this->assertSession()->pageTextContains('There are no content blocks available.'); | ||||
|     $this->assertSession()->linkExists('content block'); | ||||
| 
 | ||||
|     $block_content = BlockContent::create([ | ||||
|       'info' => 'Non-reusable block', | ||||
|       'type' => 'basic', | ||||
|       'reusable' => FALSE, | ||||
|     ]); | ||||
|     $block_content->save(); | ||||
| 
 | ||||
|     $this->drupalGet('admin/content/block'); | ||||
|     // Confirm that the empty text is displayed.
 | ||||
|     $this->assertSession()->pageTextContains('There are no content blocks available.'); | ||||
|     // Confirm the non-reusable block is not on the page.
 | ||||
|     $this->assertSession()->pageTextNotContains('Non-reusable block'); | ||||
| 
 | ||||
|     $this->drupalLogout(); | ||||
| 
 | ||||
|     // Create test block for other user tests.
 | ||||
|     $test_block = $this->createBlockContent($label); | ||||
| 
 | ||||
|     $link_text = t('Add content block'); | ||||
|     // Test as a user with view only permissions.
 | ||||
|     $this->drupalLogin($this->baseUser1); | ||||
|     $this->drupalGet('admin/content/block'); | ||||
|     $this->assertSession()->statusCodeEquals(200); | ||||
|     $this->assertSession()->linkNotExists($link_text); | ||||
|     $matches = $this->xpath('//td[1]'); | ||||
|     $actual = $matches[0]->getText(); | ||||
|     $this->assertEquals($label, $actual, 'Label found for test block.'); | ||||
|     $this->assertSession()->linkNotExists('Edit'); | ||||
|     $this->assertSession()->linkNotExists('Delete'); | ||||
|     $this->assertSession()->linkByHrefNotExists('admin/content/block/' . $test_block->id() . '/delete'); | ||||
| 
 | ||||
|     $this->drupalLogout(); | ||||
| 
 | ||||
|     // Test as a user with permission to create/edit/delete basic blocks.
 | ||||
|     $this->drupalLogin($this->baseUser2); | ||||
|     $this->drupalGet('admin/content/block'); | ||||
|     $this->assertSession()->statusCodeEquals(200); | ||||
|     $this->assertSession()->linkExists($link_text); | ||||
|     $matches = $this->xpath('//td/a'); | ||||
|     $actual = $matches[0]->getText(); | ||||
|     $this->assertEquals($label, $actual, 'Label found for test block.'); | ||||
|     $this->assertSession()->linkByHrefExists('admin/content/block/' . $test_block->id()); | ||||
|     $this->assertSession()->linkByHrefExists('admin/content/block/' . $test_block->id() . '/delete'); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										40
									
								
								tests/src/Functional/BlockContentPageViewTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								tests/src/Functional/BlockContentPageViewTest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,40 @@ | ||||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace Drupal\Tests\block_content\Functional; | ||||
| 
 | ||||
| /** | ||||
|  * Create a block and test block access by attempting to view the block. | ||||
|  * | ||||
|  * @group block_content | ||||
|  */ | ||||
| class BlockContentPageViewTest extends BlockContentTestBase { | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected static $modules = ['block_content_test']; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected $defaultTheme = 'stark'; | ||||
| 
 | ||||
|   /** | ||||
|    * Checks block edit and fallback functionality. | ||||
|    */ | ||||
|   public function testPageEdit(): void { | ||||
|     $this->drupalLogin($this->adminUser); | ||||
|     $block = $this->createBlockContent(); | ||||
| 
 | ||||
|     // Attempt to view the block.
 | ||||
|     $this->drupalGet('block-content/' . $block->id()); | ||||
| 
 | ||||
|     // Ensure user was able to view the block.
 | ||||
|     $this->assertSession()->statusCodeEquals(200); | ||||
|     $this->drupalGet('<front>'); | ||||
|     $this->assertSession()->pageTextContains('This block is broken or missing. You may be missing content or you might need to install the original module.'); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										74
									
								
								tests/src/Functional/BlockContentRedirectTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								tests/src/Functional/BlockContentRedirectTest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,74 @@ | ||||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace Drupal\Tests\block_content\Functional; | ||||
| 
 | ||||
| /** | ||||
|  * Ensures that custom block type functions work correctly. | ||||
|  * | ||||
|  * @group block_content | ||||
|  * @group legacy | ||||
|  */ | ||||
| class BlockContentRedirectTest extends BlockContentTestBase { | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected $defaultTheme = 'stark'; | ||||
| 
 | ||||
|   /** | ||||
|    * Tests the deprecation message from the old block-type page. | ||||
|    * | ||||
|    * @group legacy | ||||
|    */ | ||||
|   public function testBlockContentTypeRedirect(): void { | ||||
|     $this->drupalLogin($this->adminUser); | ||||
|     $this->expectDeprecation('The path /admin/structure/block/block-content/types is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Use /admin/structure/block-content. See https://www.drupal.org/node/3320855'); | ||||
|     $this->drupalGet('/admin/structure/block/block-content/types'); | ||||
|     $this->assertSession() | ||||
|       ->pageTextContains("You have been redirected from admin/structure/block/block-content/types. Update links, shortcuts, and bookmarks to use admin/structure/block-content."); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Tests the deprecation message from the old block library page. | ||||
|    * | ||||
|    * @group legacy | ||||
|    */ | ||||
|   public function testBlockLibraryRedirect(): void { | ||||
|     $this->drupalLogin($this->adminUser); | ||||
|     $this->expectDeprecation('The path /admin/structure/block/block-content is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Use /admin/content/block. See https://www.drupal.org/node/3320855'); | ||||
|     $this->drupalGet('admin/structure/block/block-content'); | ||||
|     $this->assertSession() | ||||
|       ->pageTextContains("You have been redirected from admin/structure/block/block-content. Update links, shortcuts, and bookmarks to use admin/content/block."); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Tests the deprecation message from the old block edit page. | ||||
|    * | ||||
|    * @group legacy | ||||
|    */ | ||||
|   public function testBlockContentEditRedirect(): void { | ||||
|     $block = $this->createBlockContent(); | ||||
|     $this->drupalLogin($this->adminUser); | ||||
|     $this->expectDeprecation('The path /block/{block_content} is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Use /admin/content/block/{block_content}. See https://www.drupal.org/node/3320855'); | ||||
|     $this->drupalGet("/block/{$block->id()}"); | ||||
|     $this->assertSession() | ||||
|       ->pageTextContains("You have been redirected from block/{$block->id()}. Update links, shortcuts, and bookmarks to use admin/content/block/{$block->id()}."); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Tests the deprecation message from the old block delete page. | ||||
|    * | ||||
|    * @group legacy | ||||
|    */ | ||||
|   public function testBlockContentDeleteRedirect(): void { | ||||
|     $block = $this->createBlockContent(); | ||||
|     $this->drupalLogin($this->adminUser); | ||||
|     $this->expectDeprecation('The path /block/{block_content} is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Use /admin/content/block/{block_content}. See https://www.drupal.org/node/3320855'); | ||||
|     $this->drupalGet("/block/{$block->id()}/delete"); | ||||
|     $this->assertSession() | ||||
|       ->pageTextContains("You have been redirected from block/{$block->id()}/delete. Update links, shortcuts, and bookmarks to use admin/content/block/{$block->id()}/delete."); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										96
									
								
								tests/src/Functional/BlockContentRevisionDeleteTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								tests/src/Functional/BlockContentRevisionDeleteTest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,96 @@ | ||||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace Drupal\Tests\block_content\Functional; | ||||
| 
 | ||||
| /** | ||||
|  * Block content revision delete form test. | ||||
|  * | ||||
|  * @group block_content | ||||
|  * @coversDefaultClass \Drupal\Core\Entity\Form\RevisionDeleteForm | ||||
|  */ | ||||
| class BlockContentRevisionDeleteTest extends BlockContentTestBase { | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected $defaultTheme = 'stark'; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected $permissions = [ | ||||
|     'view any basic block content history', | ||||
|     'delete any basic block content revisions', | ||||
|   ]; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function setUp(): void { | ||||
|     parent::setUp(); | ||||
|     $this->drupalLogin($this->adminUser); | ||||
|     $this->drupalPlaceBlock('page_title_block'); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Tests revision delete. | ||||
|    */ | ||||
|   public function testDeleteForm(): void { | ||||
|     $entity = $this->createBlockContent(save: FALSE) | ||||
|       ->setRevisionCreationTime((new \DateTimeImmutable('11 January 2009 4pm'))->getTimestamp()) | ||||
|       ->setRevisionTranslationAffected(TRUE); | ||||
|     $entity->setNewRevision(); | ||||
|     $entity->save(); | ||||
|     $revisionId = $entity->getRevisionId(); | ||||
| 
 | ||||
|     // Cannot delete latest revision.
 | ||||
|     $this->drupalGet($entity->toUrl('revision-delete-form')); | ||||
|     $this->assertSession()->statusCodeEquals(403); | ||||
| 
 | ||||
|     // Create a new non default revision.
 | ||||
|     $entity | ||||
|       ->setRevisionCreationTime((new \DateTimeImmutable('11 January 2009 5pm'))->getTimestamp()) | ||||
|       ->setRevisionTranslationAffected(TRUE) | ||||
|       ->setNewRevision(); | ||||
|     $entity->isDefaultRevision(FALSE); | ||||
|     $entity->save(); | ||||
|     $nonDefaultRevisionId = $entity->getRevisionId(); | ||||
| 
 | ||||
|     // Reload the default entity.
 | ||||
|     $revision = \Drupal::entityTypeManager()->getStorage('block_content') | ||||
|       ->loadRevision($revisionId); | ||||
|     // Cannot delete default revision.
 | ||||
|     $this->drupalGet($revision->toUrl('revision-delete-form')); | ||||
|     $this->assertSession()->statusCodeEquals(403); | ||||
|     $this->assertFalse($revision->access('delete revision', $this->adminUser, FALSE)); | ||||
| 
 | ||||
|     // Reload the non default entity.
 | ||||
|     $revision2 = \Drupal::entityTypeManager()->getStorage('block_content') | ||||
|       ->loadRevision($nonDefaultRevisionId); | ||||
|     $this->drupalGet($revision2->toUrl('revision-delete-form')); | ||||
|     $this->assertSession()->pageTextContains('Are you sure you want to delete the revision from Sun, 01/11/2009 - 17:00?'); | ||||
|     $this->assertSession()->buttonExists('Delete'); | ||||
|     $this->assertSession()->linkExists('Cancel'); | ||||
|     $this->assertTrue($revision2->access('delete revision', $this->adminUser, FALSE)); | ||||
| 
 | ||||
|     $countRevisions = static function (): int { | ||||
|       return (int) \Drupal::entityTypeManager()->getStorage('block_content') | ||||
|         ->getQuery() | ||||
|         ->accessCheck(FALSE) | ||||
|         ->allRevisions() | ||||
|         ->count() | ||||
|         ->execute(); | ||||
|     }; | ||||
| 
 | ||||
|     $count = $countRevisions(); | ||||
|     $this->submitForm([], 'Delete'); | ||||
|     $this->assertEquals($count - 1, $countRevisions()); | ||||
|     $this->assertSession()->statusCodeEquals(200); | ||||
|     $this->assertSession()->addressEquals(sprintf('admin/content/block/%s/revisions', $entity->id())); | ||||
|     $this->assertSession()->pageTextContains(sprintf('Revision from Sun, 01/11/2009 - 17:00 of basic %s has been deleted.', $entity->label())); | ||||
|     $this->assertSession()->elementsCount('css', 'table tbody tr', 1); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										98
									
								
								tests/src/Functional/BlockContentRevisionRevertTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								tests/src/Functional/BlockContentRevisionRevertTest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,98 @@ | ||||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace Drupal\Tests\block_content\Functional; | ||||
| 
 | ||||
| /** | ||||
|  * Block content revision form test. | ||||
|  * | ||||
|  * @group block_content | ||||
|  * @coversDefaultClass \Drupal\Core\Entity\Form\RevisionRevertForm | ||||
|  */ | ||||
| class BlockContentRevisionRevertTest extends BlockContentTestBase { | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected $defaultTheme = 'stark'; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected $permissions = [ | ||||
|     'view any basic block content history', | ||||
|     'revert any basic block content revisions', | ||||
|   ]; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function setUp(): void { | ||||
|     parent::setUp(); | ||||
|     $this->drupalLogin($this->adminUser); | ||||
|     $this->drupalPlaceBlock('page_title_block'); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Tests revision revert. | ||||
|    */ | ||||
|   public function testRevertForm(): void { | ||||
|     $entity = $this->createBlockContent(save: FALSE) | ||||
|       ->setRevisionCreationTime((new \DateTimeImmutable('11 January 2009 4pm'))->getTimestamp()) | ||||
|       ->setRevisionTranslationAffected(TRUE); | ||||
|     $entity->setNewRevision(); | ||||
|     $entity->save(); | ||||
|     $revisionId = $entity->getRevisionId(); | ||||
| 
 | ||||
|     // Cannot revert latest revision.
 | ||||
|     $this->drupalGet($entity->toUrl('revision-revert-form')); | ||||
|     $this->assertSession()->statusCodeEquals(403); | ||||
| 
 | ||||
|     // Create a new non default revision.
 | ||||
|     $entity | ||||
|       ->setRevisionCreationTime((new \DateTimeImmutable('11 January 2009 5pm'))->getTimestamp()) | ||||
|       ->setRevisionTranslationAffected(TRUE) | ||||
|       ->setNewRevision(); | ||||
|     $entity->isDefaultRevision(FALSE); | ||||
|     $entity->save(); | ||||
|     $nonDefaultRevisionId = $entity->getRevisionId(); | ||||
| 
 | ||||
|     // Reload the default entity.
 | ||||
|     $revision = \Drupal::entityTypeManager()->getStorage('block_content') | ||||
|       ->loadRevision($revisionId); | ||||
|     // Cannot revert default revision.
 | ||||
|     $this->drupalGet($revision->toUrl('revision-revert-form')); | ||||
|     $this->assertSession()->statusCodeEquals(403); | ||||
|     $this->assertFalse($revision->access('revert', $this->adminUser, FALSE)); | ||||
| 
 | ||||
|     // Reload the non default entity.
 | ||||
|     $revision2 = \Drupal::entityTypeManager()->getStorage('block_content') | ||||
|       ->loadRevision($nonDefaultRevisionId); | ||||
|     $this->drupalGet($revision2->toUrl('revision-revert-form')); | ||||
|     $this->assertSession()->pageTextContains('Are you sure you want to revert to the revision from Sun, 01/11/2009 - 17:00?'); | ||||
|     $this->assertSession()->buttonExists('Revert'); | ||||
|     $this->assertSession()->linkExists('Cancel'); | ||||
|     $this->assertTrue($revision2->access('revert', $this->adminUser, FALSE)); | ||||
| 
 | ||||
|     $countRevisions = static function (): int { | ||||
|       return (int) \Drupal::entityTypeManager()->getStorage('block_content') | ||||
|         ->getQuery() | ||||
|         ->accessCheck(FALSE) | ||||
|         ->allRevisions() | ||||
|         ->count() | ||||
|         ->execute(); | ||||
|     }; | ||||
| 
 | ||||
|     $count = $countRevisions(); | ||||
|     $this->submitForm([], 'Revert'); | ||||
|     $this->assertEquals($count + 1, $countRevisions()); | ||||
|     $this->assertSession()->statusCodeEquals(200); | ||||
|     $this->assertSession()->addressEquals(sprintf('admin/content/block/%s/revisions', $entity->id())); | ||||
|     $this->assertSession()->pageTextContains(sprintf('basic %s has been reverted to the revision from Sun, 01/11/2009 - 17:00.', $entity->label())); | ||||
|     // Three rows, from the top: the newly reverted revision, the revision from
 | ||||
|     // 5pm, and the revision from 4pm.
 | ||||
|     $this->assertSession()->elementsCount('css', 'table tbody tr', 3); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,94 @@ | ||||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace Drupal\Tests\block_content\Functional; | ||||
| 
 | ||||
| /** | ||||
|  * Block content version history test. | ||||
|  * | ||||
|  * @group block_content | ||||
|  * @coversDefaultClass \Drupal\Core\Entity\Controller\VersionHistoryController | ||||
|  */ | ||||
| class BlockContentRevisionVersionHistoryTest extends BlockContentTestBase { | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected $defaultTheme = 'stark'; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected $permissions = [ | ||||
|     'view any basic block content history', | ||||
|     'revert any basic block content revisions', | ||||
|     'delete any basic block content revisions', | ||||
|   ]; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function setUp(): void { | ||||
|     parent::setUp(); | ||||
|     $this->drupalLogin($this->adminUser); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Tests version history page. | ||||
|    */ | ||||
|   public function testVersionHistory(): void { | ||||
|     $entity = $this->createBlockContent(save: FALSE); | ||||
| 
 | ||||
|     $entity | ||||
|       ->setInfo('first revision') | ||||
|       ->setRevisionCreationTime((new \DateTimeImmutable('1st June 2020 7am'))->getTimestamp()) | ||||
|       ->setRevisionLogMessage('first revision log') | ||||
|       ->setRevisionUser($this->drupalCreateUser(name: 'first author')) | ||||
|       ->setNewRevision(); | ||||
|     $entity->save(); | ||||
| 
 | ||||
|     $entity | ||||
|       ->setInfo('second revision') | ||||
|       ->setRevisionCreationTime((new \DateTimeImmutable('2nd June 2020 8am'))->getTimestamp()) | ||||
|       ->setRevisionLogMessage('second revision log') | ||||
|       ->setRevisionUser($this->drupalCreateUser(name: 'second author')) | ||||
|       ->setNewRevision(); | ||||
|     $entity->save(); | ||||
| 
 | ||||
|     $entity | ||||
|       ->setInfo('third revision') | ||||
|       ->setRevisionCreationTime((new \DateTimeImmutable('3rd June 2020 9am'))->getTimestamp()) | ||||
|       ->setRevisionLogMessage('third revision log') | ||||
|       ->setRevisionUser($this->drupalCreateUser(name: 'third author')) | ||||
|       ->setNewRevision(); | ||||
|     $entity->save(); | ||||
| 
 | ||||
|     $this->drupalGet($entity->toUrl('version-history')); | ||||
|     $this->assertSession()->elementsCount('css', 'table tbody tr', 3); | ||||
| 
 | ||||
|     // Order is newest to oldest revision by creation order.
 | ||||
|     $row1 = $this->assertSession()->elementExists('css', 'table tbody tr:nth-child(1)'); | ||||
|     // Latest revision does not have revert or delete revision operation.
 | ||||
|     $this->assertSession()->elementNotExists('named', ['link', 'Revert'], $row1); | ||||
|     $this->assertSession()->elementNotExists('named', ['link', 'Delete'], $row1); | ||||
|     $this->assertSession()->elementTextContains('css', 'table tbody tr:nth-child(1)', 'Current revision'); | ||||
|     $this->assertSession()->elementTextContains('css', 'table tbody tr:nth-child(1)', 'third revision log'); | ||||
|     $this->assertSession()->elementTextContains('css', 'table tbody tr:nth-child(1)', '06/03/2020 - 09:00 by third author'); | ||||
| 
 | ||||
|     $row2 = $this->assertSession()->elementExists('css', 'table tbody tr:nth-child(2)'); | ||||
|     $this->assertSession()->elementExists('named', ['link', 'Revert'], $row2); | ||||
|     $this->assertSession()->elementExists('named', ['link', 'Delete'], $row2); | ||||
|     $this->assertSession()->elementTextNotContains('css', 'table tbody tr:nth-child(2)', 'Current revision'); | ||||
|     $this->assertSession()->elementTextContains('css', 'table tbody tr:nth-child(2)', 'second revision log'); | ||||
|     $this->assertSession()->elementTextContains('css', 'table tbody tr:nth-child(2)', '06/02/2020 - 08:00 by second author'); | ||||
| 
 | ||||
|     $row3 = $this->assertSession()->elementExists('css', 'table tbody tr:nth-child(3)'); | ||||
|     $this->assertSession()->elementExists('named', ['link', 'Revert'], $row3); | ||||
|     $this->assertSession()->elementExists('named', ['link', 'Delete'], $row3); | ||||
|     $this->assertSession()->elementTextNotContains('css', 'table tbody tr:nth-child(2)', 'Current revision'); | ||||
|     $this->assertSession()->elementTextContains('css', 'table tbody tr:nth-child(3)', 'first revision log'); | ||||
|     $this->assertSession()->elementTextContains('css', 'table tbody tr:nth-child(3)', '06/01/2020 - 07:00 by first author'); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										111
									
								
								tests/src/Functional/BlockContentRevisionsTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								tests/src/Functional/BlockContentRevisionsTest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,111 @@ | ||||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace Drupal\Tests\block_content\Functional; | ||||
| 
 | ||||
| use Drupal\block_content\Entity\BlockContent; | ||||
| use Drupal\user\UserInterface; | ||||
| 
 | ||||
| /** | ||||
|  * Create a block with revisions. | ||||
|  * | ||||
|  * @group block_content | ||||
|  */ | ||||
| class BlockContentRevisionsTest extends BlockContentTestBase { | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected $defaultTheme = 'stark'; | ||||
| 
 | ||||
|   /** | ||||
|    * Stores blocks created during the test. | ||||
|    * @var array | ||||
|    */ | ||||
|   protected $blocks; | ||||
| 
 | ||||
|   /** | ||||
|    * Stores log messages used during the test. | ||||
|    * @var array | ||||
|    */ | ||||
|   protected $revisionLogs; | ||||
| 
 | ||||
|   /** | ||||
|    * Sets the test up. | ||||
|    */ | ||||
|   protected function setUp(): void { | ||||
|     parent::setUp(); | ||||
| 
 | ||||
|     // Create initial block.
 | ||||
|     $block = $this->createBlockContent('initial'); | ||||
| 
 | ||||
|     $blocks = []; | ||||
|     $logs = []; | ||||
| 
 | ||||
|     // Get original block.
 | ||||
|     $blocks[] = $block->getRevisionId(); | ||||
|     $logs[] = ''; | ||||
| 
 | ||||
|     // Create three revisions.
 | ||||
|     $revision_count = 3; | ||||
|     for ($i = 0; $i < $revision_count; $i++) { | ||||
|       $block->setNewRevision(TRUE); | ||||
|       $block->setRevisionLogMessage($this->randomMachineName(32)); | ||||
|       $block->setRevisionUser($this->adminUser); | ||||
|       $block->setRevisionCreationTime(time()); | ||||
|       $logs[] = $block->getRevisionLogMessage(); | ||||
|       $block->save(); | ||||
|       $blocks[] = $block->getRevisionId(); | ||||
|     } | ||||
| 
 | ||||
|     $this->blocks = $blocks; | ||||
|     $this->revisionLogs = $logs; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Checks block revision related operations. | ||||
|    */ | ||||
|   public function testRevisions(): void { | ||||
|     $blocks = $this->blocks; | ||||
|     $logs = $this->revisionLogs; | ||||
| 
 | ||||
|     foreach ($blocks as $delta => $revision_id) { | ||||
|       // Confirm the correct revision text appears.
 | ||||
|       /** @var \Drupal\block_content\BlockContentInterface  $loaded */ | ||||
|       $loaded = $this->container->get('entity_type.manager') | ||||
|         ->getStorage('block_content') | ||||
|         ->loadRevision($revision_id); | ||||
|       // Verify revision log is the same.
 | ||||
|       $this->assertEquals($logs[$delta], $loaded->getRevisionLogMessage(), "Correct log message found for revision $revision_id"); | ||||
|       if ($delta > 0) { | ||||
|         $this->assertInstanceOf(UserInterface::class, $loaded->getRevisionUser()); | ||||
|         $this->assertIsNumeric($loaded->getRevisionUserId()); | ||||
|         $this->assertIsNumeric($loaded->getRevisionCreationTime()); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     // Confirm that this is the default revision.
 | ||||
|     $this->assertTrue($loaded->isDefaultRevision(), 'Third block revision is the default one.'); | ||||
| 
 | ||||
|     // Make a new revision and set it to not be default.
 | ||||
|     // This will create a new revision that is not "front facing".
 | ||||
|     // Save this as a non-default revision.
 | ||||
|     $loaded->setNewRevision(); | ||||
|     $loaded->isDefaultRevision(FALSE); | ||||
|     $loaded->body = $this->randomMachineName(8); | ||||
|     $loaded->save(); | ||||
| 
 | ||||
|     // Confirm that revision body text is not present on default version of
 | ||||
|     // block.
 | ||||
|     $this->drupalGet('admin/content/block/' . $loaded->id()); | ||||
|     $this->assertSession()->pageTextNotContains($loaded->body->value); | ||||
| 
 | ||||
|     // Verify that the non-default revision id is greater than the default
 | ||||
|     // revision id.
 | ||||
|     $default_revision = BlockContent::load($loaded->id()); | ||||
|     // Verify that the revision ID is greater than the default revision ID.
 | ||||
|     $this->assertGreaterThan($default_revision->getRevisionId(), $loaded->getRevisionId()); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										112
									
								
								tests/src/Functional/BlockContentSaveTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								tests/src/Functional/BlockContentSaveTest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,112 @@ | ||||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace Drupal\Tests\block_content\Functional; | ||||
| 
 | ||||
| use Drupal\block_content\Entity\BlockContent; | ||||
| 
 | ||||
| /** | ||||
|  * Tests $block_content->save() for saving content. | ||||
|  * | ||||
|  * @group block_content | ||||
|  */ | ||||
| class BlockContentSaveTest extends BlockContentTestBase { | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected static $modules = ['block_content_test']; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected $defaultTheme = 'stark'; | ||||
| 
 | ||||
|   /** | ||||
|    * Sets the test up. | ||||
|    */ | ||||
|   protected function setUp(): void { | ||||
|     parent::setUp(); | ||||
| 
 | ||||
|     $this->drupalLogin($this->adminUser); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Checks whether content block IDs are saved properly during an import. | ||||
|    */ | ||||
|   public function testImport(): void { | ||||
|     // Content block ID must be a number that is not in the database.
 | ||||
|     $max_id = (int) \Drupal::entityQueryAggregate('block_content') | ||||
|       ->accessCheck(FALSE) | ||||
|       ->aggregate('id', 'max') | ||||
|       ->execute()[0]['id_max']; | ||||
|     $test_id = $max_id + mt_rand(1000, 1000000); | ||||
|     $info = $this->randomMachineName(8); | ||||
|     $block_array = [ | ||||
|       'info' => $info, | ||||
|       'body' => ['value' => $this->randomMachineName(32)], | ||||
|       'type' => 'basic', | ||||
|       'id' => $test_id, | ||||
|     ]; | ||||
|     $block = BlockContent::create($block_array); | ||||
|     $block->enforceIsNew(TRUE); | ||||
|     $block->save(); | ||||
| 
 | ||||
|     // Verify that block_submit did not wipe the provided id.
 | ||||
|     $this->assertEquals($test_id, $block->id(), 'Block imported using provide id'); | ||||
| 
 | ||||
|     // Test the import saved.
 | ||||
|     $block_by_id = BlockContent::load($test_id); | ||||
|     $this->assertNotEmpty($block_by_id, 'Content block load by block ID.'); | ||||
|     $this->assertSame($block_array['body']['value'], $block_by_id->body->value); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Tests determining changes in hook_block_presave(). | ||||
|    * | ||||
|    * Verifies the static block load cache is cleared upon save. | ||||
|    */ | ||||
|   public function testDeterminingChanges(): void { | ||||
|     // Initial creation.
 | ||||
|     $block = $this->createBlockContent('test_changes'); | ||||
|     // Creating a block should set the changed date to the current time
 | ||||
|     // which is always greater than the time set by hooks we're testing.
 | ||||
|     $this->assertGreaterThan(979534800, $block->getChangedTime(), 'Creating a block sets default "changed" timestamp.'); | ||||
| 
 | ||||
|     // Update the block without applying changes.
 | ||||
|     $block->save(); | ||||
|     $this->assertEquals('test_changes', $block->label(), 'No changes have been determined.'); | ||||
| 
 | ||||
|     // Apply changes.
 | ||||
|     $block->setInfo('updated'); | ||||
|     $block->save(); | ||||
| 
 | ||||
|     // The hook implementations block_content_test_block_content_presave() and
 | ||||
|     // block_content_test_block_content_update() determine changes and change
 | ||||
|     // the title as well as programmatically set the 'changed' timestamp.
 | ||||
|     $this->assertEquals('updated_presave_update', $block->label(), 'Changes have been determined.'); | ||||
|     $this->assertEquals(979534800, $block->getChangedTime(), 'Saving a content block uses "changed" timestamp set in presave hook.'); | ||||
| 
 | ||||
|     // Test the static block load cache to be cleared.
 | ||||
|     $block = BlockContent::load($block->id()); | ||||
|     $this->assertEquals('updated_presave', $block->label(), 'Static cache has been cleared.'); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Tests saving a block on block insert. | ||||
|    * | ||||
|    * This test ensures that a block has been fully saved when | ||||
|    * hook_block_content_insert() is invoked, so that the block can be saved again | ||||
|    * in a hook implementation without errors. | ||||
|    * | ||||
|    * @see block_test_block_insert() | ||||
|    */ | ||||
|   public function testBlockContentSaveOnInsert(): void { | ||||
|     // block_content_test_block_content_insert() triggers a save on insert if the
 | ||||
|     // title equals 'new'.
 | ||||
|     $block = $this->createBlockContent('new'); | ||||
|     $this->assertEquals('BlockContent ' . $block->id(), $block->label(), 'Content block saved on block insert.'); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										138
									
								
								tests/src/Functional/BlockContentTestBase.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								tests/src/Functional/BlockContentTestBase.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,138 @@ | ||||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace Drupal\Tests\block_content\Functional; | ||||
| 
 | ||||
| use Drupal\block_content\Entity\BlockContent; | ||||
| use Drupal\block_content\Entity\BlockContentType; | ||||
| use Drupal\Tests\BrowserTestBase; | ||||
| 
 | ||||
| /** | ||||
|  * Sets up block content types. | ||||
|  */ | ||||
| abstract class BlockContentTestBase extends BrowserTestBase { | ||||
| 
 | ||||
|   /** | ||||
|    * Profile to use. | ||||
|    * | ||||
|    * @var string | ||||
|    */ | ||||
|   protected $profile = 'testing'; | ||||
| 
 | ||||
|   /** | ||||
|    * Admin user. | ||||
|    * | ||||
|    * @var \Drupal\user\UserInterface | ||||
|    */ | ||||
|   protected $adminUser; | ||||
| 
 | ||||
|   /** | ||||
|    * Permissions to grant admin user. | ||||
|    * | ||||
|    * @var array | ||||
|    */ | ||||
|   protected $permissions = [ | ||||
|     'administer blocks', | ||||
|     'access block library', | ||||
|     'administer block types', | ||||
|     'administer block content', | ||||
|   ]; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected static $modules = ['block', 'block_content']; | ||||
| 
 | ||||
|   /** | ||||
|    * Whether or not to auto-create the basic block type during setup. | ||||
|    * | ||||
|    * @var bool | ||||
|    */ | ||||
|   protected $autoCreateBasicBlockType = TRUE; | ||||
| 
 | ||||
|   /** | ||||
|    * Sets the test up. | ||||
|    */ | ||||
|   protected function setUp(): void { | ||||
|     parent::setUp(); | ||||
|     if ($this->autoCreateBasicBlockType) { | ||||
|       $this->createBlockContentType('basic', TRUE); | ||||
|     } | ||||
| 
 | ||||
|     $this->adminUser = $this->drupalCreateUser($this->permissions); | ||||
|     $this->drupalPlaceBlock('local_actions_block'); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Creates a content block. | ||||
|    * | ||||
|    * @param bool|string $title | ||||
|    *   (optional) Title of block. When no value is given uses a random name. | ||||
|    *   Defaults to FALSE. | ||||
|    * @param string $bundle | ||||
|    *   (optional) Bundle name. Defaults to 'basic'. | ||||
|    * @param bool $save | ||||
|    *   (optional) Whether to save the block. Defaults to TRUE. | ||||
|    * | ||||
|    * @return \Drupal\block_content\Entity\BlockContent | ||||
|    *   Created content block. | ||||
|    */ | ||||
|   protected function createBlockContent($title = FALSE, $bundle = 'basic', $save = TRUE) { | ||||
|     $title = $title ?: $this->randomMachineName(); | ||||
|     $block_content = BlockContent::create([ | ||||
|       'info' => $title, | ||||
|       'type' => $bundle, | ||||
|       'langcode' => 'en', | ||||
|     ]); | ||||
|     if ($block_content && $save === TRUE) { | ||||
|       $block_content->save(); | ||||
|     } | ||||
|     return $block_content; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Creates a block type (bundle). | ||||
|    * | ||||
|    * @param array|string $values | ||||
|    *   The value to create the block content type. If $values is an array | ||||
|    *   it should be like: ['id' => 'foo', 'label' => 'Foo']. If $values | ||||
|    *   is a string, it will be considered that it represents the label. | ||||
|    * @param bool $create_body | ||||
|    *   Whether or not to create the body field | ||||
|    * | ||||
|    * @return \Drupal\block_content\Entity\BlockContentType | ||||
|    *   Created block type. | ||||
|    */ | ||||
|   protected function createBlockContentType($values, $create_body = FALSE) { | ||||
|     if (is_array($values)) { | ||||
|       if (!isset($values['id'])) { | ||||
|         do { | ||||
|           $id = $this->randomMachineName(8); | ||||
|         } while (BlockContentType::load($id)); | ||||
|       } | ||||
|       else { | ||||
|         $id = $values['id']; | ||||
|       } | ||||
|       $values += [ | ||||
|         'id' => $id, | ||||
|         'label' => $id, | ||||
|         'revision' => FALSE, | ||||
|       ]; | ||||
|       $bundle = BlockContentType::create($values); | ||||
|     } | ||||
|     else { | ||||
|       $bundle = BlockContentType::create([ | ||||
|         'id' => $values, | ||||
|         'label' => $values, | ||||
|         'revision' => FALSE, | ||||
|       ]); | ||||
|     } | ||||
|     $bundle->save(); | ||||
|     if ($create_body) { | ||||
|       block_content_add_body_field($bundle->id()); | ||||
|     } | ||||
|     return $bundle; | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										158
									
								
								tests/src/Functional/BlockContentTranslationUITest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								tests/src/Functional/BlockContentTranslationUITest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,158 @@ | ||||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace Drupal\Tests\block_content\Functional; | ||||
| 
 | ||||
| use Drupal\block_content\Entity\BlockContentType; | ||||
| use Drupal\Tests\content_translation\Functional\ContentTranslationUITestBase; | ||||
| 
 | ||||
| /** | ||||
|  * Tests the block content translation UI. | ||||
|  * | ||||
|  * @group block_content | ||||
|  */ | ||||
| class BlockContentTranslationUITest extends ContentTranslationUITestBase { | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected static $modules = [ | ||||
|     'language', | ||||
|     'content_translation', | ||||
|     'block', | ||||
|     'field_ui', | ||||
|     'block_content', | ||||
|   ]; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected $defaultTheme = 'stark'; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected $defaultCacheContexts = [ | ||||
|     'languages:language_interface', | ||||
|     'session', | ||||
|     'theme', | ||||
|     'url.path', | ||||
|     'url.query_args', | ||||
|     'user.permissions', | ||||
|     'user.roles:authenticated', | ||||
|   ]; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function setUp(): void { | ||||
|     $this->entityTypeId = 'block_content'; | ||||
|     $this->bundle = 'basic'; | ||||
|     $this->testLanguageSelector = FALSE; | ||||
|     parent::setUp(); | ||||
|     $this->doSetup(); | ||||
| 
 | ||||
|     $this->drupalPlaceBlock('page_title_block'); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function setupBundle() { | ||||
|     // Create the basic bundle since it is provided by standard.
 | ||||
|     $bundle = BlockContentType::create([ | ||||
|       'id' => $this->bundle, | ||||
|       'label' => $this->bundle, | ||||
|       'revision' => FALSE, | ||||
|     ]); | ||||
|     $bundle->save(); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   public function getTranslatorPermissions() { | ||||
|     return array_merge(parent::getTranslatorPermissions(), [ | ||||
|       'translate any entity', | ||||
|       'access administration pages', | ||||
|       'administer blocks', | ||||
|       'administer block_content fields', | ||||
|       'access block library', | ||||
|       'create basic block content', | ||||
|       'edit any basic block content', | ||||
|       'delete any basic block content', | ||||
|     ]); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function getNewEntityValues($langcode) { | ||||
|     return ['info' => $this->randomMachineName()] + parent::getNewEntityValues($langcode); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Returns an edit array containing the values to be posted. | ||||
|    */ | ||||
|   protected function getEditValues($values, $langcode, $new = FALSE) { | ||||
|     $edit = parent::getEditValues($values, $langcode, $new); | ||||
|     foreach ($edit as $property => $value) { | ||||
|       if ($property == 'info') { | ||||
|         $edit['info[0][value]'] = $value; | ||||
|         unset($edit[$property]); | ||||
|       } | ||||
|     } | ||||
|     return $edit; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function doTestBasicTranslation() { | ||||
|     parent::doTestBasicTranslation(); | ||||
| 
 | ||||
|     // Ensure that a block translation can be created using the same description
 | ||||
|     // as in the original language.
 | ||||
|     $default_langcode = $this->langcodes[0]; | ||||
|     $values = $this->getNewEntityValues($default_langcode); | ||||
|     $storage = \Drupal::entityTypeManager()->getStorage($this->entityTypeId); | ||||
|     /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ | ||||
|     $entity = $storage->create(['type' => 'basic'] + $values); | ||||
|     $entity->save(); | ||||
|     $entity->addTranslation('it', $values); | ||||
| 
 | ||||
|     try { | ||||
|       $entity->save(); | ||||
|     } | ||||
|     catch (\Exception $e) { | ||||
|       $this->fail('Blocks can have translations with the same "info" value.'); | ||||
|     } | ||||
| 
 | ||||
|     // Check that the translate operation link is shown.
 | ||||
|     $this->drupalGet('admin/content/block'); | ||||
|     $this->assertSession()->linkByHrefExists('admin/content/block/' . $entity->id() . '/translations'); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function doTestTranslationEdit() { | ||||
|     $storage = $this->container->get('entity_type.manager') | ||||
|       ->getStorage($this->entityTypeId); | ||||
|     $storage->resetCache([$this->entityId]); | ||||
|     $entity = $storage->load($this->entityId); | ||||
|     $languages = $this->container->get('language_manager')->getLanguages(); | ||||
| 
 | ||||
|     foreach ($this->langcodes as $langcode) { | ||||
|       // We only want to test the title for non-english translations.
 | ||||
|       if ($langcode != 'en') { | ||||
|         $options = ['language' => $languages[$langcode]]; | ||||
|         $url = $entity->toUrl('edit-form', $options); | ||||
|         $this->drupalGet($url); | ||||
|         $this->assertSession()->pageTextContains("Edit {$entity->bundle()} {$entity->getTranslation($langcode)->label()} [{$languages[$langcode]->getName()} translation]"); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										266
									
								
								tests/src/Functional/BlockContentTypeTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										266
									
								
								tests/src/Functional/BlockContentTypeTest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,266 @@ | ||||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace Drupal\Tests\block_content\Functional; | ||||
| 
 | ||||
| use Drupal\block_content\Entity\BlockContentType; | ||||
| use Drupal\Component\Utility\Html; | ||||
| use Drupal\Core\Url; | ||||
| use Drupal\Tests\system\Functional\Menu\AssertBreadcrumbTrait; | ||||
| 
 | ||||
| /** | ||||
|  * Ensures that block type functions work correctly. | ||||
|  * | ||||
|  * @group block_content | ||||
|  */ | ||||
| class BlockContentTypeTest extends BlockContentTestBase { | ||||
| 
 | ||||
|   use AssertBreadcrumbTrait; | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected static $modules = ['field_ui']; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected $defaultTheme = 'stark'; | ||||
| 
 | ||||
|   /** | ||||
|    * Permissions to grant admin user. | ||||
|    * | ||||
|    * @var array | ||||
|    */ | ||||
|   protected $permissions = [ | ||||
|     'administer block content', | ||||
|     'administer blocks', | ||||
|     'administer block_content fields', | ||||
|     'administer block types', | ||||
|     'administer block content', | ||||
|     'access block library', | ||||
|   ]; | ||||
| 
 | ||||
|   /** | ||||
|    * Whether or not to create an initial block type. | ||||
|    * | ||||
|    * @var bool | ||||
|    */ | ||||
|   protected $autoCreateBasicBlockType = FALSE; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function setUp(): void { | ||||
|     parent::setUp(); | ||||
| 
 | ||||
|     $this->drupalPlaceBlock('page_title_block'); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Tests the order of the block content types on the add page. | ||||
|    */ | ||||
|   public function testBlockContentAddPageOrder(): void { | ||||
|     $this->createBlockContentType(['id' => 'bundle_1', 'label' => 'Bundle 1']); | ||||
|     $this->createBlockContentType(['id' => 'bundle_2', 'label' => 'Aaa Bundle 2']); | ||||
|     $this->drupalLogin($this->adminUser); | ||||
|     $this->drupalGet('block/add'); | ||||
|     $this->assertSession()->pageTextMatches('/Aaa Bundle 2(.*)Bundle 1/'); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Tests creating a block type programmatically and via a form. | ||||
|    */ | ||||
|   public function testBlockContentTypeCreation(): void { | ||||
|     // Log in a test user.
 | ||||
|     $this->drupalLogin($this->adminUser); | ||||
| 
 | ||||
|     // Test the page with no block-types.
 | ||||
|     $this->drupalGet('block/add'); | ||||
|     $this->assertSession()->statusCodeEquals(200); | ||||
|     $this->assertSession()->pageTextContains('You have not created any block types yet'); | ||||
|     $this->clickLink('block type creation page'); | ||||
| 
 | ||||
|     // Create a block type via the user interface.
 | ||||
|     $edit = [ | ||||
|       'id' => 'foo', | ||||
|       'label' => 'title for foo', | ||||
|     ]; | ||||
|     $this->submitForm($edit, 'Save and manage fields'); | ||||
| 
 | ||||
|     // Asserts that form submit redirects to the expected manage fields page.
 | ||||
|     $this->assertSession()->addressEquals('admin/structure/block-content/manage/' . $edit['id'] . '/fields'); | ||||
| 
 | ||||
|     $block_type = BlockContentType::load('foo'); | ||||
|     $this->assertInstanceOf(BlockContentType::class, $block_type); | ||||
| 
 | ||||
|     $field_definitions = \Drupal::service('entity_field.manager')->getFieldDefinitions('block_content', 'foo'); | ||||
|     $this->assertTrue(isset($field_definitions['body']), 'Body field created when using the UI to create block content types.'); | ||||
| 
 | ||||
|     // Check that the block type was created in site default language.
 | ||||
|     $default_langcode = \Drupal::languageManager()->getDefaultLanguage()->getId(); | ||||
|     $this->assertEquals($block_type->language()->getId(), $default_langcode); | ||||
| 
 | ||||
|     // Create block types programmatically.
 | ||||
|     $this->createBlockContentType('basic', TRUE); | ||||
|     $field_definitions = \Drupal::service('entity_field.manager')->getFieldDefinitions('block_content', 'basic'); | ||||
|     $this->assertTrue(isset($field_definitions['body']), "Body field for 'basic' block type created when using the testing API to create block content types."); | ||||
| 
 | ||||
|     $this->createBlockContentType('other'); | ||||
|     $field_definitions = \Drupal::service('entity_field.manager')->getFieldDefinitions('block_content', 'other'); | ||||
|     $this->assertFalse(isset($field_definitions['body']), "Body field for 'other' block type not created when using the testing API to create block content types."); | ||||
| 
 | ||||
|     $block_type = BlockContentType::load('other'); | ||||
|     $this->assertInstanceOf(BlockContentType::class, $block_type); | ||||
| 
 | ||||
|     $this->drupalGet('block/add/' . $block_type->id()); | ||||
|     $this->assertSession()->statusCodeEquals(200); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Tests editing a block type using the UI. | ||||
|    */ | ||||
|   public function testBlockContentTypeEditing(): void { | ||||
|     $this->drupalPlaceBlock('system_breadcrumb_block'); | ||||
|     // Now create an initial block-type.
 | ||||
|     $this->createBlockContentType('basic', TRUE); | ||||
| 
 | ||||
|     $this->drupalLogin($this->adminUser); | ||||
|     // We need two block types to prevent /block/add redirecting.
 | ||||
|     $this->createBlockContentType('other'); | ||||
| 
 | ||||
|     $field_definitions = \Drupal::service('entity_field.manager')->getFieldDefinitions('block_content', 'other'); | ||||
|     $this->assertFalse(isset($field_definitions['body']), 'Body field was not created when using the API to create block content types.'); | ||||
| 
 | ||||
|     // Verify that title and body fields are displayed.
 | ||||
|     $this->drupalGet('block/add/basic'); | ||||
|     $this->assertSession()->pageTextContains('Block description'); | ||||
|     $this->assertNotEmpty($this->cssSelect('#edit-body-0-value'), 'Body field was found.'); | ||||
| 
 | ||||
|     // Change the block type name.
 | ||||
|     $edit = [ | ||||
|       'label' => 'Bar', | ||||
|     ]; | ||||
|     $this->drupalGet('admin/structure/block-content/manage/basic'); | ||||
|     $this->assertSession()->titleEquals('Edit basic block type | Drupal'); | ||||
|     $this->submitForm($edit, 'Save'); | ||||
|     $front_page_path = Url::fromRoute('<front>')->toString(); | ||||
|     $this->assertBreadcrumb('admin/structure/block-content/manage/basic/fields', [ | ||||
|       $front_page_path => 'Home', | ||||
|       'admin/structure/block-content' => 'Block types', | ||||
|       'admin/structure/block-content/manage/basic' => 'Edit Bar', | ||||
|     ]); | ||||
|     \Drupal::service('entity_field.manager')->clearCachedFieldDefinitions(); | ||||
| 
 | ||||
|     $this->drupalGet('block/add'); | ||||
|     $this->assertSession()->pageTextContains('Bar'); | ||||
|     $this->clickLink('Bar'); | ||||
|     // Verify that the original machine name was used in the URL.
 | ||||
|     $this->assertSession()->addressEquals(Url::fromRoute('block_content.add_form', ['block_content_type' => 'basic'])); | ||||
| 
 | ||||
|     // Remove the body field.
 | ||||
|     $this->drupalGet('admin/structure/block-content/manage/basic/fields/block_content.basic.body/delete'); | ||||
|     $this->submitForm([], 'Delete'); | ||||
|     // Resave the settings for this type.
 | ||||
|     $this->drupalGet('admin/structure/block-content/manage/basic'); | ||||
|     $this->submitForm([], 'Save'); | ||||
|     // Check that the body field doesn't exist.
 | ||||
|     $this->drupalGet('block/add/basic'); | ||||
|     $this->assertEmpty($this->cssSelect('#edit-body-0-value'), 'Body field was not found.'); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Tests deleting a block type that still has content. | ||||
|    */ | ||||
|   public function testBlockContentTypeDeletion(): void { | ||||
|     // Now create an initial block-type.
 | ||||
|     $this->createBlockContentType('basic', TRUE); | ||||
| 
 | ||||
|     // Create a block type programmatically.
 | ||||
|     $type = $this->createBlockContentType('foo'); | ||||
| 
 | ||||
|     $this->drupalLogin($this->adminUser); | ||||
| 
 | ||||
|     // Add a new block of this type.
 | ||||
|     $block = $this->createBlockContent(FALSE, 'foo'); | ||||
|     // Attempt to delete the block type, which should not be allowed.
 | ||||
|     $this->drupalGet('admin/structure/block-content/manage/' . $type->id() . '/delete'); | ||||
|     $this->assertSession()->pageTextContains($type->label() . ' is used by 1 content block on your site. You can not remove this block type until you have removed all of the ' . $type->label() . ' blocks.'); | ||||
|     $this->assertSession()->pageTextNotContains('This action cannot be undone.'); | ||||
| 
 | ||||
|     // Delete the block.
 | ||||
|     $block->delete(); | ||||
|     // Attempt to delete the block type, which should now be allowed.
 | ||||
|     $this->drupalGet('admin/structure/block-content/manage/' . $type->id() . '/delete'); | ||||
|     $this->assertSession()->pageTextContains('Are you sure you want to delete the block type ' . $type->id() . '?'); | ||||
|     $this->assertSession()->pageTextContains('This action cannot be undone.'); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Tests that redirects work as expected when multiple block types exist. | ||||
|    */ | ||||
|   public function testsBlockContentAddTypes(): void { | ||||
|     // Now create an initial block-type.
 | ||||
|     $this->createBlockContentType('basic', TRUE); | ||||
| 
 | ||||
|     $this->drupalLogin($this->adminUser); | ||||
|     // Create two block types programmatically.
 | ||||
|     $this->createBlockContentType('foo'); | ||||
|     $this->createBlockContentType('bar'); | ||||
| 
 | ||||
|     // Get the content block storage.
 | ||||
|     $storage = $this->container | ||||
|       ->get('entity_type.manager') | ||||
|       ->getStorage('block_content'); | ||||
| 
 | ||||
|     // Install all themes.
 | ||||
|     $themes = ['olivero', 'stark', 'claro']; | ||||
|     \Drupal::service('theme_installer')->install($themes); | ||||
|     $theme_settings = $this->config('system.theme'); | ||||
|     foreach ($themes as $default_theme) { | ||||
|       // Change the default theme.
 | ||||
|       $theme_settings->set('default', $default_theme)->save(); | ||||
|       $this->drupalPlaceBlock('local_actions_block'); | ||||
| 
 | ||||
|       // For each installed theme, go to its block page and test the redirects.
 | ||||
|       foreach ($themes as $theme) { | ||||
|         // Test that adding a block from the 'place blocks' form sends you to the
 | ||||
|         // block configure form.
 | ||||
|         $path = $theme == $default_theme ? 'admin/structure/block' : "admin/structure/block/list/$theme"; | ||||
|         $this->drupalGet($path); | ||||
|         $this->clickLink('Place block'); | ||||
|         $this->clickLink('Add content block'); | ||||
|         $this->clickLink('foo'); | ||||
|         // Create a new block.
 | ||||
|         $edit = ['info[0][value]' => $this->randomMachineName(8)]; | ||||
|         $this->submitForm($edit, 'Save and configure'); | ||||
|         $blocks = $storage->loadByProperties(['info' => $edit['info[0][value]']]); | ||||
|         if (!empty($blocks)) { | ||||
|           $block = reset($blocks); | ||||
|           $this->assertSession()->addressEquals(Url::fromRoute('block.admin_add', ['plugin_id' => 'block_content:' . $block->uuid(), 'theme' => $theme])); | ||||
|           $this->submitForm(['region' => 'content'], 'Save block'); | ||||
|           $this->assertSession()->addressEquals(Url::fromRoute('block.admin_display_theme', ['theme' => $theme], ['query' => ['block-placement' => $theme . '-' . Html::getClass($edit['info[0][value]'])]])); | ||||
|         } | ||||
|         else { | ||||
|           $this->fail('Could not load created block.'); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     // Test that adding a block from the 'content blocks list' doesn't send you
 | ||||
|     // to the block configure form.
 | ||||
|     $this->drupalGet('admin/content/block'); | ||||
|     $this->clickLink('Add content block'); | ||||
|     $this->clickLink('foo'); | ||||
|     $edit = ['info[0][value]' => $this->randomMachineName(8)]; | ||||
|     $this->submitForm($edit, 'Save'); | ||||
|     $blocks = $storage->loadByProperties(['info' => $edit['info[0][value]']]); | ||||
|     if (!empty($blocks)) { | ||||
|       $this->assertSession()->addressEquals(Url::fromRoute('entity.block_content.collection')); | ||||
|     } | ||||
|     else { | ||||
|       $this->fail('Could not load created block.'); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										14
									
								
								tests/src/Functional/GenericTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								tests/src/Functional/GenericTest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | ||||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace Drupal\Tests\block_content\Functional; | ||||
| 
 | ||||
| use Drupal\Tests\system\Functional\Module\GenericModuleTestBase; | ||||
| 
 | ||||
| /** | ||||
|  * Generic module test for block_content. | ||||
|  * | ||||
|  * @group block_content | ||||
|  */ | ||||
| class GenericTest extends GenericModuleTestBase {} | ||||
							
								
								
									
										98
									
								
								tests/src/Functional/PageEditTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								tests/src/Functional/PageEditTest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,98 @@ | ||||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace Drupal\Tests\block_content\Functional; | ||||
| 
 | ||||
| use Drupal\block_content\Entity\BlockContent; | ||||
| use Drupal\Tests\system\Functional\Menu\AssertBreadcrumbTrait; | ||||
| 
 | ||||
| /** | ||||
|  * Create a block and test block edit functionality. | ||||
|  * | ||||
|  * @group block_content | ||||
|  */ | ||||
| class PageEditTest extends BlockContentTestBase { | ||||
| 
 | ||||
|   use AssertBreadcrumbTrait; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected $defaultTheme = 'stark'; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function setUp(): void { | ||||
|     parent::setUp(); | ||||
| 
 | ||||
|     $this->drupalPlaceBlock('page_title_block'); | ||||
|     $this->drupalPlaceBlock('system_breadcrumb_block'); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Checks block edit functionality. | ||||
|    */ | ||||
|   public function testPageEdit(): void { | ||||
|     $this->drupalLogin($this->adminUser); | ||||
| 
 | ||||
|     $title_key = 'info[0][value]'; | ||||
|     $body_key = 'body[0][value]'; | ||||
|     // Create block to edit.
 | ||||
|     $edit = []; | ||||
|     $edit['info[0][value]'] = $this->randomMachineName(8); | ||||
|     $edit[$body_key] = $this->randomMachineName(16); | ||||
|     $this->drupalGet('block/add/basic'); | ||||
|     $this->submitForm($edit, 'Save'); | ||||
| 
 | ||||
|     // Check that the block exists in the database.
 | ||||
|     $blocks = \Drupal::entityQuery('block_content') | ||||
|       ->accessCheck(FALSE) | ||||
|       ->condition('info', $edit['info[0][value]']) | ||||
|       ->execute(); | ||||
|     $block = BlockContent::load(reset($blocks)); | ||||
|     $this->assertNotEmpty($block, 'Content block found in database.'); | ||||
| 
 | ||||
|     // Load the edit page.
 | ||||
|     $this->drupalGet('admin/content/block/' . $block->id()); | ||||
|     $this->assertSession()->fieldValueEquals($title_key, $edit[$title_key]); | ||||
|     $this->assertSession()->fieldValueEquals($body_key, $edit[$body_key]); | ||||
| 
 | ||||
|     // Edit the content of the block.
 | ||||
|     $edit = []; | ||||
|     $edit[$title_key] = $this->randomMachineName(8); | ||||
|     $edit[$body_key] = $this->randomMachineName(16); | ||||
|     // Stay on the current page, without reloading.
 | ||||
|     $this->submitForm($edit, 'Save'); | ||||
| 
 | ||||
|     // Edit the same block, creating a new revision.
 | ||||
|     $this->drupalGet("admin/content/block/" . $block->id()); | ||||
|     $edit = []; | ||||
|     $edit['info[0][value]'] = $this->randomMachineName(8); | ||||
|     $edit[$body_key] = $this->randomMachineName(16); | ||||
|     $edit['revision'] = TRUE; | ||||
|     $this->submitForm($edit, 'Save'); | ||||
| 
 | ||||
|     // Ensure that the block revision has been created.
 | ||||
|     \Drupal::entityTypeManager()->getStorage('block_content')->resetCache([$block->id()]); | ||||
|     $revised_block = BlockContent::load($block->id()); | ||||
|     $this->assertNotSame($block->getRevisionId(), $revised_block->getRevisionId(), 'A new revision has been created.'); | ||||
| 
 | ||||
|     // Test deleting the block.
 | ||||
|     $this->drupalGet("admin/content/block/" . $revised_block->id()); | ||||
|     $this->clickLink('Delete'); | ||||
|     $this->assertSession()->pageTextContains('Are you sure you want to delete the content block ' . $revised_block->label() . '?'); | ||||
| 
 | ||||
|     // Test breadcrumb.
 | ||||
|     $trail = [ | ||||
|       '' => 'Home', | ||||
|       'admin/content/block' => 'Content blocks', | ||||
|       'admin/content/block/' . $revised_block->id() => $revised_block->label(), | ||||
|     ]; | ||||
|     $this->assertBreadcrumb( | ||||
|       'admin/content/block/' . $revised_block->id() . '/delete', $trail | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										31
									
								
								tests/src/Functional/Rest/BlockContentJsonAnonTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								tests/src/Functional/Rest/BlockContentJsonAnonTest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | ||||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace Drupal\Tests\block_content\Functional\Rest; | ||||
| 
 | ||||
| use Drupal\Tests\rest\Functional\AnonResourceTestTrait; | ||||
| 
 | ||||
| /** | ||||
|  * @group rest | ||||
|  */ | ||||
| class BlockContentJsonAnonTest extends BlockContentResourceTestBase { | ||||
| 
 | ||||
|   use AnonResourceTestTrait; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected static $format = 'json'; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected static $mimeType = 'application/json'; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected $defaultTheme = 'stark'; | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										41
									
								
								tests/src/Functional/Rest/BlockContentJsonBasicAuthTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								tests/src/Functional/Rest/BlockContentJsonBasicAuthTest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | ||||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace Drupal\Tests\block_content\Functional\Rest; | ||||
| 
 | ||||
| use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait; | ||||
| 
 | ||||
| /** | ||||
|  * @group rest | ||||
|  */ | ||||
| class BlockContentJsonBasicAuthTest extends BlockContentResourceTestBase { | ||||
| 
 | ||||
|   use BasicAuthResourceTestTrait; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected static $modules = ['basic_auth']; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected $defaultTheme = 'stark'; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected static $format = 'json'; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected static $mimeType = 'application/json'; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected static $auth = 'basic_auth'; | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										36
									
								
								tests/src/Functional/Rest/BlockContentJsonCookieTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								tests/src/Functional/Rest/BlockContentJsonCookieTest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | ||||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace Drupal\Tests\block_content\Functional\Rest; | ||||
| 
 | ||||
| use Drupal\Tests\rest\Functional\CookieResourceTestTrait; | ||||
| 
 | ||||
| /** | ||||
|  * @group rest | ||||
|  */ | ||||
| class BlockContentJsonCookieTest extends BlockContentResourceTestBase { | ||||
| 
 | ||||
|   use CookieResourceTestTrait; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected static $format = 'json'; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected static $mimeType = 'application/json'; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected static $auth = 'cookie'; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected $defaultTheme = 'stark'; | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										239
									
								
								tests/src/Functional/Rest/BlockContentResourceTestBase.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										239
									
								
								tests/src/Functional/Rest/BlockContentResourceTestBase.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,239 @@ | ||||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace Drupal\Tests\block_content\Functional\Rest; | ||||
| 
 | ||||
| use Drupal\block_content\Entity\BlockContent; | ||||
| use Drupal\block_content\Entity\BlockContentType; | ||||
| use Drupal\Core\Cache\Cache; | ||||
| use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase; | ||||
| 
 | ||||
| /** | ||||
|  * ResourceTestBase for BlockContent entity. | ||||
|  */ | ||||
| abstract class BlockContentResourceTestBase extends EntityResourceTestBase { | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected static $modules = ['block_content', 'content_translation']; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected static $entityTypeId = 'block_content'; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected static $patchProtectedFieldNames = [ | ||||
|     'changed' => NULL, | ||||
|   ]; | ||||
| 
 | ||||
|   /** | ||||
|    * @var \Drupal\block_content\BlockContentInterface | ||||
|    */ | ||||
|   protected $entity; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function setUpAuthorization($method) { | ||||
|     switch ($method) { | ||||
|       case 'GET': | ||||
|       case 'PATCH': | ||||
|         $this->grantPermissionsToTestedRole(['access block library', 'edit any basic block content']); | ||||
|         break; | ||||
| 
 | ||||
|       case 'POST': | ||||
|         $this->grantPermissionsToTestedRole(['access block library', 'create basic block content']); | ||||
|         break; | ||||
| 
 | ||||
|       case 'DELETE': | ||||
|         $this->grantPermissionsToTestedRole(['delete any basic block content']); | ||||
|         break; | ||||
| 
 | ||||
|       default: | ||||
|         $this->grantPermissionsToTestedRole(['administer block content']); | ||||
|         break; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function createEntity() { | ||||
|     if (!BlockContentType::load('basic')) { | ||||
|       $block_content_type = BlockContentType::create([ | ||||
|         'id' => 'basic', | ||||
|         'label' => 'basic', | ||||
|         'revision' => TRUE, | ||||
|       ]); | ||||
|       $block_content_type->save(); | ||||
|       block_content_add_body_field($block_content_type->id()); | ||||
|     } | ||||
| 
 | ||||
|     // Create a "Llama" content block.
 | ||||
|     $block_content = BlockContent::create([ | ||||
|       'info' => 'Llama', | ||||
|       'type' => 'basic', | ||||
|       'body' => [ | ||||
|         'value' => 'The name "llama" was adopted by European settlers from native Peruvians.', | ||||
|         'format' => 'plain_text', | ||||
|       ], | ||||
|     ]) | ||||
|       ->setUnpublished(); | ||||
|     $block_content->save(); | ||||
|     return $block_content; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function getExpectedNormalizedEntity() { | ||||
|     return [ | ||||
|       'id' => [ | ||||
|         [ | ||||
|           'value' => 1, | ||||
|         ], | ||||
|       ], | ||||
|       'uuid' => [ | ||||
|         [ | ||||
|           'value' => $this->entity->uuid(), | ||||
|         ], | ||||
|       ], | ||||
|       'langcode' => [ | ||||
|         [ | ||||
|           'value' => 'en', | ||||
|         ], | ||||
|       ], | ||||
|       'reusable' => [ | ||||
|         [ | ||||
|           'value' => TRUE, | ||||
|         ], | ||||
|       ], | ||||
|       'type' => [ | ||||
|         [ | ||||
|           'target_id' => 'basic', | ||||
|           'target_type' => 'block_content_type', | ||||
|           'target_uuid' => BlockContentType::load('basic')->uuid(), | ||||
|         ], | ||||
|       ], | ||||
|       'info' => [ | ||||
|         [ | ||||
|           'value' => 'Llama', | ||||
|         ], | ||||
|       ], | ||||
|       'revision_log' => [], | ||||
|       'changed' => [ | ||||
|         [ | ||||
|           'value' => (new \DateTime())->setTimestamp((int) $this->entity->getChangedTime()) | ||||
|             ->setTimezone(new \DateTimeZone('UTC')) | ||||
|             ->format(\DateTime::RFC3339), | ||||
|           'format' => \DateTime::RFC3339, | ||||
|         ], | ||||
|       ], | ||||
|       'revision_id' => [ | ||||
|         [ | ||||
|           'value' => 1, | ||||
|         ], | ||||
|       ], | ||||
|       'revision_created' => [ | ||||
|         [ | ||||
|           'value' => (new \DateTime())->setTimestamp((int) $this->entity->getRevisionCreationTime()) | ||||
|             ->setTimezone(new \DateTimeZone('UTC')) | ||||
|             ->format(\DateTime::RFC3339), | ||||
|           'format' => \DateTime::RFC3339, | ||||
|         ], | ||||
|       ], | ||||
|       'revision_user' => [], | ||||
|       'revision_translation_affected' => [ | ||||
|         [ | ||||
|           'value' => TRUE, | ||||
|         ], | ||||
|       ], | ||||
|       'default_langcode' => [ | ||||
|         [ | ||||
|           'value' => TRUE, | ||||
|         ], | ||||
|       ], | ||||
|       'body' => [ | ||||
|         [ | ||||
|           'value' => 'The name "llama" was adopted by European settlers from native Peruvians.', | ||||
|           'format' => 'plain_text', | ||||
|           'summary' => NULL, | ||||
|           'processed' => "<p>The name "llama" was adopted by European settlers from native Peruvians.</p>\n", | ||||
|         ], | ||||
|       ], | ||||
|       'status' => [ | ||||
|         [ | ||||
|           'value' => FALSE, | ||||
|         ], | ||||
|       ], | ||||
|     ]; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function getNormalizedPostEntity() { | ||||
|     return [ | ||||
|       'type' => [ | ||||
|         [ | ||||
|           'target_id' => 'basic', | ||||
|         ], | ||||
|       ], | ||||
|       'info' => [ | ||||
|         [ | ||||
|           'value' => 'Drama llama', | ||||
|         ], | ||||
|       ], | ||||
|     ]; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function getExpectedUnauthorizedAccessMessage($method) { | ||||
|     if (!$this->resourceConfigStorage->load(static::$resourceConfigId)) { | ||||
|       return match ($method) { | ||||
|         'GET', 'PATCH' => "The 'edit any basic block content' permission is required.", | ||||
|         'POST' => "The following permissions are required: 'create basic block content' AND 'access block library'.", | ||||
|         'DELETE' => "The 'delete any basic block content' permission is required.", | ||||
|         default => parent::getExpectedUnauthorizedAccessMessage($method), | ||||
|       }; | ||||
|     } | ||||
|     return match ($method) { | ||||
|       'GET' => "The 'access block library' permission is required.", | ||||
|       'PATCH' => "The 'edit any basic block content' permission is required.", | ||||
|       'POST' => "The following permissions are required: 'create basic block content' AND 'access block library'.", | ||||
|       'DELETE' => "The 'delete any basic block content' permission is required.", | ||||
|       default => parent::getExpectedUnauthorizedAccessMessage($method), | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function getExpectedUnauthorizedEntityAccessCacheability($is_authenticated) { | ||||
|     // @see \Drupal\block_content\BlockContentAccessControlHandler()
 | ||||
|     return parent::getExpectedUnauthorizedEntityAccessCacheability($is_authenticated) | ||||
|       ->addCacheTags(['block_content:1']); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function getExpectedCacheTags() { | ||||
|     return Cache::mergeTags(parent::getExpectedCacheTags(), ['config:filter.format.plain_text']); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected function getExpectedCacheContexts() { | ||||
|     return Cache::mergeContexts(['url.site'], $this->container->getParameter('renderer.config')['required_cache_contexts']); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										31
									
								
								tests/src/Functional/Rest/BlockContentTypeJsonAnonTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								tests/src/Functional/Rest/BlockContentTypeJsonAnonTest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | ||||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace Drupal\Tests\block_content\Functional\Rest; | ||||
| 
 | ||||
| use Drupal\Tests\rest\Functional\AnonResourceTestTrait; | ||||
| 
 | ||||
| /** | ||||
|  * @group rest | ||||
|  */ | ||||
| class BlockContentTypeJsonAnonTest extends BlockContentTypeResourceTestBase { | ||||
| 
 | ||||
|   use AnonResourceTestTrait; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected static $format = 'json'; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected static $mimeType = 'application/json'; | ||||
| 
 | ||||
|   /** | ||||
|    * {@inheritdoc} | ||||
|    */ | ||||
|   protected $defaultTheme = 'stark'; | ||||
| 
 | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Dan Chadwick
						Dan Chadwick