web-dev-qa-db-ja.com

既存のメニューツリーマニピュレータをどのように上書きできますか?

Or:ユーザーがノードアクセスチェックをバイパスしていないときに、非公開ノードのメニュー項目がメニュー選択から削除されないようにするにはどうすればよいですか許可ですが、実際にはさまざまな非公開ノードを編集できますか?

ノードフォームでは、ユーザーが実際にそれらのノードを表示および編集しても、関連ノードが非公開であるため、メニュー項目オプションに一部のメニュー項目が欠落しているという問題に直面しています。

/core/lib/Drupal/Core/Menu/MenuParentFormSelector.php関数getParentSelectOptionsは、ノード編集フォームで使用されるオプションリストを作成します。

関数は次のとおりです。

  /**
   * {@inheritdoc}
   */
  public function getParentSelectOptions($id = '', array $menus = NULL, CacheableMetadata &$cacheability = NULL) {
    if (!isset($menus)) {
      $menus = $this->getMenuOptions();
    }

    $options = array();
    $depth_limit = $this->getParentDepthLimit($id);
    foreach ($menus as $menu_name => $menu_title) {
      $options[$menu_name . ':'] = '<' . $menu_title . '>';

      $parameters = new MenuTreeParameters();
      $parameters->setMaxDepth($depth_limit);
      $tree = $this->menuLinkTree->load($menu_name, $parameters);
      $manipulators = array(
        array('callable' => 'menu.default_tree_manipulators:checkNodeAccess'),
        array('callable' => 'menu.default_tree_manipulators:checkAccess'),
        array('callable' => 'menu.default_tree_manipulators:generateIndexAndSort'),
      );
      $tree = $this->menuLinkTree->transform($tree, $manipulators);
      $this->parentSelectOptionsTreeWalk($tree, $menu_name, '--', $options, $id, $depth_limit, $cacheability);
    }
    return $options;
  }

その関数の重要な部分はマニピュレーター'menu.default_tree_manipulators:checkNodeAccess'

マニピュレータ(/core/lib/Drupal/Core/Menu/DefaultMenuLinkTreeManipulators.php)は次のことを行います(public function checkNodeAccess):

if ($this->account->hasPermission('bypass node access')) {
  $query->accessCheck(FALSE);
}
else {
  $access_result->addCacheContexts(['user.node_grants:view']);
  $query->condition('status', NODE_PUBLISHED); // <<< wtf?
}

TL; DR

したがって、「ユーザーがすべてのノードアクセスチェックをバイパスするか、メニュー選択からすべての非公開ノードを非表示にする可能性がある」と想定しています-これは奇妙です...

これをどのように上書きできますか?

3
Alex

既存のメニューのツリーマニピュレータを変更する方法を見つけることができませんでした。しかし、メニューをレンダリングする場合は可能です。たとえば、カスタムブロックで考えてみましょう。

独自のツリー操作サービスを作成します。

core.services.yml からコピー:

_  menu.default_tree_manipulators:
    class: Drupal\Core\Menu\DefaultMenuLinkTreeManipulators
    arguments: ['@access_manager', '@current_user', '@entity_type.manager']
_

同様の方法でサービスを定義できます。

_  mymodule.my_custom_tree_manipulators:
    class: Drupal\mymodule\MyCustomMenuLinkTreeManipulators
    arguments: ['@access_manager', '@current_user', '@entity_type.manager']
_

次に、MyCustomMenuLinkTreeManipulatorsを拡張するDefaultMenuLinkTreeManipulatorsクラスを作成し、カスタムメソッドを定義します。たとえば、checkAccessIgnoreStatus()とし、必要なロジックを実装します。

それを使用する方法はたくさんありますが、そのための新しいブロックプラグインを作成します。マニピュレータでメニューをレンダリングする方法の詳細については、 私の答え を参照してください。

マニピュレーターを使用します。

_$manipulators = array(
        array('callable' => 'mymodule.my_custom_tree_manipulators:checkAccessIgnoreStatus'),
        // ...
      );
_
3
Artreaktor

私も同じような仕事をしています。このクラスのparentSelectOptionsTreeWalkメソッドをオーバーライドする必要があります。それは解決策です:これをsites/defaultのservices.ymlに追加します

services:
  menu.parent_form_selector:
      class: Drupal\my_page\MyPageMenuParentFormSelector
      arguments: ['@menu.link_tree', '@entity.manager', '@string_translation']

次に、srcディレクトリファイルMyPageServiceProvider.phpのmy_pageモジュールに作成します。

<?php

namespace Drupal\my_page;

use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DependencyInjection\ServiceProviderBase;
use Symfony\Component\DependencyInjection\Reference;
/**
* Class MyPageServiceProvider.
*
* @package Drupal\my_page
*/

class MyPageServiceProvider extends ServiceProviderBase {
/**
* {@inheritdoc}
*/

public function alter(ContainerBuilder $container) {
$definition = $container->getDefinition('menu.parent_form_selector');
$definition->setClass('Drupal\my_page\MyPageMenuParentFormSelector');
$definition->setArguments(
  [
    new Reference('menu.link_tree'),
    new Reference('entity.manager'),
    new Reference('string_translation'),
  ]
);
}
}

そしてあなたのクラスを作成します:MyPagemenuParentFormSelector.php

<?php

    namespace Drupal\my_page;

use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Menu\MenuParentFormSelectorInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Menu\MenuParentFormSelector;

/**
 * Default implementation of the menu parent form selector service.
 *
 * The form selector is a list of all appropriate menu links.
 */

class MyPageMenuParentFormSelector extends MenuParentFormSelector implements MenuParentFormSelectorInterface {
  use StringTranslationTrait;

  /**
   * The menu link tree service.
   *
   * @var \Drupal\Core\Menu\MenuLinkTreeInterface
   */
  protected $menuLinkTree;

  /**
   * The entity manager.
   *
   * @var \Drupal\Core\Entity\EntityManagerInterface
   */
  protected $entityManager;

  /**
   * Iterates over all items in the tree to prepare the parents select options.
   *
   * @param \Drupal\Core\Menu\MenuLinkTreeElement[] $tree
   *   The menu tree.
   * @param string $menu_name
   *   The menu name.
   * @param string $indent
   *   The indentation string used for the label.
   * @param array $options
   *   The select options.
   * @param string $exclude
   *   An excluded menu link.
   * @param int $depth_limit
   *   The maximum depth of menu links considered for the select options.
   * @param \Drupal\Core\Cache\CacheableMetadata|null &$cacheability
   *   The object to add cacheability metadata to, if not NULL.
   */
  protected function parentSelectOptionsTreeWalk(array $tree, $menu_name, $indent, array &$options, $exclude, $depth_limit, CacheableMetadata &$cacheability = NULL) {
    foreach ($tree as $element) {
      if ($element->depth > $depth_limit) {
// Don't iterate through any links on this level.
        break;
      }

// Collect the cacheability metadata of the access result, as well as the
// link.
      if ($cacheability) {
        $cacheability = $cacheability
          ->merge(CacheableMetadata::createFromObject($element->access))
          ->merge(CacheableMetadata::createFromObject($element->link));
      }

// Only show accessible links.
      if (!$element->access->isAllowed()) {
        continue;
      }

      $link = $element->link;
      if ($link->getPluginId() != $exclude) {
        $title = $indent . ' ' . Unicode::truncate($link->getTitle(), 255, TRUE, FALSE);
        if (!$link->isEnabled()) {
          $title .= ' (' . $this->t('disabled') . ')';
        }
        $options[$menu_name . ':' . $link->getPluginId()] = $title;
        if (!empty($element->subtree)) {
          $this->parentSelectOptionsTreeWalk($element->subtree, $menu_name, $indent . '--', $options, $exclude, $depth_limit, $cacheability);
        }
      }
    }
  }
}

ここにオーバーライドに関する良い情報がありますdrupalコアサービス https://www.hs2solutions.com/blog/drupal-how-override-core-drupal-8-service

0
lamp5