You are here

EditorNoteHelperService.php in Editor Notes 8

Namespace

Drupal\editor_note

File

src/EditorNoteHelperService.php
View source
<?php

namespace Drupal\editor_note;

use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Xss;
use Drupal\Core\Database\Connection;
use Drupal\Core\Datetime\DateFormatter;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Session\AccountProxy;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url;
use Drupal\editor_note\Entity\EditorNote;
use Drupal\field\Entity\FieldConfig;

/**
 * Class EditorNoteHelperService.
 */
class EditorNoteHelperService {
  use StringTranslationTrait;

  /**
   * Drupal\Core\Entity\EntityTypeManagerInterface definition.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * Date formatter service.
   *
   * @var \Drupal\Core\Datetime\DateFormatter
   */
  protected $dateFormatter;

  /**
   * The database connection.
   *
   * @var \Drupal\Core\Database\Connection
   */
  protected $connection;

  /**
   * Current user object.
   *
   * @var \Drupal\Core\Session\AccountProxy
   */
  protected $currentUser;

  /**
   * The module handler to invoke the alter hook with.
   *
   * @var \Drupal\Core\Extension\ModuleHandlerInterface
   */
  protected $moduleHandler;

  /**
   * Constructs a new OnboardStationEntityHelper object.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   Entity type manager service.
   * @param \Drupal\Core\Datetime\DateFormatter $date_formatter
   *   Date format service.
   * @param \Drupal\Core\Database\Connection $connection
   *   The database connection.
   * @param \Drupal\Core\Session\AccountProxy $current_user
   *   Current user object.
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
   *   The module handler to invoke the alter hook with.
   */
  public function __construct(EntityTypeManagerInterface $entity_type_manager, DateFormatter $date_formatter, Connection $connection, AccountProxy $current_user, ModuleHandlerInterface $module_handler) {
    $this->entityTypeManager = $entity_type_manager;
    $this->dateFormatter = $date_formatter;
    $this->connection = $connection;
    $this->currentUser = $current_user;
    $this->moduleHandler = $module_handler;
  }

  /**
   * Create 'Editor Node' entity.
   *
   * @param \Drupal\Core\Entity\ContentEntityInterface $host_entity
   *   Host entity to attach editor note.
   * @param string $field_machine_name
   *   Host entity editor note field machine name.
   * @param string $note
   *   Editor note.
   * @param string $format
   *   Text format.
   */
  public function createNote(ContentEntityInterface $host_entity, $field_machine_name, $note, $format = 'plain_text') {
    $storage = $this->entityTypeManager
      ->getStorage('editor_note');
    $entityType = $host_entity
      ->getEntityType();
    $data = [
      'entity_id' => $host_entity
        ->id(),
      'entity_type' => $entityType
        ->id(),
      'revision_id' => $host_entity
        ->getRevisionId() ?? 0,
      'note' => [
        'value' => $note,
        'format' => $format,
      ],
      'bundle' => $host_entity
        ->bundle(),
      'field_machine_name' => $field_machine_name,
    ];
    $note = $storage
      ->create($data);
    $note
      ->save();
  }

  /**
   * Returns Editor Note entity ids for passed entity and field name.
   *
   * @param int $host_entity_id
   *   Host entity ID.
   * @param string $field_machine_name
   *   Field machine name.
   * @param array $settings
   *   Widget settings.
   *
   * @return mixed
   *   A single field from the next record, or FALSE if there is no next record.
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  public function getNotesByEntityAndField($host_entity_id, $field_machine_name, $settings = []) {
    $query = $this->connection
      ->select('editor_note', 'en');
    $query
      ->fields('en', [
      'id',
    ]);
    $query
      ->condition('en.entity_id', $host_entity_id);
    $query
      ->condition('en.field_machine_name', $field_machine_name);
    $query
      ->orderBy('en.changed', 'DESC');
    if (!empty($settings['limit']) && $settings['limit'] > 0) {
      $query
        ->range(0, $settings['limit']);

      //      Not fully implemented pagination flow.
      //      @see https://www.drupal.org/project/editor_note/issues/3087584
      //      $query = $query->extend('Drupal\Core\Database\Query\PagerSelectExtender')
      //        ->limit($settings['limit']);
    }
    $record_ids = $query
      ->execute()
      ->fetchAllKeyed(0, 0);
    if ($record_ids) {
      return $this->entityTypeManager
        ->getStorage('editor_note')
        ->loadMultiple($record_ids);
    }
    return [];
  }

  /**
   * Returns field definition for a note.
   *
   * @param \Drupal\editor_note\Entity\EditorNote $editorNote
   *   Editor note entity.
   *
   * @return \Drupal\Core\Field\FieldDefinitionInterface
   *   Returns field definition object.
   */
  public function getNoteFieldDefinition(EditorNote $editorNote) {
    $field_name = $editorNote->field_machine_name->value;
    $host_entity_type = $editorNote->entity_type->value;
    $host_entity_bundle = $editorNote->bundle->value;
    return FieldConfig::loadByName($host_entity_type, $host_entity_bundle, $field_name);
  }

  /**
   * Returns editor note field widget settings.
   *
   * @param \Drupal\editor_note\Entity\EditorNote $editorNote
   * @param $field_machine_name
   *
   * @return mixed
   */
  public function getNoteFieldWidgetSettings(EditorNote $editorNote, $field_machine_name) {
    $id = $editorNote->entity_type->value . '.' . $editorNote->bundle->value . '.default';
    $entity_form_display = EntityFormDisplay::load($id);
    $component = $entity_form_display
      ->getComponent($field_machine_name);
    return $component['settings'];
  }

  /**
   * Returns ajax replace command for refreshing field widget.
   *
   * @param \Drupal\editor_note\Entity\EditorNote $editorNote
   *
   * @return \Drupal\Core\Ajax\ReplaceCommand
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  public function getWidgetAjaxReplaceCommand(EditorNote $editorNote) {
    $field_config = $this
      ->getNoteFieldDefinition($editorNote);
    $host_entity_id = $editorNote->entity_id->target_id;
    $field_machine_name = $editorNote->field_machine_name->value;
    $widget_settings = $this
      ->getNoteFieldWidgetSettings($editorNote, $field_machine_name);
    $notes = $this
      ->getNotesByEntityAndField($host_entity_id, $field_machine_name, $widget_settings);
    $table = $this
      ->generateTable($field_config, $notes, TRUE);
    return new ReplaceCommand('#formatted_notes_' . $field_machine_name, $table);
  }

  /**
   * Returns formatted notes table.
   *
   * @param \Drupal\Core\Field\FieldDefinitionInterface $field
   *   Field definition object.
   * @param array $notes
   *   Array of notes data returned by editor_note_get_notes().
   * @param bool $widget
   *   Determines whether to use function in widget along with controls or
   *   display it in formatter as just a table without controls.
   * @param string|null $edit_path
   *   Path of the edit form where field is used. Fixes pager on ajax refresh.
   *
   * @return array
   *   Returns formatted notes ready for rendering.
   *
   * @see editor_note_get_notes()
   */
  public function generateTable(FieldDefinitionInterface $field, array $notes, $widget = FALSE, $edit_path = NULL) {
    $formatted_notes = [
      '#prefix' => '<div id="formatted_notes_' . $field
        ->getName() . '">',
      '#suffix' => '</div>',
    ];
    if (!empty($notes)) {
      $rows = [];
      $counter = 0;
      $headers = $this
        ->generateHeaders($widget);
      foreach ($notes as $note_id => $item) {
        $rows[$counter] = $this
          ->generateRow($item, $widget, $field
          ->getName(), $note_id);
        $counter++;
      }
      $notes_table = [
        '#theme' => 'table',
        '#header' => $headers,
        '#rows' => $rows,
        '#attributes' => [
          'class' => [
            'field-notes-table',
          ],
        ],
      ];
      if (FALSE) {

        // @todo: Implement it.
        // if ($field['settings']['pager']['enabled']) {
        // An optional integer to distinguish between multiple pagers on
        // one page in case if 2 fields are present at the same time.
        static $page_element = 0;

        // Fixes pager on ajax refresh.
        // Otherwise pager links point on /system/ajax after ajax refresh.
        // @see https://www.drupal.org/node/1181370#comment-6088864
        // for more details.
        // @see theme_pager_link()
        if ($edit_path) {
          $_GET['q'] = $edit_path;
        }
        if ($field['settings']['pager']['pager_below']) {
          $formatted_notes['notes_table'] = $notes_table;
          $formatted_notes['notes_table_pager'] = [
            '#theme' => 'pager',
            '#element' => $page_element,
          ];
        }
        else {
          $formatted_notes['notes_table_pager'] = [
            '#theme' => 'pager',
            '#element' => $page_element,
          ];
          $formatted_notes['notes_table'] = $notes_table;
        }
        if (module_exists('field_group')) {

          // Remember which tab was active after page reload
          // when navigating between pager links.
          $settings = [
            'editorNoteContainer' => drupal_html_class('edit_link-' . $field['field_name']),
          ];
          $formatted_notes['notes_table']['#attached']['js'][] = [
            'data' => $settings,
            'type' => 'setting',
          ];
          $formatted_notes['notes_table']['#attached']['js'][] = drupal_get_path('module', 'editor_note') . '/js/editor_note.js';
        }
        $page_element++;

        //        Not fully implemented pagination flow.
        //        @see https://www.drupal.org/project/editor_note/issues/3087584
        //        if (!empty($settings) && $settings['pager_enabled']) {
        //          if ($settings['pager_below']) {
        //            $formatted_notes = [
        //              'notes_table' => $notes_table,
        //              'notes_pager' => [
        //                '#type' => 'pager',
        //              ],
        //            ];
        //          }
        //          else {
        //            $formatted_notes = [
        //              'notes_pager' => [
        //                '#type' => 'pager',
        //              ],
        //              'notes_table' => $notes_table,
        //            ];
        //          }
        //          $formatted_notes['notes_table']['#attached']['library'][] = 'editor_note/editor_note';
        //        }
      }
      else {
        $formatted_notes['notes_table'] = $notes_table;
      }
    }

    // Hook is to allow other modules to alter the formatted notes
    // before they are rendered.
    $this->moduleHandler
      ->alter('editor_note_format_notes', $formatted_notes);
    return $formatted_notes;
  }

  /**
   * Helper to prepare headers for the table.
   *
   * @param bool $widget
   *   Determines whether to use function in widget along with controls or
   *   display it in formatter as just a table without controls.
   *
   * @return array
   *   Headers data for the table.
   */
  protected function generateHeaders($widget) {
    $headers = [
      [
        'data' => $this
          ->t('Notes'),
        'class' => [
          'field-label',
        ],
      ],
      [
        'data' => $this
          ->t('Updated by'),
        'class' => [
          'field-author',
        ],
      ],
      [
        'data' => $this
          ->t('Changed'),
        'class' => [
          'field-changed',
        ],
      ],
      [
        'data' => $this
          ->t('Created'),
        'class' => [
          'field-created',
        ],
      ],
    ];
    if ($widget) {
      $headers[] = [
        'data' => $this
          ->t('Actions'),
        'class' => [
          'field-operations',
        ],
      ];
    }
    return $headers;
  }

  /**
   * Generates one Editor Notes table row.
   *
   * @param \Drupal\Core\Entity\ContentEntityInterface $item
   *   Host entity to attach editor note.
   * @param bool $widget
   *   Determines whether to use function in widget along with controls or
   *   display it in formatter as just a table without controls.
   * @param string $field_name
   *   Field machine name.
   * @param int $note_id
   *   Row number.
   *
   * @return array
   *   Row data.
   */
  protected function generateRow(ContentEntityInterface $item, $widget, $field_name, $note_id) {

    /** @var \Drupal\user\UserInterface $author */
    $author = $item->uid->entity;
    $author_name = $author
      ->label();
    $row = [
      'data' => [
        'note' => [
          'data' => [
            '#markup' => Xss::filterAdmin($item->note->value),
          ],
          'class' => [
            'note',
          ],
        ],
        'author' => [
          'uid' => $author
            ->id(),
          'data' => $author
            ->hasPermission('administer users') ? $author
            ->toLink($author_name) : $author_name,
          'class' => [
            'author',
          ],
        ],
        'changed' => [
          'data' => $this->dateFormatter
            ->format($item->changed->value, 'short'),
          'class' => [
            'changed',
          ],
        ],
        'created' => [
          'data' => $this->dateFormatter
            ->format($item->created->value, 'short'),
          'class' => [
            'created',
          ],
        ],
      ],
      'class' => [
        Html::cleanCssIdentifier('note-' . $note_id),
      ],
    ];
    $user_access = $this->currentUser
      ->id() == $item->uid->target_id || $this->currentUser
      ->hasPermission('administer any editor note');
    if ($widget && $user_access) {

      // !editor_note_access_crud_operations($field['field_name'], $note_id)
      $edit_link = [
        '#type' => 'link',
        '#title' => $this
          ->t('Edit'),
        '#url' => Url::fromRoute('editor_note.modal_form', [
          'editor_note' => $note_id,
          'nojs' => 'ajax',
        ]),
        '#attributes' => [
          'class' => [
            'use-ajax',
            'ctools-modal-' . $field_name . '-edit_link',
          ],
          'data-dialog-type' => 'modal',
          'data-dialog-options' => json_encode([
            'width' => '50%',
          ]),
        ],
      ];
      $delete_link = [
        '#type' => 'link',
        '#title' => $this
          ->t('Remove'),
        '#url' => Url::fromRoute('editor_note.confirm_delete_editor_note_form', [
          'editor_note' => $note_id,
          'nojs' => 'ajax',
        ]),
        '#attributes' => [
          'class' => [
            'use-ajax',
            'ctools-modal-' . $field_name . '-remove',
          ],
          'data-dialog-type' => 'modal',
          'data-dialog-options' => json_encode([
            'width' => '50%',
          ]),
        ],
      ];
      $basic_items[] = render($edit_link);
      $basic_items[] = render($delete_link);
      $row['data']['operations']['data'] = [
        '#theme' => 'item_list',
        '#items' => $basic_items,
      ];
    }
    return $row;
  }

}

Classes

Namesort descending Description
EditorNoteHelperService Class EditorNoteHelperService.