6

With WP 4.8 extending the widgets API, the simple text widget is using TinyMCE. The advantage is that TinyMCE scripts are already enqueued in Ajax requests.

However, for a client, I need a widget which has multiple editors inside it. This widget is not only used on the Widgets' Page but also within the Plugin Page builder by SiteOrigin.

With the new WordPress the following works in the widget form() function:

public function form( $instance ) {
        // ...

        foreach(cisI8Suffixes(true) as $suf) {
            ?><div class="cis_editor_bwrap" <?php echo (CIS_I8_ENABLE) ? 'data-cislan="'.cisLanFromSuf($suf).'"': ""; ?>><?php
            wp_editor(cisI8Extract($instance, 'text', $suf), 'text'.$suf );
            ?></div><?php
        }

        $this->containerInputHTML($instance);
        $this->linkWrapperInputHTML($instance);
}

However, it only works as long as I have only one widget as the IDs will be duplicated as soon as a second widget is created. I need either a random number or the widget ID (note: the str_replace is needed as wp_editor does not accept the minus sign in ids).

The following unfortunatly fails:

public function form( $instance ) {
        // ...
        foreach(cisI8Suffixes(true) as $suf) {
            ?><div class="cis_editor_bwrap" <?php echo (CIS_I8_ENABLE) ? 'data-cislan="'.cisLanFromSuf($suf).'"': ""; ?>><?php
            wp_editor(cisI8Extract($instance, 'text', $suf), str_replace("-","_",$this->get_field_id('text'.$suf)) );
            ?></div><?php
        }
}

Somehow WordPress does not correctly instantiate TinyMCE if a random number or the widget ID are within the editor ID. Error message is like this:

wp-tinymce.php?c=1&ver=4603-20170530:15 Uncaught TypeError: Cannot read property 'onpageload' of undefined
    at h (wp-tinymce.php?c=1&ver=4603-20170530:15)
    at m (wp-tinymce.php?c=1&ver=4603-20170530:15)
    at h.l.bind (wp-tinymce.php?c=1&ver=4603-20170530:3)
    at o.bind (wp-tinymce.php?c=1&ver=4603-20170530:5)
    at Object.init (wp-tinymce.php?c=1&ver=4603-20170530:15)
    at e (editor.min.js?ver=4.8:1)
    at HTMLDocument.<anonymous> (editor.min.js?ver=4.8:1)
    at a (wp-tinymce.php?c=1&ver=4603-20170530:3)
    at HTMLDocument.p (wp-tinymce.php?c=1&ver=4603-20170530:3)

This is the HTML outputted by wp_editor if using possibility two:

<div class="wp-core-ui wp-editor-wrap html-active" id="wp-widget_ciseditor_c27_text-wrap">
    <link href="http://localhost/hiedler/wp-includes/css/dashicons.min.css?ver=4.8" id="dashicons-css" media="all" rel="stylesheet" type="text/css">
    <link href="http://localhost/hiedler/wp-includes/css/editor.min.css?ver=4.8" id="editor-buttons-css" media="all" rel="stylesheet" type="text/css">
    <div class="wp-editor-tools hide-if-no-js" id="wp-widget_ciseditor_c27_text-editor-tools">
        <div class="wp-media-buttons" id="wp-widget_ciseditor_c27_text-media-buttons">
            <button class="button insert-media add_media" data-editor="widget_ciseditor_c27_text" id="insert-media-button" type="button"><span class="wp-media-buttons-icon"></span> Dateien hinzufügen</button>
        </div>
        <div class="wp-editor-tabs">
            <button class="wp-switch-editor switch-tmce" data-wp-editor-id="widget_ciseditor_c27_text" id="widget_ciseditor_c27_text-tmce" type="button">Visuell</button> <button class="wp-switch-editor switch-html" data-wp-editor-id="widget_ciseditor_c27_text" id="widget_ciseditor_c27_text-html" type="button">Text</button>
        </div>
    </div>
    <div class="wp-editor-container" id="wp-widget_ciseditor_c27_text-editor-container">
        <div class="quicktags-toolbar" id="qt_widget_ciseditor_c27_text_toolbar"></div>
        <textarea class="wp-editor-area" cols="40" id="widget_ciseditor_c27_text" name="widget_ciseditor_c27_text" rows="20"></textarea>
    </div>
</div>

So my question is how can I solve this?

I have the two ideas:

  1. Register the dynamic editor ID after widget creation/load and make it work. HOW?

  2. Do not use wp_editor() at all but use textareas and apply TinyMCE to them dynamically. Problems: File Upload will be missing. How to apply TinyMCE scripts on every new widget/widget load?

Tried a lot of things so far but no luck.

1

It's hard to say without seeing all the code.

Yes if you don't refer to the instance - then it won't really be a unique ID. Something like this would work - you would just reference the class property $id:

    for ( $i = 0; $i <= count( $suffixes ); $i++) {
        wp_editor( 'your content', "{$this->id}_$i" );
    }

The js in core for the text widget create a random ID for the editor instances before it handles the instantiation, so you could translate that over to PHP if you wanted to follow that:

    $el = 'el' . preg_replace( '/\D/', '', ( rand()/getrandmax() ) ) . '_';

Since your developing locally - you should turn on SCRIPT_DEBUG in your wp-config.php: define( 'SCRIPT_DEBUG', true ); to help you troubleshoot your issues with javascript. Doing so would make that error from minified files be a little more meaningful. The error is being caused by switchEditor method in editor.min.js. There's special handling of the tinymce text widget in the customizer - and it's not just as simple as calling wp_editor - though it could probably be done. You could try enabling the wp_skip_init process and adding your own initialization code perhaps:

    add_filter( 'wp_editor_settings', 'ciseditor_wp_editor_settings', 5, 2 );
    function ciseditor_wp_editor_settings( $settings, $id ) {
        // look for your prefixed id and return the id associated with it.
        if ( $ed = strstr( $id, 'foo_widget' ) ) {
            $settings['tinymce'] = array(
                'wp_skip_init' => true,
                'wp_autoresize_on' => false, // turn off the auto resize height of editor.
            );
            $settings['editor_height'] = 300; // set the height
        }
        return $settings;
    }

I'm pretty sure that won't be the way to go though. I would recommend looking at wp-admin/js/text-widgets.js to see the handling of how core is implementing the text widget and the switch editor functionality - along with properly handling the data for the customizer. You should also reference wp-includes/widgets/class-wp-widget-text.php since you're essentially wanting to make the same thing that already exists.

Generally having that error in tinymce means that the instance isn't being removed and added back properly - so it makes sense that WordPress' switchEditor in this context is throwing errors for you. There's a lot of documentation in the text-widgets.js about how tinymce and dynamic DOM components will interact that have to be accounted for or you'll end up with issues.

One quick solution might just be to disable quicktags for your editor instances, which could be done in the wp_editor_settings filter or you can also pass it in the settings array when you're making the editors, ie:

wp_editor( 'your content', "{$this->id}_$i", array( 'quicktags' => false ) );

I did dynamically create tinymce instances on a project once in wordpress, and I just remember looking at the editor code in core to see what arguments and scripts they included to get everything working, so that's always an option too. If you go that route - most of the things translate over 1:1 in terms of the php abstraction to js for the params. I think for the add media button I ended up creating my own thing, but there is the action 'media_buttons' so you should be able to just add that where it needs to be displayed.

Also consider why it is that you need multiple editors in one widget. Another way to handle this would be to remove the instances in an accordion style within the widget itself, and rebuild them as accordions are expanded so only one widget is displayed at a time. I think ultimately though - having one editor in a widget suits most use cases, and gives the end user flexibility to move content where they find it most fitting for their own use.

  • Thx I will check this :-) – Blackbam Jul 12 '17 at 20:05
0

If I recall correctly the ID supplied to wp_editor needs to already exist.

Perhaps it does for you elsewhere in your code, but it doesn't look like it from what I can see? I think it would need to be specified like this:

public function form( $instance ) {
    // ...
    foreach(cisI8Suffixes(true) as $suf) {
        $id = str_replace("-","_",$this->get_field_id('text'.$suf));
        ?><div class="cis_editor_bwrap" id="<?php echo $id; ?>" <?php echo (CIS_I8_ENABLE) ? 'data-cislan="'.cisLanFromSuf($suf).'"': ""; ?>><?php
        wp_editor(cisI8Extract($instance, 'text', $suf), $id);
        ?></div><?php
    }
}
  • Will check this. However I guess the editor must be somehow pre-registered or must be initialized otherwise if coming with ajax. – Blackbam Jun 23 '17 at 11:55
  • I have tested this for a similar purpose (multiple edit boxes on a page but not in widgets) and had it working. So my guess is based on what I can see comparing with that code... maybe adding the rest of your form code would help as the problem may be there also. – majick Jun 25 '17 at 0:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.