Commit ccd5a872 authored by tedbow's avatar tedbow Committed by marcvangend

Issue #2513246 by tedbow, marcvangend, casey: New approach for fields as block, second iteration

parent 5e49de6f
......@@ -2,3 +2,6 @@ name: Field as Block
description: 'Display fields of the current entity as blocks.'
type: module
core: 8.x
dependencies:
- block
configure: fieldblock.field_block_config_form
fieldblock.settings:
title: 'Field as Block settings'
parent: system.admin_config_system
description: 'Set Entity Types available as field blocks.'
route_name: fieldblock.field_block_config_form
weight: 50
fieldblock.field_block_config_form:
path: '/admin/config/fieldblock/fieldblockconfig'
defaults:
_form: '\Drupal\fieldblock\Form\FieldBlockConfigForm'
_title: 'Field as Block settings'
requirements:
_permission: 'access administration pages'
services:
fieldblock.block_storage:
class: Drupal\fieldblock\BlockEntityStorage
arguments: ["@config.factory", "@uuid", "@language_manager", "@entity.manager"]
......@@ -16,3 +16,13 @@ block.settings.fieldblock:*:
formatter_settings:
type: field.formatter.settings.[%parent.formatter_id]
label: 'Settings'
fieldblock.settings:
type: config_object
label: 'Field Block settings'
mapping:
enabled_entity_types:
type: sequence
label: 'Entity types to create for which to create Field Blocks.'
sequence:
type: string
label: 'Entity type'
<?php
/**
* @file
* Contains Drupal\fieldblock\BlockEntityStorage.
*/
namespace Drupal\fieldblock;
use Drupal\Core\Config\ConfigFactory;
use Drupal\Component\Uuid\Php;
use Drupal\Core\Config\Entity\ConfigEntityStorage;
use Drupal\Core\Language\LanguageManager;
use Drupal\Core\Entity\EntityManager;
/**
* Class BlockEntityStorage.
*
* @package Drupal\fieldblock
*/
class BlockEntityStorage extends ConfigEntityStorage {
/**
* Drupal\Core\Config\ConfigFactory definition.
*
* @var \Drupal\Core\Config\ConfigFactory
*/
protected $config_factory;
/**
* Drupal\Component\Uuid\Php definition.
*
* @var \Drupal\Component\Uuid\Php
*/
protected $uuid;
/**
* Drupal\Core\Language\LanguageManager definition.
*
* @var \Drupal\Core\Language\LanguageManager
*/
protected $language_manager;
/**
* Drupal\Core\Entity\EntityManager definition.
*
* @var \Drupal\Core\Entity\EntityManager
*/
protected $entity_manager;
/**
* Constructor.
*/
public function __construct(ConfigFactory $config_factory, Php $uuid, LanguageManager $language_manager, EntityManager $entity_manager) {
$this->config_factory = $config_factory;
$this->uuid = $uuid;
$this->language_manager = $language_manager;
$this->entity_manager = $entity_manager;
$entity_type = $entity_manager->getDefinition('block');
parent::__construct($entity_type, $config_factory, $uuid, $language_manager);
}
/**
* Load all blocks provided by this module.
*
* @return array|\Drupal\Core\Entity\EntityInterface[]
*/
public function loadFieldBlocks() {
// Build a query to fetch the entity IDs.
$entity_query = $this->getQuery();
$entity_query->condition('plugin', 'fieldblock:', 'STARTS_WITH');
$result = $entity_query->execute();
return $result ? $this->loadMultiple($result) : array();
}
/**
* Get all entity type ids that are currently used in Field Blocks.
*
* This will also return entity type ids for entities that no longer available.
* @return array
*/
public function getEntityTypesUsed() {
/** @var BlockEntityStorage $block_storage */
$blocks = $this->loadFieldBlocks();
$entity_types = [];
/** @var \Drupal\block\Entity\Block $block */
foreach ($blocks as $block) {
$plugin_parts = explode(':',$block->get('plugin'));
$entity_types[] = $plugin_parts['1'];
}
return $entity_types;
}
/**
* Delete all blocks for an entity type.
*
* @param $entity_type
*/
public function deleteBlocksForEntityType($entity_type) {
$blocks = $this->loadByProperties(['plugin' => "fieldblock:$entity_type"]);
$this->delete($blocks);
}
}
<?php
/**
* @file
* Contains Drupal\fieldblock\Controller\FieldBlockController.
*/
namespace Drupal\fieldblock\Controller;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Entity\ContentEntityTypeInterface;
use Drupal\Core\Entity\EntityTypeInterface;
/**
* Class FieldBlockController.
*
* @package Drupal\fieldblock\Controller
*/
class FieldBlockController extends ControllerBase {
/**
* Get currently enabled types either from config or module defaults.
*
* @return array
* Entity type ids.
*/
public function getEnabledEntityTypes() {
$entity_types = $this->config('fieldblock.settings')->get('enabled_entity_types');
if (!$entity_types) {
return $this->getDefaultEntityTypes();
}
return array_filter($entity_types);
}
/**
* Determine if Entity Type should have field block created.
* @param \Drupal\Core\Entity\EntityTypeInterface $type
*
* @return bool
*/
public function isBlockableEntityType(EntityTypeInterface $type) {
static $entity_types;
if (!$entity_types) {
$entity_types = $this->getEnabledEntityTypes();
}
return in_array($type->id(), $entity_types);
}
/**
* Return default entity types to use as blocks.
*
* @return array
* Entity type ids.
*/
protected function getDefaultEntityTypes() {
$default_types = ['node', 'user', 'taxonomy_term'];
// @todo Should there by an alter hook to allow other modules to make their entities default?
$all_types = array_keys($this->entityManager()->getDefinitions());
// Return all default types that actually exist. "taxonomy_term" at least could be disabled.
return array_intersect($default_types, $all_types);
}
/**
* Determine if a Entity is compatible with Field Blocks.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
*
* @return bool
*/
public function isFieldBlockCompatible(EntityTypeInterface $entity_type) {
return $entity_type instanceof ContentEntityTypeInterface;
}
}
<?php
/**
* @file
* Contains Drupal\fieldblock\Form\FieldBlockConfigForm.
*/
namespace Drupal\fieldblock\Form;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Entity\EntityManager;
use Drupal\fieldblock\Controller\FieldBlockController;
use Drupal\Core\Entity\ContentEntityTypeInterface;
use Drupal\fieldblock\BlockEntityStorage;
/**
* Configuration for select Entity types and delete blocks of unused types.
*/
class FieldBlockConfigForm extends ConfigFormBase {
/**
* Drupal\Core\Entity\EntityManager definition.
*
* @var \Drupal\Core\Entity\EntityManager
*/
protected $entityManager;
/**
* @var \Drupal\fieldblock\BlockEntityStorage
*/
protected $storage;
/**
* @var \Drupal\fieldblock\Controller\FieldBlockController;
*/
protected $fieldblock_controller;
/**
* Constructs a \Drupal\fieldblock\Form\FieldBlockConfigForm object.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* @param \Drupal\Core\Entity\EntityManager $entity_manager
* @param \Drupal\fieldblock\BlockEntityStorage $storage
*/
public function __construct(ConfigFactoryInterface $config_factory, EntityManager $entity_manager, BlockEntityStorage $storage) {
parent::__construct($config_factory);
$this->entityManager = $entity_manager;
$this->storage = $storage;
$this->fieldblock_controller = new FieldBlockController();
}
public static function create(ContainerInterface $container) {
return new static(
$container->get('config.factory'),
$container->get('entity.manager'),
$container->get('fieldblock.block_storage')
);
}
/**
* {@inheritdoc}
*/
protected function getEditableConfigNames() {
return [
'fieldblock.settings'
];
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'field_block_config_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$enabled = $this->fieldblock_controller->getEnabledEntityTypes();
$form['enabled_entity_types'] = array(
'#type' => 'checkboxes',
'#title' => $this->t('Enable Entity Types'),
'#options' => $this->getEntityTypeLabels(),
'#description' => $this->t('Select the Entity Types to expose as field blocks.'),
'#default_value' => $enabled,
);
$orphaned_types = $this->getOrphanedEntityTypes($enabled);
$cleanup_options = [];
$entity_type_defs = $this->entityManager->getDefinitions();
foreach ($orphaned_types as $entity_type) {
if (isset($entity_type_defs[$entity_type])) {
// This entity type still exists on the site
$cleanup_options[$entity_type] = $entity_type_defs[$entity_type]->getLabel();
}
else {
// This entity type no longer exists on the site.
$cleanup_options[$entity_type] = $this->t('Missing entity type') . ': ' . $entity_type;
}
}
if (!empty($cleanup_options)) {
$form['cleanup'] = [
'#type' => 'checkboxes',
'#required' => FALSE,
'#title' => t('Clean up remaining field blocks of removed entity types'),
'#description' => t('These entity types no longer exist, but one or more of their field blocks still do. Select the entity type(s) of which the field block(s) must be removed.'),
'#default_value' => [],
'#options' => $cleanup_options,
];
}
return parent::buildForm($form, $form_state);
}
protected function getAllEntityTypes() {
return array_keys($this->entityManager->getDefinitions());
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
parent::validateForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
parent::submitForm($form, $form_state);
$clear_cache = FALSE;
$previous_entity_types = $this->fieldblock_controller->getEnabledEntityTypes();
$new_entity_types = $form_state->getValue('enabled_entity_types');
if ($previous_entity_types != $new_entity_types) {
$clear_cache = TRUE;
}
// Remove all blocks for entity types that are no longer enabled.
if ($cleanup = $form_state->getValue('cleanup')) {
foreach ($cleanup as $entity_type => $value) {
if ($value !== 0) {
// Find and delete the remaining blocks for this entity type.
$this->storage->deleteBlocksForEntityType($entity_type);
$clear_cache = TRUE;
drupal_set_message($this->t('Remaining field blocks for the %type entity have been deleted.', ['%type' => $entity_type]));
}
else {
if (in_array($entity_type, $this->getAllEntityTypes())) {
// Keep the entity type in the settings if it still exists.
$new_entity_types[$entity_type] = $entity_type;
}
}
}
}
$this->config('fieldblock.settings')
->set('enabled_entity_types', $new_entity_types)
->save();
if ($clear_cache) {
// Invalidate the block cache to update fieldblock derivatives.
if (\Drupal::moduleHandler()->moduleExists('block')) {
\Drupal::service('plugin.manager.block')->clearCachedDefinitions();
}
}
}
/**
* Get Entity Type labels for all compatible Entity Types.
*
* @return array
*/
protected function getEntityTypeLabels() {
$definitions = $this->entityManager->getDefinitions();
$labels = [];
/** @var \Drupal\Core\Entity\EntityTypeInterface $definition */
foreach ($definitions as $definition) {
if ($this->fieldblock_controller->isFieldBlockCompatible($definition)) {
$labels[$definition->id()] = $definition->getLabel();
}
}
return $labels;
}
/**
* Get all entity types that have Field Blocks but are either:
* 1. No longer set to be used with this module
* 2. Don't exist on the site.
* @return array
* Entity type ids.
*/
protected function getOrphanedEntityTypes($enabled_entity_types) {
$orphaned_types = [];
$entity_types_used = $this->storage->getEntityTypesUsed();
$all_entity_types = $this->getAllEntityTypes();
foreach ($entity_types_used as $used_type) {
if (!in_array($used_type, $all_entity_types) || !in_array($used_type, $enabled_entity_types)) {
$orphaned_types[] = $used_type;
}
}
return $orphaned_types;
}
}
......@@ -8,11 +8,11 @@
namespace Drupal\fieldblock\Plugin\Derivative;
use Drupal\Component\Plugin\Derivative\DeriverBase;
use Drupal\Core\Entity\ContentEntityTypeInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\fieldblock\Controller\FieldBlockController;
/**
* Provides block plugin definitions for fieldblock blocks.
......@@ -53,10 +53,11 @@ class FieldBlockDeriver extends DeriverBase implements ContainerDeriverInterface
* {@inheritdoc}
*/
public function getDerivativeDefinitions($base_plugin_definition) {
$fb_controller = new FieldBlockController();
$definitions = $this->entityManager->getDefinitions();
foreach ($definitions as $entity_type_id => $definition) {
if ($definition instanceof ContentEntityTypeInterface) {
if ($fb_controller->isBlockableEntityType($definition)) {
$this->derivatives[$entity_type_id] = $base_plugin_definition;
$this->derivatives[$entity_type_id]['admin_label'] = $this->t('@type field', [
'@type' => $definition->getLabel(),
......@@ -66,4 +67,5 @@ class FieldBlockDeriver extends DeriverBase implements ContainerDeriverInterface
return $this->derivatives;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment