Chris@0: moduleExists('field_ui') ? Url::fromRoute('help.page', ['name' => 'field_ui'])->toString() : '#'; Chris@0: $output = ''; Chris@0: $output .= '

' . t('About') . '

'; Chris@18: $output .= '

' . t('The Custom Block module allows you to create and manage custom block types and content-containing blocks from the Custom block library page. Custom block types have fields; see the Field module help for more information. Once created, custom blocks can be placed in regions just like blocks provided by other modules; see the Block module help page for details. For more information, see the online documentation for the Custom Block module.', [':block-library' => Url::fromRoute('entity.block_content.collection')->toString(), ':block-content' => Url::fromRoute('entity.block_content.collection')->toString(), ':field-help' => Url::fromRoute('help.page', ['name' => 'field'])->toString(), ':blocks' => Url::fromRoute('help.page', ['name' => 'block'])->toString(), ':online-help' => 'https://www.drupal.org/documentation/modules/block_content']) . '

'; Chris@0: $output .= '

' . t('Uses') . '

'; Chris@0: $output .= '
'; Chris@0: $output .= '
' . t('Creating and managing custom block types') . '
'; Chris@18: $output .= '
' . t('Users with the Administer blocks permission can create and edit custom block types with fields and display settings, from the Block types page in the Custom block library. For more information about managing fields and display settings, see the Field UI module help.', [':types' => Url::fromRoute('entity.block_content_type.collection')->toString(), ':field-ui' => $field_ui]) . '
'; Chris@0: $output .= '
' . t('Creating custom blocks') . '
'; Chris@18: $output .= '
' . t('Users with the Administer blocks permission can create, edit, and delete custom blocks of each defined custom block type, from the Blocks page in the Custom block library. After creating a block, place it in a region from the Block layout page; see the Block module help for more information about placing blocks.', [':blocks' => Url::fromRoute('block.admin_display')->toString(), ':block-library' => Url::fromRoute('entity.block_content.collection')->toString(), ':block_help' => Url::fromRoute('help.page', ['name' => 'block'])->toString()]) . '
'; Chris@0: $output .= '
'; Chris@0: return $output; Chris@0: Chris@0: case 'entity.block_content.collection': Chris@18: $output = '

' . t('Blocks in the block library belong to Custom block types, each with its own fields and display settings. After creating a block, place it in a region from the Block layout page.', [':types' => Url::fromRoute('entity.block_content_type.collection')->toString(), ':blocks' => Url::fromRoute('block.admin_display')->toString()]) . '

'; Chris@0: return $output; Chris@0: Chris@0: case 'entity.block_content_type.collection': Chris@18: $output = '

' . t('Each block type has its own fields and display settings. Create blocks of each type on the Blocks page in the custom block library.', [':block-library' => Url::fromRoute('entity.block_content.collection')->toString()]) . '

'; Chris@0: return $output; Chris@0: Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Implements hook_theme(). Chris@0: */ Chris@0: function block_content_theme($existing, $type, $theme, $path) { Chris@0: return [ Chris@0: 'block_content_add_list' => [ Chris@0: 'variables' => ['content' => NULL], Chris@0: 'file' => 'block_content.pages.inc', Chris@0: ], Chris@0: ]; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Implements hook_entity_type_alter(). Chris@0: */ Chris@0: function block_content_entity_type_alter(array &$entity_types) { Chris@0: /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */ Chris@0: // Add a translation handler for fields if the language module is enabled. Chris@0: if (\Drupal::moduleHandler()->moduleExists('language')) { Chris@0: $translation = $entity_types['block_content']->get('translation'); Chris@0: $translation['block_content'] = TRUE; Chris@0: $entity_types['block_content']->set('translation', $translation); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Adds the default body field to a custom block type. Chris@0: * Chris@0: * @param string $block_type_id Chris@0: * Id of the block type. Chris@0: * @param string $label Chris@0: * (optional) The label for the body instance. Defaults to 'Body' Chris@0: * Chris@0: * @return \Drupal\field\Entity\FieldConfig Chris@0: * A Body field object. Chris@0: */ Chris@0: function block_content_add_body_field($block_type_id, $label = 'Body') { Chris@0: // Add or remove the body field, as needed. Chris@0: $field = FieldConfig::loadByName('block_content', $block_type_id, 'body'); Chris@0: if (empty($field)) { Chris@0: $field = FieldConfig::create([ Chris@0: 'field_storage' => FieldStorageConfig::loadByName('block_content', 'body'), Chris@0: 'bundle' => $block_type_id, Chris@0: 'label' => $label, Chris@0: 'settings' => ['display_summary' => FALSE], Chris@0: ]); Chris@0: $field->save(); Chris@0: Chris@0: // Assign widget settings for the 'default' form mode. Chris@0: entity_get_form_display('block_content', $block_type_id, 'default') Chris@0: ->setComponent('body', [ Chris@0: 'type' => 'text_textarea_with_summary', Chris@0: ]) Chris@0: ->save(); Chris@0: Chris@0: // Assign display settings for 'default' view mode. Chris@0: entity_get_display('block_content', $block_type_id, 'default') Chris@0: ->setComponent('body', [ Chris@0: 'label' => 'hidden', Chris@0: 'type' => 'text_default', Chris@0: ]) Chris@0: ->save(); Chris@0: } Chris@0: Chris@0: return $field; Chris@0: } Chris@17: Chris@17: /** Chris@17: * Implements hook_query_TAG_alter(). Chris@17: * Chris@17: * Alters any 'entity_reference' query where the entity type is Chris@17: * 'block_content' and the query has the tag 'block_content_access'. Chris@17: * Chris@17: * These queries should only return reusable blocks unless a condition on Chris@17: * 'reusable' is explicitly set. Chris@17: * Chris@17: * Block_content entities that are reusable should by default not be selectable Chris@17: * as entity reference values. A module can still create an instance of Chris@17: * \Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface Chris@17: * that will allow selection of non-reusable blocks by explicitly setting Chris@17: * a condition on the 'reusable' field. Chris@17: * Chris@17: * @see \Drupal\block_content\BlockContentAccessControlHandler Chris@17: */ Chris@17: function block_content_query_entity_reference_alter(AlterableInterface $query) { Chris@17: if ($query instanceof SelectInterface && $query->getMetaData('entity_type') === 'block_content' && $query->hasTag('block_content_access')) { Chris@17: $data_table = \Drupal::entityTypeManager()->getDefinition('block_content')->getDataTable(); Chris@17: if (array_key_exists($data_table, $query->getTables()) && !_block_content_has_reusable_condition($query->conditions(), $query->getTables())) { Chris@17: $query->condition("$data_table.reusable", TRUE); Chris@17: } Chris@17: } Chris@17: } Chris@17: Chris@17: /** Chris@17: * Utility function to find nested conditions using the reusable field. Chris@17: * Chris@17: * @todo Replace this function with a call to the API in Chris@17: * https://www.drupal.org/project/drupal/issues/2984930 Chris@17: * Chris@17: * @param array $condition Chris@17: * The condition or condition group to check. Chris@17: * @param array $tables Chris@17: * The tables from the related select query. Chris@17: * Chris@17: * @see \Drupal\Core\Database\Query\SelectInterface::getTables Chris@17: * Chris@17: * @return bool Chris@17: * Whether the conditions contain any condition using the reusable field. Chris@17: */ Chris@17: function _block_content_has_reusable_condition(array $condition, array $tables) { Chris@17: // If this is a condition group call this function recursively for each nested Chris@17: // condition until a condition is found that return TRUE. Chris@17: if (isset($condition['#conjunction'])) { Chris@17: foreach (array_filter($condition, 'is_array') as $nested_condition) { Chris@17: if (_block_content_has_reusable_condition($nested_condition, $tables)) { Chris@17: return TRUE; Chris@17: } Chris@17: } Chris@17: return FALSE; Chris@17: } Chris@17: if (isset($condition['field'])) { Chris@17: $field = $condition['field']; Chris@17: if (is_object($field) && $field instanceof ConditionInterface) { Chris@17: return _block_content_has_reusable_condition($field->conditions(), $tables); Chris@17: } Chris@17: $field_parts = explode('.', $field); Chris@17: $data_table = \Drupal::entityTypeManager()->getDefinition('block_content')->getDataTable(); Chris@17: foreach ($tables as $table) { Chris@17: if ($table['table'] === $data_table && $field_parts[0] === $table['alias'] && $field_parts[1] === 'reusable') { Chris@17: return TRUE; Chris@17: } Chris@17: } Chris@17: Chris@17: } Chris@17: return FALSE; Chris@17: }