Skip to content
Snippets Groups Projects
Commit 9101ae8f authored by Kevin Paxman's avatar Kevin Paxman
Browse files

Merge branch 'feature/ibiki-watspeed' into '1.0.x'

Feature/ibiki watspeed

See merge request !207
parents 099a8018 0671b1dd
No related branches found
No related tags found
1 merge request!207Feature/ibiki watspeed
services:
uw_cfg_common.commands:
class: Drupal\uw_cfg_common\Commands\UwDrushCommands
arguments: ['@entity_type.manager']
arguments: ['@entity_type.manager', '@uw_cfg_common.missing_blocks']
tags:
- { name: drush.command }
......@@ -3,8 +3,10 @@
namespace Drupal\uw_cfg_common\Commands;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\uw_cfg_common\Service\UWMissingBlocks;
use Drupal\uw_cfg_common\UwRoles\UwRoles;
use Drush\Commands\DrushCommands;
use Drush\Utils\StringUtils;
/**
* Drush commands for uw_cfg_common module.
......@@ -20,11 +22,19 @@ class UwDrushCommands extends DrushCommands {
*/
protected $entityTypeManager;
/**
* Service to deal with missing blocks.
*
* @var \Drupal\uw_cfg_common\Service\UWMissingBlocks
*/
protected $missingBlocks;
/**
* {@inheritDoc}
*/
public function __construct(EntityTypeManagerInterface $entityTypeManager) {
public function __construct(EntityTypeManagerInterface $entityTypeManager, UWMissingBlocks $missingBlocks) {
$this->entityTypeManager = $entityTypeManager;
$this->missingBlocks = $missingBlocks;
}
/**
......@@ -61,4 +71,100 @@ class UwDrushCommands extends DrushCommands {
$this->logger()->success('All permissions set.');
}
/**
* Removes missing blocks from unsaved layout.
*
* If not existing block is in temp storage for layout build (unsaved)
* changes and layout tab is opened this can result in server error. This
* function will check each block existence (loading block revision) and
* if block is loaded successfully it will leave it in temp storage.
* Otherwise, block will be removed from unsaved changes.
*
* @param string|null $nids
* List (CSV) of nodes to check, if empty command will check all nodes.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*
* @command uw:missing-blocks-unsaved
* @aliases uwblocksunsaved
* @usage drush uwblocksunsaved "102,201,304"
* - Checks nodes with id 102,201,304 and remove not existing blocks.
*/
public function removeBlocksFromUnsavedLayout(string $nids = NULL): void {
$nodes = NULL;
if ($nids) {
$nodes = StringUtils::csvToArray($nids);
}
$this->missingBlocks->removeMissingBlocksFromUnsaved($nodes);
$this->logger()->success('Missing blocks checks complete on unsaved.');
}
/**
* Removes missing blocks from saved layouts.
*
* Scans all nodes, checks each for missing blocks in layout builder.
* Removes missing blocks if found, and saves node. Checks revisions also.
*
* @param string|null $nids
* List (CVS) of nodes to check, if empty command will check all nodes.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
* @throws \Drupal\Core\Entity\EntityStorageException
*
* @command uw:missing-blocks-saved
* @aliases uwblockssaved
* @usage drush uwblockssaved 102
* - Checks node (nid: 102) for any missing blocks.
* @usage drush uwblockssaved "103,104,105,106"
* - Checks nodes 103, 104, 105, 106 for missing blocks.
*/
public function removeBlockFromLayout(string $nids = NULL): void {
$nodes = NULL;
if ($nids) {
$nodes = StringUtils::csvToArray($nids);
}
$this->missingBlocks->removeMissingBlocksFromSaved($nodes);
$this->logger()->success('Missing blocks checks complete on saved.');
}
/**
* Command to combine saved and unsaved removal of missing blocks.
*
* @param string|null $nids
* List of node ids to process only.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
* @throws \Drupal\Core\Entity\EntityStorageException
*
* @command uw:missing-blocks
* @aliases uwblocks
* @usage drush uwblocks
* - Run checkup on all nodes. This may take some time to complete.
* @usage drush uwblocks 102
* - Checks node (nid: 12) for any missing blocks(covers saved and unsaved).
* @usage drush uwblocks "103,104,105,106"
* - Checks nodes 103, 104, 105, 106 for missing blocks (saved and unsaved).
*/
public function removeMissingBlocks(string $nids = NULL): void {
$nodes = NULL;
if ($nids) {
$nodes = StringUtils::csvToArray($nids);
}
$this->logger()->notice('Running clean up for saved nodes.');
$this->missingBlocks->removeMissingBlocksFromSaved($nodes);
$this->logger()->notice('Running clean up for unsaved nodes.');
$this->missingBlocks->removeMissingBlocksFromUnsaved($nodes);
$this->logger()->success('Missing blocks removed from saved and unsaved layouts.');
}
}
<?php
namespace Drupal\uw_cfg_common\Service;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\KeyValueStore\KeyValueExpirableFactory;
use Drupal\layout_builder\LayoutTempstoreRepositoryInterface;
/**
* Service to fix missing blocks in saved and unsaved nodes.
*/
class UWMissingBlocks {
/**
* Entity type manager from core.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* Key value expirable service from core.
*
* @var \Drupal\Core\KeyValueStore\KeyValueExpirableFactory
*/
protected $keyValue;
/**
* Layout builder temporary repository service from core.
*
* @var \Drupal\layout_builder\LayoutTempstoreRepositoryInterface
*/
protected $temporaryRepository;
/**
* Default class constructor.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
* Entity type manager service from core.
* @param \Drupal\Core\KeyValueStore\KeyValueExpirableFactory $keyValue
* Key value expirable service from core.
* @param \Drupal\layout_builder\LayoutTempstoreRepositoryInterface $temporaryRepository
* Layout builder temporary repository service from core.
*/
public function __construct(EntityTypeManagerInterface $entityTypeManager, KeyValueExpirableFactory $keyValue, LayoutTempstoreRepositoryInterface $temporaryRepository) {
$this->entityTypeManager = $entityTypeManager;
$this->keyValue = $keyValue;
$this->temporaryRepository = $temporaryRepository;
}
/**
* Checks nodes for missing blocks in unsaved changes.
*
* It will load only nodes that have unsaved changes, based on result from
* temporary repository table (key_value_expire) with name
* (tempstore.shared.layout_builder.section_storage.overrides).
*
* @param array|null $nodes
* List of nodes to check, defaults to every node.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
public function removeMissingBlocksFromUnsaved(array $nodes = NULL): void {
$temp_nodes = $this->keyValue->get('tempstore.shared.layout_builder.section_storage.overrides')->getAll();
foreach ($temp_nodes as $override_id => $storage) {
$ids = explode('.', $override_id);
if ((!empty($nodes) && in_array($ids[1], $nodes)) || empty($nodes)) {
$layout = $storage->data['section_storage'] ?? NULL;
if ($this->removeMissingBlocksProcessLayout($layout)) {
$storage->data['section_storage'] = $layout;
$this->keyValue->get('tempstore.shared.layout_builder.section_storage.overrides')->set($override_id, $storage);
}
}
}
}
/**
* Checks saved nodes for missing blocks (includes revisions).
*
* @param array|null $only_nodes
* List of nodes to check. If omitted it will load all blocks and perform
* checks on them. This may be timely process.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
* @throws \Drupal\Core\Entity\EntityStorageException
*/
public function removeMissingBlocksFromSaved(array $only_nodes = NULL): void {
// Loading either all nodes if only_nodes is empty, or just nodes that
// are passed as arguments. Non-existing node ids will not be loaded.
$nodes = $this->entityTypeManager->getStorage('node')->loadMultiple($only_nodes);
foreach ($nodes as $node) {
$layout = $node->get('layout_builder__layout');
if ($this->removeMissingBlocksProcessLayout($layout)) {
$node->save();
}
// Revisions.
$vids = $this->entityTypeManager->getStorage('node')->revisionIds($node);
foreach ($vids as $vid) {
$revision_node = $this->entityTypeManager->getStorage('node')->loadRevision($vid);
$layout = $revision_node->get('layout_builder__layout');
if ($this->removeMissingBlocksProcessLayout($layout)) {
$revision_node->save();
}
}
}
}
/**
* Checks layout for missing blocks.
*
* @param \Drupal\layout_builder\Field\LayoutSectionItemList|\Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage $layout
* Layout to check.
*
* @return bool
* Were there any changes that would require save on entity.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
private function removeMissingBlocksProcessLayout($layout): bool {
$save_required = FALSE;
$sections = $layout->getSections();
foreach ($sections as $section) {
$components = $section->getComponents();
foreach ($components as $component) {
$config = $component->get('configuration');
if (!empty($config['block_revision_id'])) {
$block_revision = $this->entityTypeManager->getStorage('block_content')
->loadRevision($config['block_revision_id']);
if (!$block_revision) {
$section->removeComponent($component->getUuid());
$save_required = TRUE;
}
}
}
}
return $save_required;
}
}
......@@ -588,3 +588,12 @@ function uw_cfg_common_update_9103() {
'label' => 'Private content viewer',
])->save();
}
/**
* Removes missing blocks from temp storage and saved nodes (revisions too).
*/
function uw_cfg_common_update_9104(&$sandbox) {
$service = \Drupal::service('uw_cfg_common.missing_blocks');
$service->removeMissingBlocksFromUnsaved();
$service->removeMissingBlocksFromSaved();
}
......@@ -32,3 +32,6 @@ services:
uw_cfg_common.features:
class: Drupal\uw_cfg_common\Helpers\UwFeatureRevert
arguments: ['@features.manager', '@config_update.config_update']
uw_cfg_common.missing_blocks:
class: Drupal\uw_cfg_common\Service\UWMissingBlocks
arguments: ['@entity_type.manager', '@keyvalue.expirable', '@layout_builder.tempstore_repository']
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment