quickedit.module 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. <?php
  2. /**
  3. * @file
  4. * Provides in-place content editing functionality for fields.
  5. *
  6. * The Quick Edit module makes content editable in-place. Rather than having to
  7. * visit a separate page to edit content, it may be edited in-place.
  8. *
  9. * Technically, this module adds classes and data- attributes to fields and
  10. * entities, enabling them for in-place editing.
  11. */
  12. use Drupal\Core\Url;
  13. use Drupal\Core\Entity\EntityInterface;
  14. use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
  15. use Drupal\Core\Entity\RevisionableInterface;
  16. use Drupal\Core\Routing\RouteMatchInterface;
  17. /**
  18. * Implements hook_help().
  19. */
  20. function quickedit_help($route_name, RouteMatchInterface $route_match) {
  21. switch ($route_name) {
  22. case 'help.page.quickedit':
  23. $output = '<h3>' . t('About') . '</h3>';
  24. $output .= '<p>' . t('The Quick Edit module allows users with the <a href=":quickedit_permission">Access in-place editing</a> and <a href=":contextual_permission">Use contextual links</a> permissions to edit field content without visiting a separate page. For more information, see the <a href=":handbook_url">online documentation for the Quick Edit module</a>.', [':handbook_url' => 'https://www.drupal.org/documentation/modules/edit', ':quickedit_permission' => Url::fromRoute('user.admin_permissions', [], ['fragment' => 'module-quickedit'])->toString(), ':contextual_permission' => Url::fromRoute('user.admin_permissions', [], ['fragment' => 'module-contextual'])->toString()]) . '</p>';
  25. $output .= '<h3>' . t('Uses') . '</h3>';
  26. $output .= '<dl>';
  27. $output .= '<dt>' . t('Editing content in-place') . '</dt>';
  28. $output .= '<dd>';
  29. $output .= '<p>' . t('To edit content in place, you need to activate quick edit mode for a content item. Activate quick edit mode by choosing Quick edit from the contextual links for an area displaying the content (see the <a href=":contextual">Contextual Links module help</a> for more information about how to use contextual links).', [':contextual' => Url::fromRoute('help.page', ['name' => 'contextual'])->toString()]) . '</p>';
  30. $output .= '<p>' . t('Once quick edit mode is activated, you will be able to edit the individual fields of your content. In the default theme, with a JavaScript-enabled browser and a mouse, the output of different fields in your content is outlined in blue, a pop-up gives the field name as you hover over the field output, and clicking on a field activates the editor. Closing the pop-up window ends quick edit mode.') . '</p>';
  31. $output .= '</dd>';
  32. $output .= '</dl>';
  33. return $output;
  34. }
  35. }
  36. /**
  37. * Implements hook_page_attachments().
  38. *
  39. * Adds the quickedit library to the page for any user who has the 'access
  40. * in-place editing' permission.
  41. */
  42. function quickedit_page_attachments(array &$page) {
  43. if (!\Drupal::currentUser()->hasPermission('access in-place editing')) {
  44. return;
  45. }
  46. // In-place editing is only supported on the front-end.
  47. if (\Drupal::service('router.admin_context')->isAdminRoute()) {
  48. return;
  49. }
  50. $page['#attached']['library'][] = 'quickedit/quickedit';
  51. }
  52. /**
  53. * Implements hook_library_info_alter().
  54. *
  55. * Includes additional stylesheets defined by the admin theme to allow it to
  56. * customize the Quick Edit toolbar appearance.
  57. *
  58. * An admin theme can specify CSS files to make the front-end administration
  59. * experience of in-place editing match the administration experience in the
  60. * back-end.
  61. *
  62. * The CSS files can be specified via the "edit_stylesheets" property in the
  63. * .info.yml file:
  64. * @code
  65. * quickedit_stylesheets:
  66. * - css/quickedit.css
  67. * @endcode
  68. */
  69. function quickedit_library_info_alter(&$libraries, $extension) {
  70. if ($extension === 'quickedit' && isset($libraries['quickedit'])) {
  71. $theme = Drupal::config('system.theme')->get('admin');
  72. // First let the base theme modify the library, then the actual theme.
  73. $alter_library = function (&$library, $theme) use (&$alter_library) {
  74. if (!empty($theme) && $theme_path = drupal_get_path('theme', $theme)) {
  75. $info = \Drupal::service('extension.list.theme')->getExtensionInfo($theme);
  76. // Recurse to process base theme(s) first.
  77. if (isset($info['base theme'])) {
  78. $alter_library($library, $info['base theme']);
  79. }
  80. if (isset($info['quickedit_stylesheets'])) {
  81. foreach ($info['quickedit_stylesheets'] as $path) {
  82. $library['css']['theme']['/' . $theme_path . '/' . $path] = [];
  83. }
  84. }
  85. }
  86. };
  87. $alter_library($libraries['quickedit'], $theme);
  88. }
  89. }
  90. /**
  91. * Implements hook_field_formatter_info_alter().
  92. *
  93. * Quick Edit extends the @FieldFormatter annotation with the following keys:
  94. * - quickedit: currently only contains one subkey 'editor' which indicates
  95. * which in-place editor should be used. Possible values are 'form',
  96. * 'plain_text', 'disabled' or another in-place editor other than the ones
  97. * Quick Edit module provides.
  98. */
  99. function quickedit_field_formatter_info_alter(&$info) {
  100. foreach ($info as $key => $settings) {
  101. // Set in-place editor to 'form' if none is supplied.
  102. if (empty($settings['quickedit'])) {
  103. $info[$key]['quickedit'] = ['editor' => 'form'];
  104. }
  105. }
  106. }
  107. /**
  108. * Implements hook_preprocess_HOOK() for the page title template.
  109. */
  110. function quickedit_preprocess_page_title(&$variables) {
  111. $variables['#cache']['contexts'][] = 'user.permissions';
  112. if (\Drupal::currentUser()->hasPermission('access in-place editing')) {
  113. $variables['title_attributes']['class'][] = 'js-quickedit-page-title';
  114. }
  115. }
  116. /**
  117. * Implements hook_preprocess_HOOK() for field templates.
  118. */
  119. function quickedit_preprocess_field(&$variables) {
  120. $variables['#cache']['contexts'][] = 'user.permissions';
  121. $element = $variables['element'];
  122. /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
  123. $entity = $element['#object'];
  124. if (!\Drupal::currentUser()->hasPermission('access in-place editing') || ($entity instanceof RevisionableInterface && !$entity->isLatestRevision())) {
  125. return;
  126. }
  127. // Quick Edit module only supports view modes, not dynamically defined
  128. // "display options" (which \Drupal\Core\Field\FieldItemListInterface::view()
  129. // always names the "_custom" view mode).
  130. // @see \Drupal\Core\Field\FieldItemListInterface::view()
  131. // @see https://www.drupal.org/node/2120335
  132. if ($element['#view_mode'] === '_custom') {
  133. return;
  134. }
  135. // Fields that are computed fields are not editable.
  136. $definition = $entity->getFieldDefinition($element['#field_name']);
  137. if (!$definition->isComputed()) {
  138. $variables['attributes']['data-quickedit-field-id'] = $entity->getEntityTypeId() . '/' . $entity->id() . '/' . $element['#field_name'] . '/' . $element['#language'] . '/' . $element['#view_mode'];
  139. }
  140. }
  141. /**
  142. * Implements hook_entity_view_alter().
  143. */
  144. function quickedit_entity_view_alter(&$build, EntityInterface $entity, EntityViewDisplayInterface $display) {
  145. if (isset($build['#embed'])) {
  146. return;
  147. }
  148. $build['#cache']['contexts'][] = 'user.permissions';
  149. if (!\Drupal::currentUser()->hasPermission('access in-place editing') || ($entity instanceof RevisionableInterface && !$entity->isLatestRevision())) {
  150. return;
  151. }
  152. $build['#attributes']['data-quickedit-entity-id'] = $entity->getEntityTypeId() . '/' . $entity->id();
  153. }
  154. /**
  155. * Check if a loaded entity is the latest revision.
  156. *
  157. * @param \Drupal\Core\Entity\RevisionableInterface $entity
  158. * The entity to check.
  159. *
  160. * @return bool
  161. * TRUE if the loaded entity is the latest revision, FALSE otherwise.
  162. *
  163. * @deprecated in drupal:8.5.0 and is removed from drupal:9.0.0. Use
  164. * \Drupal\Core\Entity\RevisionableInterface::isLatestRevision() instead.
  165. * As internal API, _quickedit_entity_is_latest_revision() may also be removed
  166. * in a minor release.
  167. *
  168. * @internal
  169. */
  170. function _quickedit_entity_is_latest_revision(RevisionableInterface $entity) {
  171. @trigger_error('_quickedit_entity_is_latest_revision() is deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Entity\RevisionableInterface::isLatestRevision() instead. As internal API, _quickedit_entity_is_latest_revision() may also be removed in a minor release.', E_USER_DEPRECATED);
  172. return $entity->isLatestRevision();
  173. }