mirror of
https://gitlab.unicamp.br/infimecc_drupal11_modules/site_users.git
synced 2026-05-04 00:20:41 -03:00
Adiciona menu 'Adicionar' configurável no menu da conta do usuário
Item pai 'Adicionar' no menu account com subitens derivados dinamicamente a partir de site_users.settings:add_content_links. O pai fica oculto quando o usuário não tem acesso a nenhum dos routes configurados. - Rota site_users.add_content com _custom_access via AddContentAccessCheck - hook_menu_links_discovered_alter() gera os subitens com IDs estáveis - Formulário de settings com tabela editável (label, rota, parâmetro, peso) - CSS do microsite atualizado com dropdown ao hover/focus-within Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -9,3 +9,4 @@ user_editable_fields:
|
|||||||
field_user_social_links: true
|
field_user_social_links: true
|
||||||
field_user_photos: true
|
field_user_photos: true
|
||||||
role_view_modes: { }
|
role_view_modes: { }
|
||||||
|
add_content_links: [ ]
|
||||||
|
|||||||
@@ -30,3 +30,25 @@ site_users.settings:
|
|||||||
sequence:
|
sequence:
|
||||||
type: string
|
type: string
|
||||||
label: 'View mode machine name'
|
label: 'View mode machine name'
|
||||||
|
add_content_links:
|
||||||
|
type: sequence
|
||||||
|
label: 'Add content menu items'
|
||||||
|
sequence:
|
||||||
|
type: mapping
|
||||||
|
label: 'Add content menu item'
|
||||||
|
mapping:
|
||||||
|
label:
|
||||||
|
type: label
|
||||||
|
label: 'Menu item label'
|
||||||
|
route_name:
|
||||||
|
type: string
|
||||||
|
label: 'Route name'
|
||||||
|
route_parameters:
|
||||||
|
type: sequence
|
||||||
|
label: 'Route parameters'
|
||||||
|
sequence:
|
||||||
|
type: string
|
||||||
|
label: 'Parameter value'
|
||||||
|
weight:
|
||||||
|
type: integer
|
||||||
|
label: 'Weight'
|
||||||
|
|||||||
@@ -4,3 +4,10 @@ site_users.settings:
|
|||||||
route_name: site_users.settings
|
route_name: site_users.settings
|
||||||
parent: site_tools.admin_config
|
parent: site_tools.admin_config
|
||||||
weight: 10
|
weight: 10
|
||||||
|
|
||||||
|
site_users.add_content:
|
||||||
|
title: 'Adicionar'
|
||||||
|
route_name: site_users.add_content
|
||||||
|
menu_name: account
|
||||||
|
weight: -5
|
||||||
|
expanded: true
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
|
|||||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||||
use Drupal\Core\Field\FieldItemListInterface;
|
use Drupal\Core\Field\FieldItemListInterface;
|
||||||
use Drupal\Core\Form\FormStateInterface;
|
use Drupal\Core\Form\FormStateInterface;
|
||||||
|
use Drupal\Core\Menu\MenuLinkDefault;
|
||||||
use Drupal\Core\Session\AccountInterface;
|
use Drupal\Core\Session\AccountInterface;
|
||||||
use Drupal\Core\Url;
|
use Drupal\Core\Url;
|
||||||
use Drupal\field\FieldConfigInterface;
|
use Drupal\field\FieldConfigInterface;
|
||||||
@@ -604,3 +605,44 @@ function site_users_get_default_photo(UserInterface $user): ?MediaInterface {
|
|||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_menu_links_discovered_alter().
|
||||||
|
*
|
||||||
|
* Adiciona dinamicamente os subitens do menu "Adicionar" configurados em
|
||||||
|
* site_users.settings:add_content_links.
|
||||||
|
*/
|
||||||
|
function site_users_menu_links_discovered_alter(array &$links): void {
|
||||||
|
$items = \Drupal::config('site_users.settings')->get('add_content_links') ?? [];
|
||||||
|
|
||||||
|
foreach ($items as $item) {
|
||||||
|
if (empty($item['route_name']) || empty($item['label'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID estável derivado da rota e dos parâmetros.
|
||||||
|
$parts = [preg_replace('/[^a-z0-9]/', '_', strtolower($item['route_name']))];
|
||||||
|
foreach ($item['route_parameters'] ?? [] as $value) {
|
||||||
|
$parts[] = preg_replace('/[^a-z0-9]/', '_', strtolower((string) $value));
|
||||||
|
}
|
||||||
|
$id = 'site_users.add_content_child.' . implode('_', $parts);
|
||||||
|
|
||||||
|
$links[$id] = [
|
||||||
|
'id' => $id,
|
||||||
|
'title' => $item['label'],
|
||||||
|
'route_name' => $item['route_name'],
|
||||||
|
'route_parameters' => $item['route_parameters'] ?? [],
|
||||||
|
'menu_name' => 'account',
|
||||||
|
'parent' => 'site_users.add_content',
|
||||||
|
'weight' => (int) ($item['weight'] ?? 0),
|
||||||
|
'provider' => 'site_users',
|
||||||
|
'class' => MenuLinkDefault::class,
|
||||||
|
'form_class' => 'Drupal\Core\Menu\Form\MenuLinkDefaultForm',
|
||||||
|
'metadata' => [],
|
||||||
|
'options' => [],
|
||||||
|
'expanded' => FALSE,
|
||||||
|
'enabled' => TRUE,
|
||||||
|
'discovered' => TRUE,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,3 +5,11 @@ site_users.settings:
|
|||||||
_title: 'Site Users Settings'
|
_title: 'Site Users Settings'
|
||||||
requirements:
|
requirements:
|
||||||
_permission: 'administer site_users settings'
|
_permission: 'administer site_users settings'
|
||||||
|
|
||||||
|
site_users.add_content:
|
||||||
|
path: '/add-content'
|
||||||
|
defaults:
|
||||||
|
_controller: '\Drupal\site_users\Controller\AddContentController::redirectToFirst'
|
||||||
|
_title: 'Adicionar'
|
||||||
|
requirements:
|
||||||
|
_custom_access: 'site_users.add_content_access::access'
|
||||||
|
|||||||
@@ -6,3 +6,9 @@ services:
|
|||||||
- '@entity_type.manager'
|
- '@entity_type.manager'
|
||||||
- '@file.repository'
|
- '@file.repository'
|
||||||
- '@file_system'
|
- '@file_system'
|
||||||
|
|
||||||
|
site_users.add_content_access:
|
||||||
|
class: Drupal\site_users\Access\AddContentAccessCheck
|
||||||
|
arguments:
|
||||||
|
- '@config.factory'
|
||||||
|
- '@access_manager'
|
||||||
|
|||||||
47
src/Access/AddContentAccessCheck.php
Normal file
47
src/Access/AddContentAccessCheck.php
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Drupal\site_users\Access;
|
||||||
|
|
||||||
|
use Drupal\Core\Access\AccessManagerInterface;
|
||||||
|
use Drupal\Core\Access\AccessResult;
|
||||||
|
use Drupal\Core\Access\AccessResultInterface;
|
||||||
|
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||||
|
use Drupal\Core\Routing\Access\AccessInterface;
|
||||||
|
use Drupal\Core\Session\AccountInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grants access to the "Adicionar" parent menu route when any child is accessible.
|
||||||
|
*/
|
||||||
|
class AddContentAccessCheck implements AccessInterface {
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private readonly ConfigFactoryInterface $configFactory,
|
||||||
|
private readonly AccessManagerInterface $accessManager,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns allowed if the user can access at least one configured add route.
|
||||||
|
*/
|
||||||
|
public function access(AccountInterface $account): AccessResultInterface {
|
||||||
|
$items = $this->configFactory
|
||||||
|
->get('site_users.settings')
|
||||||
|
->get('add_content_links') ?? [];
|
||||||
|
|
||||||
|
foreach ($items as $item) {
|
||||||
|
if (empty($item['route_name'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$params = $item['route_parameters'] ?? [];
|
||||||
|
if ($this->accessManager->checkNamedRoute($item['route_name'], $params, $account)) {
|
||||||
|
return AccessResult::allowed()
|
||||||
|
->addCacheContexts(['user.permissions', 'user.roles']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return AccessResult::forbidden()
|
||||||
|
->addCacheContexts(['user.permissions', 'user.roles']);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
48
src/Controller/AddContentController.php
Normal file
48
src/Controller/AddContentController.php
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Drupal\site_users\Controller;
|
||||||
|
|
||||||
|
use Drupal\Core\Access\AccessManagerInterface;
|
||||||
|
use Drupal\Core\Controller\ControllerBase;
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||||
|
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redirects to the first accessible add-content route for the current user.
|
||||||
|
*/
|
||||||
|
class AddContentController extends ControllerBase {
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private readonly AccessManagerInterface $accessManager,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public static function create(ContainerInterface $container): static {
|
||||||
|
return new static(
|
||||||
|
$container->get('access_manager'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redirects to the first accessible add route, or to the front page.
|
||||||
|
*/
|
||||||
|
public function redirectToFirst(): RedirectResponse {
|
||||||
|
$items = $this->config('site_users.settings')
|
||||||
|
->get('add_content_links') ?? [];
|
||||||
|
$account = $this->currentUser();
|
||||||
|
|
||||||
|
foreach ($items as $item) {
|
||||||
|
if (empty($item['route_name'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$params = $item['route_parameters'] ?? [];
|
||||||
|
if ($this->accessManager->checkNamedRoute($item['route_name'], $params, $account)) {
|
||||||
|
return $this->redirect($item['route_name'], $params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->redirect('<front>');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -177,6 +177,66 @@ class SiteUsersSettingsForm extends ConfigFormBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fieldset para itens do menu "Adicionar".
|
||||||
|
$form['add_content_links'] = [
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#title' => $this->t('Add content menu items'),
|
||||||
|
'#description' => $this->t('Configure items shown under the "Adicionar" entry in the account menu. Each item points to an entity add route. Leave "Label" empty to remove the row.'),
|
||||||
|
'#tree' => TRUE,
|
||||||
|
];
|
||||||
|
|
||||||
|
$saved_items = $config->get('add_content_links') ?? [];
|
||||||
|
// Append one blank row for adding a new item.
|
||||||
|
$saved_items[] = ['label' => '', 'route_name' => '', 'route_parameters' => [], 'weight' => 0];
|
||||||
|
|
||||||
|
$form['add_content_links']['table'] = [
|
||||||
|
'#type' => 'table',
|
||||||
|
'#header' => [
|
||||||
|
$this->t('Label'),
|
||||||
|
$this->t('Route name'),
|
||||||
|
$this->t('Parameter name'),
|
||||||
|
$this->t('Parameter value'),
|
||||||
|
$this->t('Weight'),
|
||||||
|
],
|
||||||
|
'#empty' => $this->t('No items yet. Fill in the row below to add one.'),
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($saved_items as $delta => $item) {
|
||||||
|
$params = $item['route_parameters'] ?? [];
|
||||||
|
$param_name = array_key_first($params) ?? '';
|
||||||
|
$param_value = $params[$param_name] ?? '';
|
||||||
|
|
||||||
|
$form['add_content_links']['table'][$delta]['label'] = [
|
||||||
|
'#type' => 'textfield',
|
||||||
|
'#default_value' => $item['label'] ?? '',
|
||||||
|
'#size' => 20,
|
||||||
|
'#placeholder' => $this->t('e.g. Artigo'),
|
||||||
|
];
|
||||||
|
$form['add_content_links']['table'][$delta]['route_name'] = [
|
||||||
|
'#type' => 'textfield',
|
||||||
|
'#default_value' => $item['route_name'] ?? '',
|
||||||
|
'#size' => 30,
|
||||||
|
'#placeholder' => 'e.g. node.add',
|
||||||
|
];
|
||||||
|
$form['add_content_links']['table'][$delta]['param_name'] = [
|
||||||
|
'#type' => 'textfield',
|
||||||
|
'#default_value' => $param_name,
|
||||||
|
'#size' => 20,
|
||||||
|
'#placeholder' => 'e.g. node_type',
|
||||||
|
];
|
||||||
|
$form['add_content_links']['table'][$delta]['param_value'] = [
|
||||||
|
'#type' => 'textfield',
|
||||||
|
'#default_value' => $param_value,
|
||||||
|
'#size' => 20,
|
||||||
|
'#placeholder' => 'e.g. article',
|
||||||
|
];
|
||||||
|
$form['add_content_links']['table'][$delta]['weight'] = [
|
||||||
|
'#type' => 'number',
|
||||||
|
'#default_value' => (int) ($item['weight'] ?? 0),
|
||||||
|
'#size' => 4,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
return parent::buildForm($form, $form_state);
|
return parent::buildForm($form, $form_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,6 +290,32 @@ class SiteUsersSettingsForm extends ConfigFormBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Salvar add_content_links: ignorar linhas sem label ou route_name.
|
||||||
|
$links_raw = $form_state->getValue(['add_content_links', 'table']) ?? [];
|
||||||
|
$add_content_links = [];
|
||||||
|
foreach ($links_raw as $row) {
|
||||||
|
$label = trim($row['label'] ?? '');
|
||||||
|
$route_name = trim($row['route_name'] ?? '');
|
||||||
|
if ($label === '' || $route_name === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$params = [];
|
||||||
|
$param_name = trim($row['param_name'] ?? '');
|
||||||
|
$param_value = trim($row['param_value'] ?? '');
|
||||||
|
if ($param_name !== '') {
|
||||||
|
$params[$param_name] = $param_value;
|
||||||
|
}
|
||||||
|
$add_content_links[] = [
|
||||||
|
'label' => $label,
|
||||||
|
'route_name' => $route_name,
|
||||||
|
'route_parameters' => $params,
|
||||||
|
'weight' => (int) ($row['weight'] ?? 0),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
// Reordena por weight.
|
||||||
|
usort($add_content_links, fn($a, $b) => $a['weight'] <=> $b['weight']);
|
||||||
|
$config->set('add_content_links', $add_content_links);
|
||||||
|
|
||||||
// Salvar role_view_modes: apenas os valores marcados (filtrar 0).
|
// Salvar role_view_modes: apenas os valores marcados (filtrar 0).
|
||||||
$role_view_modes_raw = $form_state->getValue('role_view_modes') ?? [];
|
$role_view_modes_raw = $form_state->getValue('role_view_modes') ?? [];
|
||||||
$roles = \Drupal\user\Entity\Role::loadMultiple();
|
$roles = \Drupal\user\Entity\Role::loadMultiple();
|
||||||
|
|||||||
18
src/Plugin/Menu/AddContentMenuLink.php
Normal file
18
src/Plugin/Menu/AddContentMenuLink.php
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Drupal\site_users\Plugin\Menu;
|
||||||
|
|
||||||
|
use Drupal\Core\Menu\MenuLinkDefault;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides derived menu links for add-content actions in the account menu.
|
||||||
|
*
|
||||||
|
* @MenuLink(
|
||||||
|
* id = "site_users.add_content_child",
|
||||||
|
* deriver = "Drupal\site_users\Plugin\Menu\AddContentMenuLinkDeriver"
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
class AddContentMenuLink extends MenuLinkDefault {
|
||||||
|
}
|
||||||
64
src/Plugin/Menu/AddContentMenuLinkDeriver.php
Normal file
64
src/Plugin/Menu/AddContentMenuLinkDeriver.php
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Drupal\site_users\Plugin\Menu;
|
||||||
|
|
||||||
|
use Drupal\Component\Plugin\Derivative\DeriverBase;
|
||||||
|
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||||
|
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates account menu "Adicionar" child links from site_users.settings.
|
||||||
|
*/
|
||||||
|
class AddContentMenuLinkDeriver extends DeriverBase implements ContainerDeriverInterface {
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private readonly ConfigFactoryInterface $configFactory,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public static function create(ContainerInterface $container, $base_plugin_id): static {
|
||||||
|
return new static($container->get('config.factory'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getDerivativeDefinitions($base_plugin_definition): array {
|
||||||
|
$this->derivatives = [];
|
||||||
|
|
||||||
|
$items = $this->configFactory
|
||||||
|
->get('site_users.settings')
|
||||||
|
->get('add_content_links') ?? [];
|
||||||
|
|
||||||
|
foreach ($items as $item) {
|
||||||
|
if (empty($item['route_name']) || empty($item['label'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = $this->buildId($item);
|
||||||
|
$this->derivatives[$id] = $base_plugin_definition;
|
||||||
|
$this->derivatives[$id]['title'] = $item['label'];
|
||||||
|
$this->derivatives[$id]['route_name'] = $item['route_name'];
|
||||||
|
$this->derivatives[$id]['route_parameters'] = $item['route_parameters'] ?? [];
|
||||||
|
$this->derivatives[$id]['weight'] = (int) ($item['weight'] ?? 0);
|
||||||
|
$this->derivatives[$id]['menu_name'] = 'account';
|
||||||
|
$this->derivatives[$id]['parent'] = 'site_users.add_content';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->derivatives;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a stable derivative ID from the item's route and parameters.
|
||||||
|
*/
|
||||||
|
private function buildId(array $item): string {
|
||||||
|
$parts = [preg_replace('/[^a-z0-9]/', '_', strtolower($item['route_name']))];
|
||||||
|
foreach ($item['route_parameters'] ?? [] as $value) {
|
||||||
|
$parts[] = preg_replace('/[^a-z0-9]/', '_', strtolower((string) $value));
|
||||||
|
}
|
||||||
|
return implode('_', $parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -310,8 +310,42 @@ body.microsite {
|
|||||||
margin-inline-end: 18px;
|
margin-inline-end: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.microsite-top-bar li.menu__item {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
.microsite-top-bar ul.menu ul.menu {
|
.microsite-top-bar ul.menu ul.menu {
|
||||||
display: none;
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
min-width: 160px;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: hsl(201, 15%, 15%);
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.35);
|
||||||
|
z-index: 100;
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.microsite-top-bar li.menu__item:hover > ul.menu,
|
||||||
|
.microsite-top-bar li.menu__item:focus-within > ul.menu {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.microsite-top-bar ul.menu ul.menu li.menu__item {
|
||||||
|
margin-inline-end: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.microsite-top-bar ul.menu ul.menu a.menu__link {
|
||||||
|
padding: 6px 16px;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.microsite-top-bar ul.menu ul.menu a.menu__link:hover {
|
||||||
|
background-color: hsl(201, 15%, 25%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.microsite-top-bar a.menu__link {
|
.microsite-top-bar a.menu__link {
|
||||||
|
|||||||
Reference in New Issue
Block a user