Issue #1932652 by Wim Leers, nirbhasa, quicksketch: Add image uploading to WYSIWYGs through editor.module.
parent
595d6d9ad7
commit
5429044d5e
|
@ -314,6 +314,18 @@ function hook_entity_delete(Drupal\Core\Entity\EntityInterface $entity) {
|
|||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Respond to entity revision deletion.
|
||||
*
|
||||
* This hook runs after the entity type-specific revision delete hook.
|
||||
*
|
||||
* @param Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity object for the entity revision that has been deleted.
|
||||
*/
|
||||
function hook_entity_revision_delete(Drupal\Core\Entity\EntityInterface $entity) {
|
||||
// @todo: code example
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter or execute an Drupal\Core\Entity\Query\EntityQueryInterface.
|
||||
*
|
||||
|
|
|
@ -79,6 +79,20 @@ function ckeditor_library_info() {
|
|||
array('system', 'underscore')
|
||||
),
|
||||
);
|
||||
$libraries['drupal.ckeditor.drupalimage.admin'] = array(
|
||||
'title' => 'Only show the "drupalimage" plugin settings when its button is enabled.',
|
||||
'version' => VERSION,
|
||||
'js' => array(
|
||||
$module_path . '/js/ckeditor.drupalimage.admin.js' => array(),
|
||||
),
|
||||
'dependencies' => array(
|
||||
array('system', 'jquery'),
|
||||
array('system', 'drupal'),
|
||||
array('system', 'jquery.once'),
|
||||
array('system', 'drupal.vertical-tabs'),
|
||||
array('system', 'drupalSettings'),
|
||||
),
|
||||
);
|
||||
$libraries['drupal.ckeditor.stylescombo.admin'] = array(
|
||||
'title' => 'Only show the "stylescombo" plugin settings when its button is enabled.',
|
||||
'version' => VERSION,
|
||||
|
@ -91,8 +105,6 @@ function ckeditor_library_info() {
|
|||
array('system', 'jquery.once'),
|
||||
array('system', 'drupal.vertical-tabs'),
|
||||
array('system', 'drupalSettings'),
|
||||
// @todo D8 formUpdated event should be debounced already.
|
||||
array('system', 'drupal.debounce'),
|
||||
),
|
||||
);
|
||||
$libraries['ckeditor'] = array(
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
(function ($, Drupal, drupalSettings) {
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Shows the "drupalimage" plugin settings only when the button is enabled.
|
||||
*/
|
||||
Drupal.behaviors.ckeditorDrupalImageSettings = {
|
||||
attach: function (context) {
|
||||
var $context = $(context);
|
||||
var $drupalImageVerticalTab = $('#edit-editor-settings-plugins-drupalimage').data('verticalTab');
|
||||
|
||||
// Hide if the "DrupalImage" button is disabled.
|
||||
if ($('.ckeditor-toolbar-disabled li[data-button-name="DrupalImage"]').length === 1) {
|
||||
$drupalImageVerticalTab.tabHide();
|
||||
}
|
||||
|
||||
// React to added/removed toolbar buttons.
|
||||
$context
|
||||
.find('.ckeditor-toolbar-active')
|
||||
.on('CKEditorToolbarChanged.ckeditorDrupalImageSettings', function (e, action, button) {
|
||||
if (button === 'DrupalImage') {
|
||||
if (action === 'added') {
|
||||
$drupalImageVerticalTab.tabShow();
|
||||
}
|
||||
else {
|
||||
$drupalImageVerticalTab.tabHide();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Provides the summary for the "drupalimage" plugin settings vertical tab.
|
||||
*/
|
||||
Drupal.behaviors.ckeditorDrupalImageSettingsSummary = {
|
||||
attach: function () {
|
||||
$('#edit-editor-settings-plugins-drupalimage').drupalSetSummary(function (context) {
|
||||
var root = 'input[name="editor[settings][plugins][drupalimage][image_upload]';
|
||||
var $status = $(root + '[status]"]');
|
||||
var $maxFileSize = $(root + '[max_size]"]');
|
||||
var $maxWidth = $(root + '[max_dimensions][width]"]');
|
||||
var $maxHeight = $(root + '[max_dimensions][height]"]');
|
||||
var $scheme = $(root + '[scheme]"]:checked');
|
||||
|
||||
var maxFileSize = $maxFileSize.val() ? $maxFileSize.val() : $maxFileSize.attr('placeholder');
|
||||
var maxDimensions = ($maxWidth.val() && $maxHeight.val()) ? '(' + $maxWidth.val() + 'x' + $maxHeight.val() + ')' : '';
|
||||
|
||||
if (!$status.is(':checked')) {
|
||||
return Drupal.t('Uploads disabled');
|
||||
}
|
||||
|
||||
var output = '';
|
||||
output += Drupal.t('Uploads enabled, max size: @size @dimensions', { '@size': maxFileSize, '@dimensions': maxDimensions });
|
||||
if ($scheme.length) {
|
||||
output += '<br />' + $scheme.attr('data-label');
|
||||
}
|
||||
return output;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery, Drupal, drupalSettings);
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\ckeditor\Plugin\CKEditorPlugin;
|
||||
|
||||
use Drupal\ckeditor\CKEditorPluginBase;
|
||||
use Drupal\ckeditor\CKEditorPluginConfigurableInterface;
|
||||
use Drupal\ckeditor\Annotation\CKEditorPlugin;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\editor\Entity\Editor;
|
||||
|
@ -17,11 +18,11 @@ use Drupal\editor\Entity\Editor;
|
|||
*
|
||||
* @CKEditorPlugin(
|
||||
* id = "drupalimage",
|
||||
* label = @Translation("Drupal image"),
|
||||
* label = @Translation("Image"),
|
||||
* module = "ckeditor"
|
||||
* )
|
||||
*/
|
||||
class DrupalImage extends CKEditorPluginBase {
|
||||
class DrupalImage extends CKEditorPluginBase implements CKEditorPluginConfigurableInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
@ -61,4 +62,36 @@ class DrupalImage extends CKEditorPluginBase {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \Drupal\editor\Form\EditorImageDialog
|
||||
* @see editor_image_upload_settings_form()
|
||||
*/
|
||||
public function settingsForm(array $form, array &$form_state, Editor $editor) {
|
||||
form_load_include($form_state, 'inc', 'editor', 'editor.admin');
|
||||
$form['image_upload'] = editor_image_upload_settings_form($editor);
|
||||
$form['image_upload']['#attached']['library'][] = array('ckeditor', 'drupal.ckeditor.drupalimage.admin');
|
||||
$form['image_upload']['#element_validate'] = array(
|
||||
array($this, 'validateImageUploadSettings'),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* #element_validate handler for the "image_upload" element in settingsForm().
|
||||
*
|
||||
* Moves the text editor's image upload settings from the DrupalImage plugin's
|
||||
* own settings into $editor->image_upload.
|
||||
*
|
||||
* @see \Drupal\editor\Form\EditorImageDialog
|
||||
* @see editor_image_upload_settings_form()
|
||||
*/
|
||||
function validateImageUploadSettings(array $element, array &$form_state) {
|
||||
$settings = &$form_state['values']['editor']['settings']['plugins']['drupalimage']['image_upload'];
|
||||
$form_state['editor']->image_upload = $settings;
|
||||
unset($form_state['values']['editor']['settings']['plugins']['drupalimage']);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -112,6 +112,7 @@ class CKEditor extends EditorBase implements ContainerFactoryPluginInterface {
|
|||
// CKEditor plugin settings, if any.
|
||||
$form['plugin_settings'] = array(
|
||||
'#type' => 'vertical_tabs',
|
||||
'#title' => t('CKEditor plugin settings'),
|
||||
);
|
||||
$this->ckeditorPluginManager->injectPluginSettingsForm($form, $form_state, $editor);
|
||||
if (count(element_children($form['plugins'])) === 0) {
|
||||
|
|
|
@ -12,6 +12,32 @@ editor.editor.*:
|
|||
label: 'Text editor'
|
||||
settings:
|
||||
type: editor.settings.[%parent.editor]
|
||||
image_upload:
|
||||
type: mapping
|
||||
label: 'Image upload settings'
|
||||
mapping:
|
||||
status:
|
||||
type: boolean
|
||||
label: 'Status'
|
||||
scheme:
|
||||
type: string
|
||||
label: 'File storage'
|
||||
directory:
|
||||
type: string
|
||||
label: 'Upload directory'
|
||||
max_size:
|
||||
type: string
|
||||
label: 'Maximum file size'
|
||||
max_dimensions:
|
||||
type: mapping
|
||||
label: 'Maximum dimensions'
|
||||
mapping:
|
||||
width:
|
||||
type: integer
|
||||
label: 'Maximum width'
|
||||
height:
|
||||
type: integer
|
||||
label: 'Maximum height'
|
||||
status:
|
||||
type: boolean
|
||||
label: 'Status'
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Administration functions for editor.module.
|
||||
*/
|
||||
|
||||
use Drupal\editor\Entity\Editor;
|
||||
|
||||
/**
|
||||
* Subform constructor to configure the text editor's image upload settings.
|
||||
*
|
||||
* Each text editor plugin that is configured to offer the ability to insert
|
||||
* images and uses EditorImageDialog for that, should use this form to update
|
||||
* the text editor's configuration so that EditorImageDialog knows whether it
|
||||
* should allow the user to upload images.
|
||||
*
|
||||
* @param \Drupal\editor\Entity\Editor $editor
|
||||
* The text editor entity that is being edited.
|
||||
*
|
||||
* @return array
|
||||
* The image upload settings form.
|
||||
*
|
||||
* @see \Drupal\editor\Form\EditorImageDialog
|
||||
* @ingroup forms
|
||||
*/
|
||||
function editor_image_upload_settings_form(Editor $editor) {
|
||||
// Defaults.
|
||||
$editor->image_upload = isset($editor->image_upload) ? $editor->image_upload : array();
|
||||
$editor->image_upload += array(
|
||||
'status' => FALSE,
|
||||
'scheme' => file_default_scheme(),
|
||||
'directory' => 'inline-images',
|
||||
'max_size' => '',
|
||||
'max_dimensions' => array('width' => '', 'height' => ''),
|
||||
);
|
||||
|
||||
$form['status'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Enable image uploads'),
|
||||
'#default_value' => $editor->image_upload['status'],
|
||||
'#attributes' => array(
|
||||
'data-editor-image-upload' => 'status',
|
||||
),
|
||||
);
|
||||
$show_if_image_uploads_enabled = array(
|
||||
'visible' => array(
|
||||
':input[data-editor-image-upload="status"]' => array('checked' => TRUE),
|
||||
),
|
||||
);
|
||||
|
||||
// Any visible, writable wrapper can potentially be used for uploads,
|
||||
// including a remote file system that integrates with a CDN.
|
||||
$stream_wrappers = file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE);
|
||||
foreach ($stream_wrappers as $scheme => $info) {
|
||||
$options[$scheme] = $info['description'];
|
||||
}
|
||||
if (!empty($options)) {
|
||||
$form['scheme'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('File storage'),
|
||||
'#default_value' => $editor->image_upload['scheme'],
|
||||
'#options' => $options,
|
||||
'#states' => $show_if_image_uploads_enabled,
|
||||
'#access' => count($options) > 1,
|
||||
);
|
||||
}
|
||||
// Set data- attributes with human-readable names for all possible stream
|
||||
// wrappers, so that drupal.ckeditor.drupalimage.admin's summary rendering
|
||||
// can use that.
|
||||
foreach ($stream_wrappers as $scheme => $info) {
|
||||
$form['scheme'][$scheme]['#attributes']['data-label'] = t('Storage: @name', array('@name' => $info['name']));
|
||||
}
|
||||
|
||||
$form['directory'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#default_value' => $editor->image_upload['directory'],
|
||||
'#title' => t('Upload directory'),
|
||||
'#description' => t("A directory relative to Drupal's files directory where uploaded images will be stored."),
|
||||
'#states' => $show_if_image_uploads_enabled,
|
||||
);
|
||||
|
||||
$default_max_size = format_size(file_upload_max_size());
|
||||
$form['max_size'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#default_value' => $editor->image_upload['max_size'],
|
||||
'#title' => t('Maximum file size'),
|
||||
'#description' => t('If this is left empty, then the file size will be limited by the PHP maximum upload size of @size.', array('@size' => $default_max_size)),
|
||||
'#maxlength' => 20,
|
||||
'#size' => 10,
|
||||
'#placeholder' => $default_max_size,
|
||||
'#states' => $show_if_image_uploads_enabled,
|
||||
);
|
||||
|
||||
$form['max_dimensions'] = array(
|
||||
'#type' => 'item',
|
||||
'#title' => t('Maximum dimensions'),
|
||||
'#field_prefix' => '<div class="container-inline clearfix">',
|
||||
'#field_suffix' => '</div>',
|
||||
'#description' => t('Images larger than these dimensions will be scaled down.'),
|
||||
'#states' => $show_if_image_uploads_enabled,
|
||||
);
|
||||
$form['max_dimensions']['width'] = array(
|
||||
'#title' => t('Width'),
|
||||
'#title_display' => 'invisible',
|
||||
'#type' => 'number',
|
||||
'#default_value' => $editor->image_upload['max_dimensions']['width'],
|
||||
'#size' => 8,
|
||||
'#maxlength' => 8,
|
||||
'#min' => 1,
|
||||
'#max' => 99999,
|
||||
'#placeholder' => 'width',
|
||||
'#field_suffix' => ' x ',
|
||||
'#states' => $show_if_image_uploads_enabled,
|
||||
);
|
||||
$form['max_dimensions']['height'] = array(
|
||||
'#title' => t('Height'),
|
||||
'#title_display' => 'invisible',
|
||||
'#type' => 'number',
|
||||
'#default_value' => $editor->image_upload['max_dimensions']['height'],
|
||||
'#size' => 8,
|
||||
'#maxlength' => 8,
|
||||
'#min' => 1,
|
||||
'#max' => 99999,
|
||||
'#placeholder' => 'height',
|
||||
'#field_suffix' => 'pixels',
|
||||
'#states' => $show_if_image_uploads_enabled,
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
|
@ -8,6 +8,8 @@
|
|||
use Drupal\file\Entity\File;
|
||||
use Drupal\editor\Entity\Editor;
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\field\Field;
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
|
@ -384,3 +386,182 @@ function editor_pre_render_format($element) {
|
|||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_insert().
|
||||
*/
|
||||
function editor_entity_insert(EntityInterface $entity) {
|
||||
$referenced_files_by_field = _editor_get_file_uuids_by_field($entity);
|
||||
foreach ($referenced_files_by_field as $field => $uuids) {
|
||||
_editor_record_file_usage($uuids, $entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_update().
|
||||
*/
|
||||
function editor_entity_update(EntityInterface $entity) {
|
||||
// On new revisions, all files are considered to be a new usage and no
|
||||
// deletion of previous file usages are necessary.
|
||||
if (!empty($entity->original) && $entity->getRevisionId() != $entity->original->getRevisionId()) {
|
||||
$referenced_files_by_field = _editor_get_file_uuids_by_field($entity);
|
||||
foreach ($referenced_files_by_field as $field => $uuids) {
|
||||
_editor_record_file_usage($uuids, $entity);
|
||||
}
|
||||
}
|
||||
// On modified revisions, detect which file references have been added (and
|
||||
// record their usage) and which ones have been removed (delete their usage).
|
||||
// File references that existed both in the previous version of the revision
|
||||
// and in the new one don't need their usage to be updated.
|
||||
else {
|
||||
$original_uuids_by_field = _editor_get_file_uuids_by_field($entity->original);
|
||||
$uuids_by_field = _editor_get_file_uuids_by_field($entity);
|
||||
|
||||
// Detect file usages that should be incremented.
|
||||
foreach ($uuids_by_field as $field => $uuids) {
|
||||
$added_files = array_diff($uuids_by_field[$field], $original_uuids_by_field[$field]);
|
||||
_editor_record_file_usage($added_files, $entity);
|
||||
}
|
||||
|
||||
// Detect file usages that should be decremented.
|
||||
foreach ($original_uuids_by_field as $field => $uuids) {
|
||||
$removed_files = array_diff($original_uuids_by_field[$field], $uuids_by_field[$field]);
|
||||
_editor_delete_file_usage($removed_files, $entity, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_delete().
|
||||
*/
|
||||
function editor_entity_delete(EntityInterface $entity) {
|
||||
$referenced_files_by_field = _editor_get_file_uuids_by_field($entity);
|
||||
foreach ($referenced_files_by_field as $field => $uuids) {
|
||||
_editor_delete_file_usage($uuids, $entity, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_revision_delete().
|
||||
*/
|
||||
function editor_entity_revision_delete(EntityInterface $entity) {
|
||||
$referenced_files_by_field = _editor_get_file_uuids_by_field($entity);
|
||||
foreach ($referenced_files_by_field as $field => $uuids) {
|
||||
_editor_delete_file_usage($uuids, $entity, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Records file usage of files referenced by processed text fields.
|
||||
*
|
||||
* Every referenced file that does not yet have the FILE_STATUS_PERMANENT state,
|
||||
* will be given that state.
|
||||
*
|
||||
* @param array $uuids
|
||||
* An array of file entity UUIDs.
|
||||
* @param EntityInterface $entity
|
||||
* An entity whose fields to inspect for file references.
|
||||
*/
|
||||
function _editor_record_file_usage(array $uuids, EntityInterface $entity) {
|
||||
foreach ($uuids as $uuid) {
|
||||
$file = entity_load_by_uuid('file', $uuid);
|
||||
if ($file->status !== FILE_STATUS_PERMANENT) {
|
||||
$file->status = FILE_STATUS_PERMANENT;
|
||||
$file->save();
|
||||
}
|
||||
file_usage()->add($file, 'editor', $entity->entityType(), $entity->id());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes file usage of files referenced by processed text fields.
|
||||
*
|
||||
* @param array $uuids
|
||||
* An array of file entity UUIDs.
|
||||
* @param EntityInterface $entity
|
||||
* An entity whose fields to inspect for file references.
|
||||
* @param $count
|
||||
* The number of references to delete. Should be 1 when deleting a single
|
||||
* revision and 0 when deleting an entity entirely.
|
||||
*
|
||||
* @see Drupal\file\FileUsage\FileUsageInterface::delete()
|
||||
*/
|
||||
function _editor_delete_file_usage(array $uuids, EntityInterface $entity, $count) {
|
||||
foreach ($uuids as $uuid) {
|
||||
$file = entity_load_by_uuid('file', $uuid);
|
||||
file_usage()->delete($file, 'editor', $entity->entityType(), $entity->id(), $count);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all files referenced (data-editor-file-uuid) by processed text fields.
|
||||
*
|
||||
* @param EntityInterface $entity
|
||||
* An entity whose fields to analyze.
|
||||
*
|
||||
* @return array
|
||||
* An array of file entity UUIDs.
|
||||
*/
|
||||
function _editor_get_file_uuids_by_field(EntityInterface $entity) {
|
||||
$uuids = array();
|
||||
|
||||
$processed_text_fields = _editor_get_processed_text_fields($entity);
|
||||
foreach ($processed_text_fields as $processed_text_field) {
|
||||
$text = $entity->get($processed_text_field)->value;
|
||||
$uuids[$processed_text_field] = _editor_parse_file_uuids($text);
|
||||
}
|
||||
return $uuids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the text fields on an entity that have text processing enabled.
|
||||
*
|
||||
* @param EntityInterface $entity
|
||||
* An entity whose fields to analyze.
|
||||
*
|
||||
* @return array
|
||||
* The names of the fields on this entity that have text processing enabled.
|
||||
*/
|
||||
function _editor_get_processed_text_fields(EntityInterface $entity) {
|
||||
$properties = $entity->getPropertyDefinitions();
|
||||
if (empty($properties)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// Find all configurable fields, because only they could have a
|
||||
// text_processing setting.
|
||||
$configurable_fields = array_keys(array_filter($properties, function ($definition) {
|
||||
return isset($definition['configurable']) && $definition['configurable'] === TRUE;
|
||||
}));
|
||||
if (empty($configurable_fields)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// Only return fields that have text processing enabled.
|
||||
return array_filter($configurable_fields, function ($field) use ($entity) {
|
||||
$settings = Field::fieldInfo()
|
||||
->getInstance($entity->entityType(), $entity->bundle(), $field)
|
||||
->getFieldSettings();
|
||||
return isset($settings['text_processing']) && $settings['text_processing'] === '1';
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an HTML snippet for any data-editor-file-uuid attributes.
|
||||
*
|
||||
* @param string $text
|
||||
* The partial (X)HTML snippet to load. Invalid markup will be corrected on
|
||||
* import.
|
||||
*
|
||||
* @return array
|
||||
* An array of all found UUIDs.
|
||||
*/
|
||||
function _editor_parse_file_uuids($text) {
|
||||
$dom = filter_dom_load($text);
|
||||
$xpath = new \DOMXPath($dom);
|
||||
$uuids = array();
|
||||
foreach ($xpath->query('//*[@data-editor-file-uuid]') as $node) {
|
||||
$uuids[] = $node->getAttribute('data-editor-file-uuid');
|
||||
}
|
||||
return $uuids;
|
||||
}
|
||||
|
|
|
@ -47,12 +47,19 @@ class Editor extends ConfigEntityBase implements EditorInterface {
|
|||
public $editor;
|
||||
|
||||
/**
|
||||
* The array of settings for the text editor.
|
||||
* The array of text editor plugin-specific settings for the text editor.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $settings = array();
|
||||
|
||||
/**
|
||||
* The array of image upload settings for the text editor.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $image_upload = array();
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\Entity::id().
|
||||
*/
|
||||
|
|
|
@ -13,6 +13,8 @@ use Drupal\Core\Ajax\AjaxResponse;
|
|||
use Drupal\Core\Ajax\HtmlCommand;
|
||||
use Drupal\editor\Ajax\EditorDialogSave;
|
||||
use Drupal\Core\Ajax\CloseModalDialogCommand;
|
||||
use Drupal\Core\StreamWrapper\LocalStream;
|
||||
use Drupal\file\FileInterface;
|
||||
|
||||
/**
|
||||
* Provides an image dialog for text editors.
|
||||
|
@ -42,16 +44,50 @@ class EditorImageDialog implements FormInterface {
|
|||
$form['#prefix'] = '<div id="editor-image-dialog-form">';
|
||||
$form['#suffix'] = '</div>';
|
||||
|
||||
// Everything under the "attributes" key is merged directly into the
|
||||
// generated img tag's attributes.
|
||||
$form['attributes']['src'] = array(
|
||||
'#title' => t('URL'),
|
||||
'#type' => 'textfield',
|
||||
'#default_value' => isset($input['src']) ? $input['src'] : '',
|
||||
'#maxlength' => 2048,
|
||||
$editor = editor_load($filter_format->format);
|
||||
|
||||
// Construct strings to use in the upload validators.
|
||||
if (!empty($editor->image_upload['dimensions'])) {
|
||||
$max_dimensions = $editor->image_upload['dimensions']['max_width'] . 'x' . $editor->image_upload['dimensions']['max_height'];
|
||||
}
|
||||
else {
|
||||
$max_dimensions = 0;
|
||||
}
|
||||
$max_filesize = min(parse_size($editor->image_upload['max_size']), file_upload_max_size());
|
||||
|
||||
$existing_file = isset($input['data-editor-file-uuid']) ? entity_load_by_uuid('file', $input['data-editor-file-uuid']) : NULL;
|
||||
$fid = $existing_file ? $existing_file->id() : NULL;
|
||||
|
||||
$form['fid'] = array(
|
||||
'#title' => t('Image'),
|
||||
'#type' => 'managed_file',
|
||||
'#upload_location' => $editor->image_upload['scheme'] . '://' .$editor->image_upload['directory'],
|
||||
'#default_value' => $fid ? array($fid) : NULL,
|
||||
'#upload_validators' => array(
|
||||
'file_validate_extensions' => array('gif png jpg jpeg'),
|
||||
'file_validate_size' => array($max_filesize),
|
||||
'file_validate_image_resolution' => array($max_dimensions),
|
||||
),
|
||||
'#required' => TRUE,
|
||||
);
|
||||
|
||||
$form['attributes']['src'] = array(
|
||||
'#title' => t('URL'),
|
||||
'#type' => 'textfield',
|
||||
'#default_value' => isset($input['src']) ? $input['src'] : '',
|
||||
'#maxlength' => 2048,
|
||||
'#required' => TRUE,
|
||||
);
|
||||
|
||||
// If the editor has image uploads enabled, show a managed_file form item,
|
||||
// otherwise show a (file URL) text form item.
|
||||
if ($editor->image_upload['status'] === '1') {
|
||||
$form['attributes']['src']['#access'] = FALSE;
|
||||
}
|
||||
else {
|
||||
$form['fid']['#access'] = FALSE;
|
||||
}
|
||||
|
||||
$form['attributes']['alt'] = array(
|
||||
'#title' => t('Alternative text'),
|
||||
'#type' => 'textfield',
|
||||
|
@ -120,6 +156,14 @@ class EditorImageDialog implements FormInterface {
|
|||
public function submitForm(array &$form, array &$form_state) {
|
||||
$response = new AjaxResponse();
|
||||
|
||||
// Convert any uploaded files from the FID values to data-editor-file-uuid
|
||||
// attributes.
|
||||
if (!empty($form_state['values']['fid'][0])) {
|
||||
$file = file_load($form_state['values']['fid'][0]);
|
||||
$form_state['values']['attributes']['src'] = file_create_url($file->getFileUri());
|
||||
$form_state['values']['attributes']['data-editor-file-uuid'] = $file->uuid();
|
||||
}
|
||||
|
||||
if (form_get_errors()) {
|
||||
unset($form['#prefix'], $form['#suffix']);
|
||||
$status_messages = array('#theme' => 'status_messages');
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\editor\Tests\EditorFileUsageTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\editor\Tests;
|
||||
|
||||
use Drupal\simpletest\DrupalUnitTestBase;
|
||||
|
||||
/**
|
||||
* Unit tests for editor.module's entity hooks to track file usage.
|
||||
*/
|
||||
class EditorFileUsageTest extends DrupalUnitTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system', 'editor', 'editor_test', 'filter', 'node', 'entity', 'field', 'text', 'field_sql_storage', 'file');
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Text Editor file usage',
|
||||
'description' => 'Tests tracking of file usage by the Text Editor module.',
|
||||
'group' => 'Text Editor',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp();
|
||||
$this->installSchema('system', 'url_alias');
|
||||
$this->installSchema('node', 'node');
|
||||
$this->installSchema('node', 'node_access');
|
||||
$this->installSchema('node', 'node_field_data');
|
||||
$this->installSchema('node', 'node_field_revision');
|
||||
$this->installSchema('file', 'file_managed');
|
||||
$this->installSchema('file', 'file_usage');
|
||||
|
||||
// Add text formats.
|
||||
$filtered_html_format = entity_create('filter_format', array(
|
||||
'format' => 'filtered_html',
|
||||
'name' => 'Filtered HTML',
|
||||
'weight' => 0,
|
||||
'filters' => array(),
|
||||
));
|
||||
$filtered_html_format->save();
|
||||
|
||||
// Set up text editor.
|
||||
$editor = entity_create('editor', array(
|
||||
'format' => 'filtered_html',
|
||||
'editor' => 'unicorn',
|
||||
));
|
||||
$editor->save();
|
||||
|
||||
// Create a node type for testing.
|
||||
$type = entity_create('node_type', array('type' => 'page', 'name' => 'page'));
|
||||
$type->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the configurable text editor manager.
|
||||
*/
|
||||
function testEditorEntityHooks() {
|
||||
$image = entity_create('file', array());
|
||||
$image->setFileUri('core/misc/druplicon.png');
|
||||
$image->setFilename(drupal_basename($image->getFileUri()));
|
||||
$image->save();
|
||||
$this->assertIdentical(array(), file_usage()->listUsage($image), 'The image has zero usages.');
|
||||
|
||||
// Test editor_entity_insert(): increment.
|
||||
$node = entity_create('node', array(
|
||||
'type' => 'page',
|
||||
'title' => 'test',
|
||||
'body' => array(
|
||||
'value' => '<p>Hello, world!</p><img src="awesome-llama.jpg" data-editor-file-uuid="' . $image->uuid() . '" />',
|
||||
'format' => 'filtered_html',
|
||||
)
|
||||
));
|
||||
$node->save();
|
||||
$this->assertIdentical(array('editor' => array('node' => array(1 => '1'))), file_usage()->listUsage($image), 'The image has 1 usage.');
|
||||
|
||||
// Test editor_entity_update(): increment, twice, by creating new revisions.
|
||||
$node->setNewRevision(TRUE);
|
||||
$node->save();
|
||||
$second_revision_id = $node->getRevisionId();
|
||||
$node->setNewRevision(TRUE);
|
||||
$node->save();
|
||||
$this->assertIdentical(array('editor' => array('node' => array(1 => '3'))), file_usage()->listUsage($image), 'The image has 3 usages.');
|
||||
|
||||
// Test hook_entity_update(): decrement, by modifying the last revision:
|
||||
// remove the data- attribute from the body field.
|
||||
$body = $node->get('body')->offsetGet(0)->get('value');
|
||||
$original_value = $body->getValue();
|
||||
$new_value = str_replace('data-editor-file-uuid', 'data-editor-file-uuid-modified', $original_value);
|
||||
$body->setValue($new_value);
|
||||
$node->save();
|
||||
$this->assertIdentical(array('editor' => array('node' => array(1 => '2'))), file_usage()->listUsage($image), 'The image has 2 usages.');
|
||||
|
||||
// Test hook_entity_update(): increment, by modifying the last revision:
|
||||
// readd the data- attribute to the body field.
|
||||
$node->get('body')->offsetGet(0)->get('value')->setValue($original_value);
|
||||
$node->save();
|
||||
$this->assertIdentical(array('editor' => array('node' => array(1 => '3'))), file_usage()->listUsage($image), 'The image has 3 usages.');
|
||||
|
||||
// Test editor_entity_revision_delete(): decrement, by deleting a revision.
|
||||
entity_revision_delete('node', $second_revision_id);
|
||||
$this->assertIdentical(array('editor' => array('node' => array(1 => '2'))), file_usage()->listUsage($image), 'The image has 2 usages.');
|
||||
|
||||
// Test editor_entity_delete().
|
||||
$node->delete();
|
||||
$this->assertIdentical(array(), file_usage()->listUsage($image), 'The image has zero usages again.');
|
||||
}
|
||||
|
||||
}
|
|
@ -88,4 +88,5 @@ class TextProcessed extends TypedData {
|
|||
// throw new ReadOnlyException('Unable to set a computed property.');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ class Text extends TokenizeAreaPluginBase {
|
|||
'#default_value' => $this->options['content'],
|
||||
'#rows' => 6,
|
||||
'#format' => isset($this->options['format']) ? $this->options['format'] : filter_default_format(),
|
||||
'#wysiwyg' => FALSE,
|
||||
'#editor' => FALSE,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ class InputRequired extends ExposedFormPluginBase {
|
|||
'#description' => t('Text to display instead of results until the user selects and applies an exposed filter.'),
|
||||
'#default_value' => $this->options['text_input_required'],
|
||||
'#format' => isset($this->options['text_input_required_format']) ? $this->options['text_input_required_format'] : filter_default_format(),
|
||||
'#wysiwyg' => FALSE,
|
||||
'#editor' => FALSE,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,5 +20,13 @@ settings:
|
|||
plugins:
|
||||
stylescombo:
|
||||
styles: ''
|
||||
image_upload:
|
||||
status: '1'
|
||||
scheme: public
|
||||
directory: inline-images
|
||||
max_size: ''
|
||||
max_dimensions:
|
||||
width: ''
|
||||
height: ''
|
||||
status: '1'
|
||||
langcode: und
|
||||
|
|
|
@ -30,5 +30,13 @@ settings:
|
|||
plugins:
|
||||
stylescombo:
|
||||
styles: ''
|
||||
image_upload:
|
||||
status: '1'
|
||||
scheme: public
|
||||
directory: inline-images
|
||||
max_size: ''
|
||||
max_dimensions:
|
||||
width: ''
|
||||
height: ''
|
||||
status: '1'
|
||||
langcode: und
|
||||
|
|
Loading…
Reference in New Issue