EditorImageDialog.php 8.79 KB
Newer Older
1 2 3 4 5 6 7 8 9
<?php

/**
 * @file
 * Contains \Drupal\editor\Form\EditorImageDialog.
 */

namespace Drupal\editor\Form;

10
use Drupal\Component\Utility\Bytes;
11
use Drupal\Core\Form\FormBase;
12
use Drupal\Core\Form\FormStateInterface;
13
use Drupal\filter\Entity\FilterFormat;
14 15 16 17 18 19 20 21
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\HtmlCommand;
use Drupal\editor\Ajax\EditorDialogSave;
use Drupal\Core\Ajax\CloseModalDialogCommand;

/**
 * Provides an image dialog for text editors.
 */
22
class EditorImageDialog extends FormBase {
23 24 25 26

  /**
   * {@inheritdoc}
   */
27
  public function getFormId() {
28 29 30 31 32 33
    return 'editor_image_dialog';
  }

  /**
   * {@inheritdoc}
   *
34
   * @param \Drupal\filter\Entity\FilterFormat $filter_format
35 36
   *   The filter format for which this dialog corresponds.
   */
37
  public function buildForm(array $form, FormStateInterface $form_state, FilterFormat $filter_format = NULL) {
38 39
    // The default values are set directly from \Drupal::request()->request,
    // provided by the editor plugin opening the dialog.
40
    if (!isset($form_state['image_element'])) {
41 42
      $user_input = $form_state->getUserInput();
      $form_state['image_element'] = isset($user_input['editor_object']) ? $user_input['editor_object'] : array();
43 44
    }
    $image_element = $form_state['image_element'];
45 46

    $form['#tree'] = TRUE;
47
    $form['#attached']['library'][] = 'editor/drupal.editor.dialog';
48 49 50
    $form['#prefix'] = '<div id="editor-image-dialog-form">';
    $form['#suffix'] = '</div>';

51 52 53
    $editor = editor_load($filter_format->format);

    // Construct strings to use in the upload validators.
54 55 56
    $image_upload = $editor->getImageUploadSettings();
    if (!empty($image_upload['dimensions'])) {
      $max_dimensions = $image_upload['dimensions']['max_width'] . 'x' . $image_upload['dimensions']['max_height'];
57 58 59 60
    }
    else {
      $max_dimensions = 0;
    }
61
    $max_filesize = min(Bytes::toInt($image_upload['max_size']), file_upload_max_size());
62

63
    $existing_file = isset($image_element['data-editor-file-uuid']) ? entity_load_by_uuid('file', $image_element['data-editor-file-uuid']) : NULL;
64 65 66
    $fid = $existing_file ? $existing_file->id() : NULL;

    $form['fid'] = array(
67
      '#title' => $this->t('Image'),
68
      '#type' => 'managed_file',
69
      '#upload_location' => $image_upload['scheme'] . '://' . $image_upload['directory'],
70 71 72 73 74 75
      '#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),
      ),
76 77 78
      '#required' => TRUE,
    );

79
    $form['attributes']['src'] = array(
80 81 82 83 84
      '#title' => $this->t('URL'),
      '#type' => 'textfield',
      '#default_value' => isset($image_element['src']) ? $image_element['src'] : '',
      '#maxlength' => 2048,
      '#required' => TRUE,
85 86 87 88
    );

    // If the editor has image uploads enabled, show a managed_file form item,
    // otherwise show a (file URL) text form item.
89
    if ($image_upload['status']) {
90
      $form['attributes']['src']['#access'] = FALSE;
91
      $form['attributes']['src']['#required'] = FALSE;
92 93 94
    }
    else {
      $form['fid']['#access'] = FALSE;
95
      $form['fid']['#required'] = FALSE;
96 97
    }

98 99 100 101 102 103 104 105 106 107 108
    // The alt attribute is *required*, but we allow users to opt-in to empty
    // alt attributes for the very rare edge cases where that is valid by
    // specifying two double quotes as the alternative text in the dialog.
    // However, that *is* stored as an empty alt attribute, so if we're editing
    // an existing image (which means the src attribute is set) and its alt
    // attribute is empty, then we show that as two double quotes in the dialog.
    // @see https://www.drupal.org/node/2307647
    $alt = isset($image_element['alt']) ? $image_element['alt'] : '';
    if ($alt === '' && !empty($image_element['src'])) {
      $alt = '""';
    }
109
    $form['attributes']['alt'] = array(
110
      '#title' => $this->t('Alternative text'),
111
      '#placeholder' => $this->t('Short description for the visually impaired'),
112
      '#type' => 'textfield',
113
      '#required' => TRUE,
114 115
      '#required_error' => $this->t('Alternative text is required.<br><em>(Only in rare cases should this be left empty. To create empty alternative text, enter <code>""</code> — two double quotes without any content).'),
      '#default_value' => $alt,
116 117 118
      '#maxlength' => 2048,
    );
    $form['dimensions'] = array(
119
      '#type' => 'fieldset',
120
      '#title' => $this->t('Image size'),
121 122 123 124 125
      '#attributes' => array('class' => array(
        'container-inline',
        'fieldgroup',
        'form-composite',
      )),
126 127
    );
    $form['dimensions']['width'] = array(
128
      '#title' => $this->t('Width'),
129 130
      '#title_display' => 'invisible',
      '#type' => 'number',
131
      '#default_value' => isset($image_element['width']) ? $image_element['width'] : '',
132 133 134 135
      '#size' => 8,
      '#maxlength' => 8,
      '#min' => 1,
      '#max' => 99999,
136
      '#placeholder' => $this->t('width'),
137 138 139 140
      '#field_suffix' => ' x ',
      '#parents' => array('attributes', 'width'),
    );
    $form['dimensions']['height'] = array(
141
      '#title' => $this->t('Height'),
142 143
      '#title_display' => 'invisible',
      '#type' => 'number',
144
      '#default_value' => isset($image_element['height']) ? $image_element['height'] : '',
145 146 147 148
      '#size' => 8,
      '#maxlength' => 8,
      '#min' => 1,
      '#max' => 99999,
149 150
      '#placeholder' => $this->t('height'),
      '#field_suffix' => $this->t('pixels'),
151 152 153
      '#parents' => array('attributes', 'height'),
    );

154
    // When Drupal core's filter_align is being used, the text editor may
155
    // offer the ability to change the alignment.
156
    if (isset($image_element['data-align']) && $filter_format->filters('filter_align')->status) {
157 158 159 160 161 162 163 164 165
      $form['align'] = array(
        '#title' => $this->t('Align'),
        '#type' => 'radios',
        '#options' => array(
          'none' => $this->t('None'),
          'left' => $this->t('Left'),
          'center' => $this->t('Center'),
          'right' => $this->t('Right'),
        ),
166
        '#default_value' => $image_element['data-align'] === '' ? 'none' : $image_element['data-align'],
167 168
        '#wrapper_attributes' => array('class' => array('container-inline')),
        '#attributes' => array('class' => array('container-inline')),
169
        '#parents' => array('attributes', 'data-align'),
170 171 172 173 174
      );
    }

    // When Drupal core's filter_caption is being used, the text editor may
    // offer the ability to in-place edit the image's caption: show a toggle.
175
    if (isset($image_element['hasCaption']) && $filter_format->filters('filter_caption')->status) {
176 177 178 179
      $form['caption'] = array(
        '#title' => $this->t('Caption'),
        '#type' => 'checkbox',
        '#default_value' => $image_element['hasCaption'] === 'true',
180
        '#parents' => array('attributes', 'hasCaption'),
181 182 183
      );
    }

184 185 186 187 188
    $form['actions'] = array(
      '#type' => 'actions',
    );
    $form['actions']['save_modal'] = array(
      '#type' => 'submit',
189
      '#value' => $this->t('Save'),
190 191 192
      // No regular submit-handler. This form only works via JavaScript.
      '#submit' => array(),
      '#ajax' => array(
193
        'callback' => '::submitForm',
194 195 196 197 198 199 200 201 202 203
        'event' => 'click',
      ),
    );

    return $form;
  }

  /**
   * {@inheritdoc}
   */
204
  public function submitForm(array &$form, FormStateInterface $form_state) {
205 206
    $response = new AjaxResponse();

207 208
    // Convert any uploaded files from the FID values to data-editor-file-uuid
    // attributes.
209 210 211
    $fid = $form_state->getValue(array('fid', 0));
    if (!empty($fid)) {
      $file = file_load($fid);
212 213 214 215
      $file_url = file_create_url($file->getFileUri());
      // Transform absolute image URLs to relative image URLs: prevent problems
      // on multisite set-ups and prevent mixed content errors.
      $file_url = file_url_transform_relative($file_url);
216 217
      $form_state->setValue(array('attributes', 'src'), $file_url);
      $form_state->setValue(array('attributes', 'data-editor-file-uuid'), $file->uuid());
218 219
    }

220 221 222 223 224 225
    // When the alt attribute is set to two double quotes, transform it to the
    // empty string: two double quotes signify "empty alt attribute". See above.
    if (trim($form_state->getValue(array('attributes', 'alt'))) === '""') {
      $form_state->setValue(array('attributes', 'alt'), '');
    }

226
    if (form_get_errors($form_state)) {
227
      unset($form['#prefix'], $form['#suffix']);
228
      $status_messages = array('#theme' => 'status_messages');
229
      $output = drupal_render($form);
230
      $output = '<div>' . drupal_render($status_messages) . $output . '</div>';
231 232 233
      $response->addCommand(new HtmlCommand('#editor-image-dialog-form', $output));
    }
    else {
234
      $response->addCommand(new EditorDialogSave($form_state->getValues()));
235 236 237 238 239 240 241
      $response->addCommand(new CloseModalDialogCommand());
    }

    return $response;
  }

}