WordPress: TinyMCE Plugin mit Dialogbox

Achtung, der Artikel gilt nur für WordPress-Versionen vor 3.9!

In diesem Beitrag zeige ich eine Möglichkeit, wie man den Wordpress-standardeditor TinyMCE um einen Button erweitert der eine beliebig komplexes Dialogfenster öffnet mit dem man mit den gewohnten APIs auf die Wordpressumgebung zugreifen kann. Sämtliche Codebeispiele sind als Gist direkt zu sehen.

Das Tutorial beschreibt ein erdachtes Wordpress-Plugin mit folgender Verzeichnisstruktur:

my-plugin/
	i18n/
		mce_locale.php
	js/
		editor_plugin.js
	my_plugin.php

TinyMCE

Ein TinyMCE-Plugin ist wie Folgt aufgebaut:

Sämtliche Plugin-Dateien sind in einem eigenen Verzeichnis zusammengefasst. In der editor_plugin.js wird das Plugin initialisiert:

// js/editor_plugin.js
/**
 * an example tinyMCE Plugin
 */
tinymce.create(
	'tinymce.plugins.myPlugin',
	{
		/**
		 * @param tinymce.Editor editor
		 * @param string url
		 */
		init : function( editor, url ) {
			/**
			 * register a new button
			 */
			editor.addButton(
				'my_plugin_button',
				{
					cmd : 'my_plugin_button_cmd',
					title : editor.getLang( 'myPlugin.buttonTitle', 'My Button' ),
					image : url + '/img/example.gif'
				}
			);
			/**
		 	* and a new command
		 	*/
			editor.addCommand(
				'my_plugin_cmd',
				function() {
					/**
					* @link http://www.tinymce.com/wiki.php/API3:method.tinymce.WindowManager.open
					* @param Object Popup settings
					* @param Object Arguments to pass to the Popup
					*/
					editor.windowManager.open(
						{
							file : url + '/dialog.htm',
							width : 320,
							height : 120,
							inline : 1
						},
						{
							plugin_url : url,
							some_custom_arg : 'custom value'
						}
					);
				}
			);
		}
	}
);
// register plugin
tinymce.PluginManager.add( 'myPlugin', tinymce.plugins.myPlugin );

(Das ist der Standard von TinyMCE, die Datei kann natürlich auch anders heißen). Dafür wird der Methode tinymce.create() neben dem Namen des Plugin-Objekts an zweiter Stelle ein Objekt übergeben, welches u.A. die Methode init definiert. Diese akzeptiert wiederum zwei Parameter: das Objekt des Editorfensters und die URL zum Pluginverzeichnis. Mit dem Editor-Objekt und dessen Methoden kann man sich in die Funktionalität von TinyMCE einklinken und z.B. einen Button registrieren, ein Kommando definieren, dass beim Klick auf den Button zur Ausführung kommt und so fort. Über die Methode editor.getLang() kommt man an die übersetzten Textbausteine heran. Dazu später mehr. Um nun ein Dialog mit mehreren Eingabemöglichkeiten zu öffnen, geht TinyMCE den Weg über eine externe HTML-Datei die es in einem Inline-Frame öffnet. Das ist in sofern von Nachteil, da in diesem Kontext nur schwerlich an die Wordpress API heranzukommen ist. Von der umständlichen Handhabung, das Fenster so zu stylen, dass es im Wordpress-Look daher kommt mal ganz abgesehen.

Wordpress bietet eine andere Option an: Das Markup (in den meisten Fällen ein HTML-Formular) für das Dialogfenster wird einfach über den Hook wp_footer in die Seite eingesetzt und per CSS ausgeblendet.

# somewhere in my_plugin.php
if ( is_admin() ) {
	/**
	 * hook in only, if current user can
	 * see the editor and want to have
	 * a rich text editor
	 */
	if (
	    (    current_user_can( 'edit_posts' )
          || current_user_can( 'edit_pages' ) 
	    )
	    && 'true' == get_user_option( 'rich_editing' )
	) {
		add_action( 'wp_footer', 'my_plugin_dialog' );
	}
}

function my_plugin_dialog() { 
	?>
	<div style="display:none;">
		<form id="my_plugin_dialog" tabindex="-1" action="">
			<div>
				<input type="text" name="foo" />
			</div>
			<div>
				<input type="submit" class="button-primary" value="Go" />
			</div>
		</form>
	</div>
	<?php
}

Die Funktionalität des Popups stellt das WP-Interne TinyMCE-Plugin »WP-Dialog« zur Verfügung. Der Methode editor.windowManager.open wird anstelle eines Pfads zum Popup einfach die ID des Formulars übergeben. Das ganze an ein Befehl gebunden, der wiederum an einen neuen Button gebunden wird, öffnet das Fenster beim Klick auf selbigen.

// js/editor_plugin.js
/**
 * somewhere in the init-method
 * of the parameter object at
 * tinymce.create()...
 *
 * this requires the mce plugin WPDialog
 */
editor.addCommand(
	'my_plugin_button_cmd',
	function() {
		/**
		 * @param Object Popup settings
		 * @param Object Arguments to pass to the Popup
		 */
		editor.windowManager.open(
			{
				 // this is the ID of the popups parent element
				 id : 'my_plugin_dialog',
				 width : 480,
				 title : editor.getLang( 'myPlugin.popupTitle', 'Default Title' )
				 height : 'auto',
				 wpDialog : true,
			},
			{
				plugin_url : url
			}
    	);
	}
);

Wordpress

Wordpress kümmert sich dankenswerter Weise um die Integration des Plugins in TinyMCE. Dazu sind die Filter mce_external_plugins für das Plugin-Script und mce_buttons für den Button bereit gestellt. Beide Filter werden auf je ein Array angewendet, dass erweitert zurück gegeben wird. Um etwas Abstand zwischen die Buttons zu bekommen, setzt man als ein Array-Element einen senkrechten Stricht '|'.

# somewhere in my_plugin.php
add_filter( 'mce_external_plugins', 'register_my_tinymce_plugin' );
add_filter( 'mce_buttons', 'register_my_tinymce_button' );

/**
 * register_my_tinymce_plugin
 *
 * @param array $mce_plugins
 * @return array $mce_plugins
 */
function register_my_tinymce_plugin( $mce_plugins ) {
	$mce_plugins[ 'myPlugin' ] = plugins_url() . '/my-plugin/js/editor_plugin.js';

	return $mce_plugins;
}

/**
 * register_my_tinymce_button
 *
 * @param array $buttons
 * @return array $buttons
 */
function register_my_tinymce_button( $buttons ) {
	array_push( $buttons, '|', 'my_plugin_button' );

	return $buttons;
}

Dynamik in den Dialog bekommt man wie gewohnt über ein JavaScript, dass mit der Funktion wp_enqueue_script() geladen wird. Innerhalb dieses Scripts kommt man über das globale JavaScript-Objekt tinyMCEPopup an den Editor bzw. mit edCanvas an die Textarea im HTML-Modus heran. Zu beachten ist, dass tinyMCEPopup zum Zeitpunkt des jQuery-eigenen Events ready nocht nicht verfügbar ist.

i18n

Um innerhalb des TinyMCE Plugins lokalisierbare Strings zu nutzen, bedarf es einer weiteren PHP-Datei, die über den Filter mce_external_languages geladen wird. Genauer gesagt loopt Wordpress über mehrere solche Dateien und erweitert dabei einen String, der die entsprechenden Textausteine enthält, die in bekannter Weise übersetzt werden können. Der String selbst ist JavaScript-Code, der die Methode tinymce.addI18n() aufruft. Über den hier definierten Namensraum sind die Textbausteine dann verfügbar.

<?php
# i18n/mce_locale.php
/**
 * @var string $strings a JavaScript snippet to add another language pack to TinyMCE
 * @var string $mce_locale an ISO 639-1 formated string of the current language e.g. en, de...
 * @deprecated wp_tiny_mce() at wp-admin/includes/post.php (for versions prior WP 3.3)
 * @see _WP_Editors::editor_settings in wp-includes/class-wp-editor.php
 */
$strings =
	'tinyMCE.addI18n(
		"' . $mce_locale . '.myPlugin",
		{
			buttonTitle : "' . esc_js( __( 'My Button Title:', 'my_textdomain' ) ) . '",
			popupTitle  : "' . esc_js( __( 'My Popup Title', 'my_textdomain' ) ) . '",
		}
	);';

Das Laden übernimmt wieder Wordpress:

<?php
# somewhere in my_plugin.php
# remember to load the plugin textdomain!
add_filter( 'mce_external_languages', 'my_mce_localisation' );

/**
 * my_mce_localisation
 *
 * @see wp-admin/includes/post.php line 1474
 * @param array $mce_external_languages
 * @return array $mce_external_languages
 */
function my_mce_localisation( $mce_external_languages ) {
	$mce_external_languages[ 'myPlugin' ] = plugin_dir_path( __FILE__ ) . 'i18n/mce_locale.php';

	return $mce_external_languages;
}

Kommentare

  1. 01) 25.08.2014
    Andy

    Salü,
    Leider ruft bei mir die ID im Command nicht den gewünschten Code auf. Respektive es wird gar kein Popup angezeigt, der Code aber ausgeführt.

    Woran könnte das wohl liegen?

    Danke und Gruss

  2. 02) 25.08.2014
    David

    Hallo Andy,

    die erste Frage muss lauten, bei welcher WordPress-Version das Problem auftritt? Für alle Versionen ab 3.9 kann dieser Artikel nicht mehr als Beispiel dienen. (Ich werde das mal ergänzen) Mit WP 3.9 hat TinyMCE einen Versionssprung gemacht, wobei sich die API geändert ebenso wie das Core-Plugin für das MCE-Popup. Zuerst solltest Du das Script wpdialogs als Voraussetzung für dein Script registrieren.

    Ich habe vor kurzem das Plugin Colored Coding dahingehend aktualisiert. Das Script verwendet $.wpdialogs() (welches jetzt ein jQuery Plugin ist) um das Dialogfenster zu öffnen. Das TinyMCE-Plugin ruft beim Klick auf den Button nur noch den Wrapper auf und gibt die Instanz des aufrufenden Editors als Parameter weiter.

    Vielleicht hilft dir auch diese Artikelserie weiter.

Fragen, Ideen oder Kritik? – Hier ist Platz dafür!

Dein Kommentar

Um ein Kommentar abzugeben, reicht der Text im Kommentarfeld. Die Angabe eines Namens wäre nett, ist aber nicht erforderlich.

Du darfst folgenden HTML-Code verwenden, musst aber nicht:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>