micronode-1.0.0/micronode.module
micronode.module
<?php
/**
* @file
* Contains micronode hooks.
*/
declare(strict_types=1);
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Config\Entity\ThirdPartySettingsInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\node\NodeInterface;
use Drupal\node\NodeTypeInterface;
/**
* Implements hook_form_BASE_FORM_ID_alter().
*/
function micronode_form_node_type_form_alter(&$form, FormStateInterface $form_state, $form_id) {
/** @var \Drupal\node\NodeTypeInterface $type */
$type = $form_state->getFormObject()->getEntity();
$settings = $type->getThirdPartySettings('micronode');
$label = $type->label();
$form['micronode'] = [
'#type' => 'details',
'#title' => t('Micro-content settings'),
'#tree' => TRUE,
];
if (isset($form['additional_settings']) && $form['additional_settings']['#type'] === 'vertical_tabs') {
$form['micronode']['#group'] = 'additional_settings';
}
$form['micronode']['micronode_is_microcontent'] = [
'#type' => 'checkbox',
'#title' => t('Is micro-content'),
'#description' => t('If true, this @label will not be accessible on its own page, except for users with permission to edit it. It will also be removed from admin lists of content.', [
'@label' => !empty($label) ? $label : t('content'),
]),
'#default_value' => !empty($settings['micronode_is_microcontent']),
];
$form['#entity_builders'][] = 'micronode_microcontent_entity_builder';
$form['#validate'][] = 'micronode_node_type_form_validate';
}
/**
* Additional entity builder callback for our node type forms.
*
* @see micronode_form_node_type_form_alter()
*/
function micronode_microcontent_entity_builder($entity_type, ThirdPartySettingsInterface $type, &$form, FormStateInterface $form_state) {
$microcontent_values = $form_state->getValue('micronode');
foreach ($microcontent_values as $key => $value) {
$type->setThirdPartySetting('micronode', $key, $value);
}
}
/**
* Validate handler for node type forms.
*
* @see micronode_form_node_type_form_alter()
*/
function micronode_node_type_form_validate(&$form, FormStateInterface $form_state) {
$type = $form_state->getFormObject()->getEntity();
assert($type instanceof NodeTypeInterface);
// We are only concerned with update operations.
if ($type->isNew()) {
return;
}
$type_setting = $type->getThirdPartySetting('micronode', 'micronode_is_microcontent', NULL);
$original_setting = $form['micronode']['micronode_is_microcontent']['#default_value'] ?? NULL;
if (is_null($original_setting)) {
return;
}
// If the type has changed its micro-content status, wipe out cached data,
// since nodes would now possibly need to be displayed in different places
// based on their new status (i.e. views).
if (!is_null($type_setting) && ((bool) $original_setting <> (bool) $type_setting)) {
foreach (Cache::getBins() as $cache_backend) {
$cache_backend->deleteAll();
}
}
}
/**
* Implements hook_ENTITY_TYPE_access().
*/
function micronode_node_access(EntityInterface $entity, $operation, AccountInterface $account) {
// Check access for micro-content.
// No need to check admin operations or editor's access, they can see it.
if ($operation === 'view' && !$entity->access('update', $account)) {
// Bail out early if this node hasn't been flagged as microcontent.
/** @var \Drupal\node\NodeInterface $entity */
if (!micronode_is_micro_content($entity)) {
return AccessResult::neutral();
}
// Deny access to non-editor users (visitors) that are trying to see this
// node on its own canonical route.
$route_match = \Drupal::routeMatch();
if ($route_match->getRouteName() === 'entity.node.canonical') {
$node_from_route = $route_match->getParameter('node');
if ($node_from_route->id() == $entity->id()) {
return AccessResult::forbidden('This content is marked as hidden micro-content and cannot be accessed through this path.');
}
}
}
return AccessResult::neutral();
}
/**
* Implements hook_menu_links_discovered_alter().
*/
function micronode_menu_links_discovered_alter(&$links) {
// Bail out if admin toolbar isn't present.
if (!\Drupal::moduleHandler()->moduleExists('admin_toolbar_tools')) {
return;
}
$micro_content_types = micronode_get_node_types(TRUE);
// Move micro-content types out of the existing "Add Content" list, and place
// them under the new "Add Micro-Content" we have created in our yml file.
foreach ($micro_content_types as $type_id => $type) {
$links["admin_toolbar_tools.extra_links:node.add.{$type_id}"]['parent'] = 'micronode.add_microcontent_page';
}
// Set the weight of the "Add content" link so that it shows up right before
// the "Add micro-content" one.
$links['admin_toolbar_tools.extra_links:node.add']['weight'] = -14;
}
/**
* Helper to return types filtered by the micro-content flag.
*
* @param bool|null $return_microcontent
* What to include in the results:
* - TRUE: Only micro-content types.
* - FALSE: Only non-micro-content types.
* - NULL: Return all types. This is the default option.
*
* @return \Drupal\node\NodeTypeInterface[]
* An associative array of content types keyed by their type ids.
*/
function micronode_get_node_types($return_microcontent = NULL) {
$cache = \Drupal::service('cache.default');
$arg_string = $return_microcontent ? 'TRUE' : ($return_microcontent === NULL ? 'NULL' : 'FALSE');
$cid = "micronode_get_node_types:$arg_string";
if ($cached = $cache->get($cid)) {
return $cached->data;
}
if (!isset($types)) {
/** @var \Drupal\node\NodeTypeInterface[] $types */
$types = [];
foreach (\Drupal::entityTypeManager()->getStorage('node_type')->loadMultiple() as $type) {
/** @var \Drupal\node\NodeTypeInterface $type */
// If the caller requested all types, just add it here.
if ($return_microcontent === NULL) {
$types[$type->id()] = $type;
continue;
}
$is_microcontent = (bool) $type->getThirdPartySetting('micronode', 'micronode_is_microcontent', FALSE);
if ($return_microcontent === TRUE && $is_microcontent === TRUE) {
$types[$type->id()] = $type;
}
elseif ($return_microcontent === FALSE && $is_microcontent === FALSE) {
$types[$type->id()] = $type;
}
}
}
// Cache the result.
$cache->set(
$cid,
$types,
Cache::PERMANENT,
['config:node_type_list']
);
return $types;
}
/**
* Detects whether a given node is to be treated as micro-content.
*
* @param \Drupal\node\NodeInterface $node
* The node object.
*
* @return bool
* TRUE if the node passed in is to be treated as micro-content, FALSE
* otherwise.
*/
function micronode_is_micro_content(NodeInterface $node) {
$micro_content_types = array_keys(micronode_get_node_types(TRUE));
return in_array($node->getType(), $micro_content_types, TRUE);
}
/**
* Implements hook_form_views_exposed_form_alter().
*/
function micronode_form_views_exposed_form_alter(&$form, FormStateInterface $form_state, $form_id) {
// Remove types from exposed filters when 'Is Micro-content' filter is in use.
if ($view = $form_state->getStorage()['view']) {
$filters = $view->getHandlers('filter');
if (isset($filters['micronode_is_microcontent'])) {
$return_microcontent = $view->display_handler->getHandler('filter', 'micronode_is_microcontent')->value;
$allowed_bundles = array_keys(micronode_get_node_types($return_microcontent));
foreach ($filters as $id => $definition) {
if (isset($definition['exposed']) && $definition['exposed'] && $definition['plugin_id'] == 'bundle') {
foreach ($form[$id]['#options'] as $key => $value) {
if (is_string($value) && !in_array($key, $allowed_bundles, TRUE)) {
unset($form[$id]['#options'][$key]);
}
}
}
}
}
}
}
/**
* Implements hook_views_plugins_wizard_alter().
*/
function micronode_views_plugins_wizard_alter(array &$plugins) {
// Hijack the Node views wizard plugin and make it use our subclass instead.
if (!empty($plugins['node'])) {
$plugins['node']['class'] = 'Drupal\micronode\Plugin\views\wizard\Micronode';
}
}
/**
* Implements hook_element_info_alter().
*/
function micronode_element_info_alter(array &$info) {
// Add a #process method to disallow micronodes from entity_autocomplete
// unless they are explicitly allowed.
array_unshift($info['entity_autocomplete']['#process'], '\Drupal\micronode\MicronodeAutocompleteHelper::disallowMicronodes');
}
