backdrop / backdrop-issues

Issue tracker for Backdrop core.
145 stars 40 forks source link

How to alter/edit images inserted into CKEditor? #3144

Open ghost opened 6 years ago

ghost commented 6 years ago

When you click the Image button in CKEditor, you see a form that includes fields like Alternative Text, Image Size, Align, etc. When you submit that form, it inserts HTML code into the editor that outputs an image (<img ...>).

I'd like to:

How do I do this?

Altering core code is generally easy enough with forms, etc. due to the availability of hook_alter() functions, but how do I do the same when it comes to CKEditor plugins and JS?

Graham-72 commented 6 years ago

@BWPanda I may be able to help with this because it relates closely to the work I have recently done for issue #2072 and am now doing for #3134, involving changes to filter.pages.inc and the associated JS plugins. But do you envisage your requirement being met by a change in core or are you thinking of a contrib module?

Are you thinking of any particular additional field types or do you want the user to be able write these in, presumably as a text field for name of field, and another for value?

ghost commented 6 years ago

Hey @Graham-72, I've developed a contrib module that I'd like to add this functionality to, so I need to do all this from contrib, not core.

I think this question is such that anyone can discover how to add any additional fields and then use that data to manipulate the HTML added to the editor, but for my specific use case I'd like to add a select list to the image form whose value determines what value a new attribute gets.

To be more specific, I need to add the srcset attribute to the image tag, and the value of that will be computed by my code based on the user's selection of my select field when uploading an image.

So, I add a select field to the image upload form which lets the user choose from a few options when inserting an image, they choose option 'A'. They press the Insert button and their image is displayed in the editor. What my module/code has done is taken their selection ('A'), computed various image URLs, then added those image URLs to a new srcset attribute on the inserted image tag (e.g. <img srcset="[URL1] 300w, [URL2] 600w, [etc.]" src="[ORIGINAL-URL]" alt="[ALT-TEXT]" />).

Does that make sense? That's my specific use-case behind this question, but it's still a generic question as to how developers can add extra fields to the Image insert form, then use those field's values to change the output HTML code. Thanks!

Graham-72 commented 6 years ago

I think you can provide an additional plugin for CKEditor within your contrib module, and then code this to extend the CKEditor plugins provided in core, namely backdroplink and backdropimage, which themselves extend/modify CKEditor's plugins.

I have no experience of adding a new plugin, only of modifying the backdroplink plugin. I guess the backdropimagecaption plugin that we have in core would provide an example of how it can be done.

I'm assuming, as you say, that the extra field is added to the image dialogue form by means of hook_form_FORM_ID_alter(),

ghost commented 6 years ago

I guess the backdropimagecaption plugin that we have in core would provide an example of how it can be done.

Right, good point! I'll check that out and see if that helps answer my questions.

quicksketch commented 6 years ago

@BWPanda The image (and link) dialog forms are intended to provide direct attribute mapping per field. So if you were to make a new select list at $form['image']['srcset'], the value of the select list would automatically be placed into the image tag's 'srcset property when the form is submitted.

For example this would do the job if you just wanted a textfield:

function mymodule_form_filter_format_editor_image_form_alter(&$form, &$form_state) {
  $values = $form_state['input']['editor_object'];
  $form['srcset'] = array(
    '#title' => t('Srcset'),
    '#type' => 'textfield',
    '#default_value' => isset($values['srcset']) ? $values['srcset'] : NULL,
    '#parents' => array('attributes', 'alt'),
  );
}

You could also do some manipulations on the given values if it doesn't map directly to an attribute name. Something like this:

function mymodule_form_filter_format_editor_image_form_alter(&$form, &$form_state) {
  $values = $form_state['input']['editor_object'];
  $form['some_field'] = array(
    '#title' => t('An indirect field'),
    '#type' => 'textfield',
    '#default_value' => $some_field_value,
  );

  $form['#submit'][] = 'mymodule_form_filter_format_editor_image_form_submit';
}

function mymodule_form_filter_format_editor_image_form_submit($form, &$form_state) {
  $submitted_value = $form_state['values']['some_field'];
  $final_value = 'foo'; // Do some manipulation here to determine the attribute.
  $form_state['values']['attributes']['srcset'] = $final_value;
  // Clean up the form element value to keep it from affecting the CKEditor plugin.
  unset($form_state['values']['some_field']);
}

In either case, whatever is in $form_state['values'] after all submit handlers have run is what gets returned as JSON and passed to the CKEditor plugin. Anything in $form_state['values']['attributes'] will be added to the <img> tag as an attribute. Any attribute value should be a string.

ghost commented 6 years ago

@quicksketch I can't get this working. Here's my code:

function MYMODULE_form_filter_format_editor_image_form_alter(&$form, &$form_state) {
  $values = array();
  if (isset($form_state['input']['editor_object'])) {
    $values = $form_state['input']['editor_object'];
  }

  $form['srcset'] = array(
    '#title' => t('Srcset'),
    '#type' => 'textfield',
    '#default_value' => isset($values['srcset']) ? $values['srcset'] : NULL,
    '#parents' => array('attributes', 'srcset'),
  );
}

When I Insert an image and the HTML is added to the editor, there's no srcset attribute or value...

Graham-72 commented 6 years ago

@BWPanda I have been investigating this and I can see that your new attribute is being provided to the ckeditor plugin script backdropimage, but there it seems not to get the same treatment as it would in the plugin backdroplink, and so never gets to be added as an attribute of the <img> tag. I will see if this is the case, and if so come up with a PR for the backdropimage plugin.

ghost commented 6 years ago

@Graham-72 Awesome, thanks!

Graham-72 commented 6 years ago

@BWPanda I'm afraid this is taking me longer than hoped and I still have no answer. I have been trying to make a modified version of the backdropimage plugin that will accept and include your srcset attribute. Accepting the input into the plugin is easy. It is the inclusion into CKeditor's result that is not yet happening. I will keep trying to find out how this might be achieved but progress may be slow.

docwilmot commented 6 years ago

I've tried this in the past. What @quicksketch says above in https://github.com/backdrop/backdrop-issues/issues/3144#issuecomment-392961418 works for the link plugin, but not for the image plugin.

ghost commented 6 years ago

... works for the link plugin, but not for the image plugin.

Is this a bug then?

Graham-72 commented 6 years ago

I think we need a new plugin. For D8 there is what seems to be a useful blog on how to create plugins fo CKeditor at http://activelamp.com/blog/drupal/drupal8-ckeditor-plugin/. It would be good to have an equivalent for Backdrop.