You are here

class LayoutBuilderLock in Layout Builder Lock 8

Hierarchy

Expanded class hierarchy of LayoutBuilderLock

3 files declare their use of LayoutBuilderLock
LayoutBuilderLockAccessCheck.php in src/Access/LayoutBuilderLockAccessCheck.php
LayoutBuilderLockTest.php in tests/src/Functional/LayoutBuilderLockTest.php
layout_builder_lock.module in ./layout_builder_lock.module
Layout Builder Lock module.

File

src/LayoutBuilderLock.php, line 9

Namespace

Drupal\layout_builder_lock
View source
class LayoutBuilderLock implements TrustedCallbackInterface {
  const NO_LOCK = [];
  const LOCKED_BLOCK_UPDATE = 1;
  const LOCKED_BLOCK_DELETE = 2;
  const LOCKED_BLOCK_MOVE = 3;
  const LOCKED_BLOCK_ADD = 4;
  const LOCKED_SECTION_CONFIGURE = 5;
  const LOCKED_SECTION_BEFORE = 6;
  const LOCKED_SECTION_AFTER = 7;
  const LOCKED_SECTION_BLOCK_MOVE = 8;

  /**
   * Returns whether the current user can manage lock settings on default.
   *
   * @return bool
   */
  protected static function hasManageLockDefaultPermission() {
    return \Drupal::currentUser()
      ->hasPermission('manage lock settings on sections');
  }

  /**
   * Returns whether the current user can manage lock settings on overrides.
   *
   * @return bool
   */
  protected static function hasManageLockOverridesPermission() {
    return \Drupal::currentUser()
      ->hasPermission('manage lock settings on overrides');
  }

  /**
   * Returns whether the current user can bypass lock settings on overrides.
   *
   * @return bool
   */
  protected static function hasBypassLockSettingsPermission() {
    return \Drupal::currentUser()
      ->hasPermission('bypass lock settings on layout overrides');
  }

  /**
   * Applies lock settings to the Layout Builder element.
   *
   * @param array $element
   *   The Layout Builder render element.
   *
   * @return array
   *   The modified Layout Builder render element.
   */
  public static function preRender(array $element) {

    // Attach library.
    $element['#attached']['library'][] = 'layout_builder_lock/edit';

    // Determine if this section is overridden.
    $overridden = FALSE;

    /** @var \Drupal\layout_builder\SectionStorageInterface $section_storage */
    $section_storage = $element['#section_storage'];
    if ($section_storage instanceof OverridesSectionStorageInterface && $section_storage
      ->isOverridden()) {
      $overridden = TRUE;
    }

    // Users who can bypass the lock sections can do everything on overrides.
    if ($overridden && self::hasBypassLockSettingsPermission()) {
      return $element;
    }

    // Users who can manage the lock settings on default can do everything.
    if (!$overridden && self::hasManageLockDefaultPermission()) {
      return $element;
    }
    $section_number = 0;
    for ($i = 0; $i < $section_storage
      ->count(); $i++) {

      // Get settings, continue if empty.
      $settings = [];
      try {
        $settings = array_filter($section_storage
          ->getSection($i)
          ->getThirdPartySetting('layout_builder_lock', 'lock', self::NO_LOCK));
      } catch (\OutOfBoundsException $ignored) {
      }
      if (empty($settings)) {
        continue;
      }
      $default_components = [];
      try {
        if ($section_storage instanceof OverridesSectionStorageInterface) {
          $default_components = $section_storage
            ->getDefaultSectionStorage()
            ->getSection($i)
            ->getComponents();
        }
        else {
          $default_components = $section_storage
            ->getSection($i)
            ->getComponents();
        }
      } catch (\OutOfBoundsException $ignored) {
      }

      // Calculate the section number, taking into account the 'add section'
      // links.
      $section_number = $i * 2 + 1;

      // Ignore non existing section numbers.
      if (!isset($element['layout_builder'][$section_number])) {
        continue;
      }
      foreach ($element['layout_builder'][$section_number] as $name => $item) {

        // Add new section link before.
        if (isset($settings[self::LOCKED_SECTION_BEFORE])) {
          unset($element['layout_builder'][$section_number - 1]);
        }

        // Add new section link after.
        if (isset($settings[self::LOCKED_SECTION_AFTER])) {
          unset($element['layout_builder'][$section_number + 1]);
        }

        // Remove or configure section links.
        if (isset($item['#url'])) {

          // Sections can not be deleted at all.
          if ($name == 'remove') {
            unset($element['layout_builder'][$section_number][$name]);
          }

          // Configure section link.
          if ($name == 'configure' && isset($settings[self::LOCKED_SECTION_CONFIGURE]) && !self::hasManageLockOverridesPermission()) {
            unset($element['layout_builder'][$section_number][$name]);
          }
        }
        elseif (isset($item['#layout'])) {
          foreach (Element::children($item) as $region_key) {

            // Track if we have non default blocks in this section.
            $has_custom_block = FALSE;
            foreach (Element::children($item[$region_key]) as $item_key) {
              if ($item_key == 'layout_builder_add_block') {
                if (isset($settings[self::LOCKED_BLOCK_ADD])) {
                  unset($element['layout_builder'][$section_number][$name][$region_key][$item_key]);
                }
              }
              elseif (isset($item[$region_key][$item_key]['#contextual_links'])) {

                // Do not apply the block operation lock settings to blocks that
                // were added in the override. Also set the custom block
                // variable to TRUE so we don't remove the region classes for
                // this section.
                if ($overridden && !isset($default_components[$item_key])) {
                  $has_custom_block = TRUE;
                  continue;
                }
                $allow_moving = TRUE;
                $layout_builder_remove_block_operations = [];
                $block_operations = [
                  'move',
                  'update',
                  'remove',
                ];

                // Do not allow moving blocks.
                if (isset($settings[self::LOCKED_BLOCK_MOVE])) {
                  $allow_moving = FALSE;
                  unset($block_operations[array_search('move', $block_operations)]);
                  $layout_builder_remove_block_operations[] = 'layout_builder_block_move';
                }

                // Do not allow updating blocks.
                if (isset($settings[self::LOCKED_BLOCK_UPDATE])) {
                  unset($block_operations[array_search('update', $block_operations)]);
                  $layout_builder_remove_block_operations[] = 'layout_builder_block_update';
                }

                // Do not allow deleting blocks.
                if (isset($settings[self::LOCKED_BLOCK_DELETE])) {
                  unset($block_operations[array_search('remove', $block_operations)]);
                  $layout_builder_remove_block_operations[] = 'layout_builder_block_remove';
                }

                // Alter the default operations in case the count of operations
                // is not equal to 3. Empty is fine too, which means the user
                // won't be able to do anything at all with this block.
                if (count($block_operations) != 3) {
                  $element['layout_builder'][$section_number][$name][$region_key][$item_key]['#contextual_links']['layout_builder_block']['metadata']['operations'] = implode(':', $block_operations);
                }

                // Add the operations to remove in a custom metadata element so
                // we can remove the contextual link later.
                // @see layout_builder_lock_contextual_links_view_alter().
                if (!empty($layout_builder_remove_block_operations)) {
                  $element['layout_builder'][$section_number][$name][$region_key][$item_key]['#contextual_links']['layout_builder_block']['metadata']['layout_builder_lock'] = implode(':', $layout_builder_remove_block_operations);
                }

                // If moving is not allowed, remove the default layout builder
                // classes on this block and add our own so we can reset the
                // pointer and padding.
                if (!$allow_moving) {
                  foreach ([
                    'layout-builder-block',
                    'js-layout-builder-block',
                  ] as $class) {
                    if (isset($element['layout_builder'][$section_number][$name][$region_key][$item_key]['#attributes']['class']) && is_array($element['layout_builder'][$section_number][$name][$region_key][$item_key]['#attributes']['class'])) {
                      $key = array_search($class, $element['layout_builder'][$section_number][$name][$region_key][$item_key]['#attributes']['class']);
                      unset($element['layout_builder'][$section_number][$name][$region_key][$item_key]['#attributes']['class'][$key]);
                    }
                  }
                  $element['layout_builder'][$section_number][$name][$region_key][$item_key]['#attributes']['class'][] = 'layout-builder-block-locked';
                }
              }
            }

            // Do not allow to move blocks into this section.
            if (isset($settings[self::LOCKED_SECTION_BLOCK_MOVE]) && !$has_custom_block) {
              if (isset($element['layout_builder'][$section_number][$name][$region_key]['#attributes']['class']) && is_array($element['layout_builder'][$section_number][$name][$region_key]['#attributes']['class'])) {
                $key = array_search('js-layout-builder-region', $element['layout_builder'][$section_number][$name][$region_key]['#attributes']['class']);
                unset($element['layout_builder'][$section_number][$name][$region_key]['#attributes']['class'][$key]);
              }
            }
          }
        }
      }
    }
    return $element;
  }

  /**
   * @inheritDoc
   */
  public static function trustedCallbacks() {
    return [
      'preRender',
    ];
  }

}

Members

Namesort descending Modifiers Type Description Overrides
LayoutBuilderLock::hasBypassLockSettingsPermission protected static function Returns whether the current user can bypass lock settings on overrides.
LayoutBuilderLock::hasManageLockDefaultPermission protected static function Returns whether the current user can manage lock settings on default.
LayoutBuilderLock::hasManageLockOverridesPermission protected static function Returns whether the current user can manage lock settings on overrides.
LayoutBuilderLock::LOCKED_BLOCK_ADD constant
LayoutBuilderLock::LOCKED_BLOCK_DELETE constant
LayoutBuilderLock::LOCKED_BLOCK_MOVE constant
LayoutBuilderLock::LOCKED_BLOCK_UPDATE constant
LayoutBuilderLock::LOCKED_SECTION_AFTER constant
LayoutBuilderLock::LOCKED_SECTION_BEFORE constant
LayoutBuilderLock::LOCKED_SECTION_BLOCK_MOVE constant
LayoutBuilderLock::LOCKED_SECTION_CONFIGURE constant
LayoutBuilderLock::NO_LOCK constant
LayoutBuilderLock::preRender public static function Applies lock settings to the Layout Builder element.
LayoutBuilderLock::trustedCallbacks public static function @inheritDoc Overrides TrustedCallbackInterface::trustedCallbacks
TrustedCallbackInterface::THROW_EXCEPTION constant Untrusted callbacks throw exceptions.
TrustedCallbackInterface::TRIGGER_SILENCED_DEPRECATION constant Untrusted callbacks trigger silenced E_USER_DEPRECATION errors.
TrustedCallbackInterface::TRIGGER_WARNING constant Untrusted callbacks trigger E_USER_WARNING errors.