inline_documentation-2.0.0-alpha1/inline_documentation.module

inline_documentation.module
<?php

/**
 * @file
 * Contains Drupal\inline_documentation\inline_documentation.module.
 */

use Drupal\Core\Form\FormState;
use Drupal\Core\Url;
use Drupal\node\Entity\Node;
use Drupal\Component\Utility\Html;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Perform some alterations on the inline_documentation node forms.
 */
function inline_documentation_form_node_form_alter(&$form, FormState $form_state, $form_id) {
  switch ($form_id) {
    case 'node_inline_documentation_form':
    case 'node_inline_documentation_edit_form':
      // Create a visibility group in the right-hand column.
      $form['visibility'] = [
        '#type' => 'details',
        '#title' => t('Visibility'),
        '#group' => 'advanced',
        '#attributes' => [
          'class' => ['node-form-options'],
        ],
        '#attached' => [
          'library' => ['node/drupal.node'],
        ],
        '#weight' => -10,
        '#optional' => FALSE,
        '#open' => TRUE,
      ];

      // Move the visibility settings to the new group.
      $form['field_inline_doc_page_element']['#group'] = 'visibility';
      $form['field_inline_doc_color']['#group'] = 'visibility';
      $form['field_inline_doc_pages']['#group'] = 'visibility';
      $form['field_inline_doc_weight']['#group'] = 'visibility';
      break;

    case 'node_inline_documentation_inline_documentation_form':
      // Create a button for activating the element selector.
      $select_button = [
        'select_element' => [
          '#type' => 'button',
          '#value' => t('Select element'),
          '#attributes' => [
            'class' => ['inline-documentation-select-element'],
          ],
        ],
      ];

      // Add the button as first widget to the field_inline_doc_page_element
      // field.
      array_unshift($form['field_inline_doc_page_element']['widget'], $select_button);

      // Change the field type of the field_inline_doc_color field to 'color'.
      $form['field_inline_doc_color']['widget'][0]['value']['#type'] = 'color';

      // Get the node object.
      $form_object = $form_state->getFormObject();
      $node = $form_object->getEntity();

      // Set the current url as default value in the field_inline_doc_pages
      // field in new nodes.
      if ($node->isNew()) {
        $form['field_inline_doc_pages']['widget'][0]['value']['#default_value'] = Url::fromRoute('<current>');
      }

      // Add a custom submit handler for our redirect.
      $form['actions']['submit']['#submit'][] = 'inline_documentation_node_edit_form_submit';

      // Change the advanced group to the accordion type.
      $form['advanced']['#type'] = 'accordion';

      // Close the revision information group by default.
      $form['revision_information']['#open'] = FALSE;

      // Move the meta group to the bottom of the form.
      $form['meta']['#weight'] = 21;

      // Create a new visibility group.
      $form['visibility'] = [
        '#type' => 'details',
        '#title' => t('Visibility'),
        '#group' => 'advanced',
        '#attributes' => [
          'class' => ['node-form-options'],
        ],
        '#attached' => [
          'library' => ['node/drupal.node'],
        ],
        '#weight' => 10,
        '#optional' => FALSE,
        '#open' => FALSE,
      ];

      // Add these fields to the visibility group.
      $form['field_inline_doc_pages']['#group'] = 'visibility';
      $form['field_inline_doc_weight']['#group'] = 'visibility';

      // Breaky breaky.
      break;
  }
}

/**
 * Submit handler for the node_inline_documentation_inline_documentation_form.
 */
function inline_documentation_node_edit_form_submit($form, FormState &$form_state) {
  // Redirect to the current url after submitting.
  $form_state->setRedirectUrl(Url::fromRoute('<current>'));
}

/**
 * Implements hook_page_bottom().
 */
function inline_documentation_page_bottom(array &$page_bottom) {
  // Get the current user.
  $user = \Drupal::currentUser();

  // Stop if the user hasn't got the permission to view inline documentation.
  if (!$user->hasPermission('view inline documentation overview')) {
    return;
  }

  $page_whitelist = \Drupal::config('inline_documentation.settings')->get('inline_documentation_page_whitelist');
  $page_blacklist = \Drupal::config('inline_documentation.settings')->get('inline_documentation_page_blacklist');
  $show_inline_documentation_overview = FALSE;

  $pages = inline_documentation_get_current_page();
  if (!empty($pages)) {
    foreach ($pages as $page) {
      if (empty($page_whitelist) || \Drupal::service('path.matcher')->matchPath($page, $page_whitelist)) {
        $show_inline_documentation_overview = TRUE;
      }
      if (!empty($page_blacklist) && \Drupal::service('path.matcher')->matchPath($page, $page_blacklist)) {
        $show_inline_documentation_overview = FALSE;
        break;
      }
    }
  }

  if (!$show_inline_documentation_overview) {
    return;
  }

  // This array will contain the inline documentation nodes in a renderable
  // array.
  $renderable_nodes = [];

  // Load inline documentation for the current page.
  $nodes = inline_documentation_load_by_page();

  // Check if there's documentation.
  if (!empty($nodes)) {
    foreach ($nodes as $node) {
      // Only show unpublished nodes to its owner or anyone with the
      // administer nodes permission.
      if (
        !$node->isPublished() &&
        $node->getOwnerId() != $user->id() &&
        !$user->hasPermission('administer nodes')
      ) {
        continue;
      }

      // Load the inline_documentation view display.
      $view_builder = \Drupal::entityTypeManager()->getViewBuilder('node');
      $renderable_nodes[] = $view_builder->view($node, 'inline_documentation', $node->language()->getId());
    }
  }

  $form = inline_documentation_get_node_form();

  // Don't show anything if no nodes and no edit form are found.
  if (empty($renderable_nodes) && empty($form)) {
    return;
  }

  // Add the inline documentation to the page.
  $page_bottom['inline_documentation'] = [
    '#theme' => 'inline_documentation_overview',
    '#nodes' => $renderable_nodes,
    '#form' => $form,
    '#attached' => [
      'library' => 'inline_documentation/inline_documentation',
    ],
  ];
}

/**
 * Loads the node add/edit form for an inline documentation node.
 *
 * @return array|NULL
 *   A renderable array containing a form or NULL.
 */
function inline_documentation_get_node_form() {
  // Get the current user.
  $user = \Drupal::currentUser();

  // First, create an empty node of the type inline_documentation.
  $node = \Drupal::entityTypeManager()
    ->getStorage('node')
    ->create(['type' => 'inline_documentation']);

  // Try to retrieve the edit-inline-doc param from the url.
  $edit_nid = \Drupal::request()->get('edit-inline-doc');

  // Check if the user may create a new node if no nid to edit is given.
  if (empty($edit_nid) && !$node->access('create', $user)) {
    return NULL;
  }

  // Check if it's not empty.
  if (!empty($edit_nid)) {
    // Check if the user may update the node.
    if (!$node->access('update', $user)) {
      return NULL;
    }

    // Load the node.
    $node = \Drupal::entityTypeManager()
      ->getStorage('node')
      ->load($edit_nid);

    // Check if the node is of the type inline_documentation.
    if ($node->getType() != 'inline_documentation') {
      return NULL;
    }
  }

  // Generate the form.
  $form = \Drupal::service('entity.form_builder')->getForm($node, 'inline_documentation');

  // Create a form title.
  $form_title = t('Add Inline Documentation');
  if (!$node->isNew()) {
    $form_title = t('Edit Inline Documentation');
    $form['#attributes']['class'][] = 'edit-form';
  }

  // Add the title to the form.
  $form['form_title'] = [
    '#markup' => '<h2>' . $form_title . '</h2>',
    '#weight' => -10,
  ];

  // Force the form to use the not-admin theme.
  $form['#theme'] = ['node_inline_documentation_inline_documentation_form', 'node_form'];

  // Force the meta group to become a details element.
  if (isset($form['meta'])) {
    $form['meta']['#theme_wrappers'] = ['details'];
    $form['meta']['#summary_attributes'] = [];
    unset($form['meta']['#attributes']['class']);
  }

  return $form;
}

/**
 * Creates html containing an error message.
 *
 * @param string $message
 *   The error message.
 *
 * @return array
 *   Renderable array with a title, error message and back link.
 */
function inline_documentation_form_error($message) {
  $form = [
    'title' => [
      '#markup' => '<h2>' . t('Error') . '</h2>',
    ],
    'error' => [
      '#markup' => '<p>' . $message . '</p>',
      '#prefix' => '<div class="error">',
      '#suffix' => '</div>',
    ],
    'back' => [
      '#type' => 'link',
      '#title' => t('Go back'),
      '#url' => Url::fromRoute('<current>'),
    ],
  ];

  return $form;
}

/**
 * Implements hook_entity_type_build().
 */
function inline_documentation_entity_type_build(array &$entity_types) {
  // Add our custom form display mode to the classes so the display can be
  // called programmatically.
  $entity_types['node']->setFormClass('inline_documentation', 'Drupal\node\NodeForm');
}

/**
 * Implements hook_preprocess_node().
 */
function inline_documentation_preprocess_node(&$variables) {
  // Check for the inline_documentation node type.
  if ($variables['node']->getType() == 'inline_documentation' && $variables['view_mode'] == 'inline_documentation') {
    // Get the value of field_inline_doc_page_element.
    $field_inline_doc_page_element = $variables['node']->get('field_inline_doc_page_element')->getValue();
    // Add the value as a data attribute.
    if (!empty($field_inline_doc_page_element[0]['value'])) {
      $variables['attributes']['data-field-inline-doc-page-element'] = $field_inline_doc_page_element[0]['value'];
    }

    // Get the value of field_inline_doc_color.
    $field_inline_doc_color = $variables['node']->get('field_inline_doc_color')->getValue();
    // Add the value as a data attribute.
    if (!empty($field_inline_doc_color[0]['value'])) {
      $variables['attributes']['data-field-inline-doc-color'] = $field_inline_doc_color[0]['value'];
      $variables['attributes']['style'] = 'border-color: ' . $field_inline_doc_color[0]['value'];
    }

    // Add extra class if the node is not published.
    if (!$variables['node']->isPublished()) {
      $variables['attributes']['class'][] = 'unpublished';
    }
  }
}

/**
 * Helper function to load inline documentation by page(s).
 *
 * @param array $pages
 *   Contains page paths to load inline documentation for.
 * @param bool $load_entities
 *   Defines if node IDs or fully loaded node objects should be returned.
 *
 * @return array
 *   Inline documentations IDs or node objects for the given page(s).
 */
function inline_documentation_load_by_page(array $pages = [], $load_entities = TRUE) {
  // Get the current path if no pages are given.
  if (empty($pages)) {
    $pages = inline_documentation_get_current_page();
  }

  // Set up a node entity query.
  $query = \Drupal::entityQuery('node')
    ->accessCheck(TRUE)
    ->condition('type', 'inline_documentation');

  // Create an or group because we want to check each path individually.
  $orGroup = $query->orConditionGroup();
  foreach ($pages as $page) {
    $orGroup->condition('field_inline_doc_pages', '%' . $page . "\r\n%", 'LIKE');
  }
  // Also, check for an empty string indicating the documentation should be
  // displayed on all pages.
  $orGroup->condition('field_inline_doc_pages', NULL, 'IS NULL');

  // Add the or group to the query.
  $query->condition($orGroup);
  $query->sort('field_inline_doc_weight', 'ASC');
  // Execute the query and retrieve the node ids.
  $query->accessCheck();
  $nids = $query->execute();

  // Load entity objects if enabled.
  if ($load_entities) {
    $nodes = Node::loadMultiple($nids);
    return $nodes;
  }

  return $nids;
}

/**
 * Gets the current page url in multiple formats.
 *
 * A page array will be created containing the following formats:
 * - <front> if it's the frontpage.
 * - The request uri as shown in the browser.
 * - The internal path.
 * - Placeholderized versions of the request uri and internal path.
 *
 * Example: /blog/category/title
 * Returns: [
 *   /blog/category/title
 *   /blog/category/*
 *   /blog/*
 *   /node/3
 *   /node/*
 * ]
 *
 * @return array
 *   An array containing all possible urls of the current page.
 */
function inline_documentation_get_current_page() {
  $pages = [];

  $current_url = Url::fromRoute('<current>');
  $internal_path = '/' . $current_url->getInternalPath();
  $request_uri = $current_url->toString();

  // Check if the current page is the frontpage.
  if (\Drupal::service('path.matcher')->isFrontPage()) {
    $pages['<front>'] = '<front>';
  }
  else {
    // Add the internal path to the pages array.
    $pages[$internal_path] = $internal_path;
    // Replace all parts with *.
    $pages += inline_documentation_placeholderize_path($internal_path);

    // Add the uri as shown in the browser to the pages array.
    $pages[$request_uri] = $request_uri;
    // Replace all parts with *.
    $pages += inline_documentation_placeholderize_path($request_uri);
  }

  return $pages;
}

/**
 * Break down a path into multiple paths with placeholders.
 *
 * Example: /blog/category/title becomes:
 * - /blog/category/*
 * - /blog/*
 * - /blog/*\/title
 *
 * @param string $path
 *   Contains a url path starting with a slash.
 *
 * @return array
 *   An array containing multiple paths with placeholders.
 */
function inline_documentation_placeholderize_path($path) {
  $pages = [];

  // Split the path in slashes.
  $slashes = explode('/', substr($path, 1));

  // For every slash, create a new path with placeholder to search for.
  foreach ($slashes as $key => $path_part) {
    $page = '';
    foreach ($slashes as $key2 => $path_part2) {
      if ($key2 < $key) {
        $page .= '/' . $path_part2;
      }
      elseif (!empty($page) && substr($page, -2) != '/*') {
        $page .= '/*';
      }
    }

    if (!empty($page)) {
      $pages[$page] = $page;
    }
  }

  // Go through the path one more time to replace the middle parts with *.
  foreach ($slashes as $key => $path_part) {
    if ($key > 0) {
      $slashes2 = $slashes;
      $slashes2[$key] = '*';
      $page = '/' . implode('/', $slashes2);
      $pages[$page] = $page;
    }
  }

  return $pages;
}

/**
 * Implements hook_entity_presave().
 *
 * Force a newline after the field_inline_doc_pages value.
 */
function inline_documentation_entity_presave(EntityInterface $entity) {
  // Only do this on inline_documentation nodes.
  if ($entity->bundle() == 'inline_documentation') {
    // Explode the pages on newlines.
    $pages = explode("\r\n", $entity->field_inline_doc_pages->value);

    // Create new string for the sanitized pages.
    $new_pages = '';

    // Check if there are pages.
    if (!empty($pages)) {
      // Iterate through them.
      foreach ($pages as $page) {
        // Remove surrounding whitespaces.
        $page = trim($page);
        // Add it to the new var if it's not empty.
        if (!empty($page)) {
          $new_pages .= $page . "\r\n";
        }
      }
    }

    // Save the new value.
    $entity->field_inline_doc_pages->value = $new_pages;
  }
}

/**
 * Implements hook_theme().
 */
function inline_documentation_theme($existing, $type, $theme, $path) {
  return [
    'inline_documentation_overview' => [
      'variables' => [
        'nodes' => NULL,
        'form' => NULL,
      ],
    ],
    'node__inline_documentation__inline_documentation' => [
      'template' => 'node--inline-documentation--inline-documentation',
      'base hook' => 'node',
    ],
  ];
}

/**
 * Implements hook_node_view_alter().
 *
 * Add metadata to the contextual links so the node edit url can be changed in
 * hook_contextual_view_alter().
 */
function inline_documentation_node_view_alter(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display) {
  $bundle = $entity->bundle();

  // Check if the node is of the type inline_documentation and the view mode
  // also is inline_documentation. Also check if the node has contextual links.
  if ($bundle == 'inline_documentation' && $build['#view_mode'] == 'inline_documentation' && isset($build['#contextual_links']['node'])) {
    // Put the node type and view mode in the contextual metadata.
    $build['#contextual_links']['node']['metadata']['bundle'] = $bundle;
    $build['#contextual_links']['node']['metadata']['view_mode'] = $build['#view_mode'];

    // Get the current url + query as shown in the browser.
    $edit_url = \Drupal::request()->getRequestUri();
    // Add a ? or & to the url.
    if (strstr($edit_url, '?')) {
      $edit_url .= '&';
    }
    else {
      $edit_url .= '?';
    }
    // Add the inline doc to edit to the url.
    $edit_url .= 'edit-inline-doc=' . $entity->id();

    // Add the url to the metadata so it can be read by the alter hook.
    $build['#contextual_links']['node']['metadata']['edit_url'] = $edit_url;
  }
}

/**
 * Implements hook_contextual_links_view_alter().
 *
 * Set a new node edit url in the contextual links for inline documentation
 * nodes in the overview.
 */
function inline_documentation_contextual_links_view_alter(&$element, $items) {
  // Check if the node bundle and view mode are set in the metadata and check
  // if they contain 'inline_documentation'. Also check if the edit url is set.
  if (
    isset($element['#contextual_links']['node']['metadata']['bundle']) &&
    $element['#contextual_links']['node']['metadata']['bundle'] == 'inline_documentation' &&
    isset($element['#contextual_links']['node']['metadata']['view_mode']) &&
    $element['#contextual_links']['node']['metadata']['view_mode'] == 'inline_documentation' &&
    isset($element['#contextual_links']['node']['metadata']['edit_url'])
  ) {
    // Get the node edit url.
    $edit_url = $element['#contextual_links']['node']['metadata']['edit_url'];

    // Overwrite the node edit url with ours.
    if (!empty($element['#links']['entitynodeedit-form']['url'])) {
      $element['#links']['entitynodeedit-form']['url'] = Url::fromUserInput($edit_url);
    }
  }
}

/**
 * Implements hook_help().
 */
function inline_documentation_help($route_name, RouteMatchInterface $route_match) {
  switch ($route_name) {
    case 'help.page.inline_documentation':
      $text = file_get_contents(__DIR__ . '/README.md');
      if (!\Drupal::moduleHandler()->moduleExists('markdown')) {
        return '<pre>' . Html::escape($text) . '</pre>';
      }
      else {
        // Use the Markdown filter to render the README.
        $filter_manager = \Drupal::service('plugin.manager.filter');
        $settings = \Drupal::configFactory()->get('markdown.settings')->getRawData();
        $config = ['settings' => $settings];
        $filter = $filter_manager->createInstance('markdown', $config);
        return $filter->process($text, 'en');
      }
  }
  return NULL;
}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc