Skip to content

Instantly share code, notes, and snippets.

@thefuxia
Created January 20, 2014 05:05
Show Gist options
  • Save thefuxia/8515113 to your computer and use it in GitHub Desktop.
Save thefuxia/8515113 to your computer and use it in GitHub Desktop.
Fetch TinyMCE per AJAX. Media upload doesn’t work yet.
<?php
/**
* Plugin Name: T5 AJAX Editor
*/
namespace T5AjaxEditor;
class Controller implements Event_Handler
{
private $editor_id = 't5_ajax_editor';
private $ajax_action = 't5_test_editor';
private $admin_action = 't5_test_editor';
private $page_output;
private $validator;
public function __construct()
{
$data = new Options_Data( $this->editor_id );
$this->validator = new Basic_Request_Validator( 't5_ajax_test' );
if ( $this->is_ajax() )
{
$editor = new Ajax_Editor( $data, $this->validator, $this->editor_id );
add_action( "wp_ajax_$this->ajax_action", [ $editor, 'render' ] );
return;
}
$this->page_output = new Options_Page( $this, $this->admin_action );
$admin_post = new Admin_Post_Handler( $data, $this->validator, $this->editor_id );
add_action( 'admin_menu', [ $this, 'register_page' ] );
add_action( 'admin_post_' . $this->admin_action, [ $admin_post, 'update' ] );
}
public function register_page()
{
$title = 'AJAX Editor';
add_menu_page(
$title,
$title,
'manage_options',
'ajax-test',
[ $this->page_output, 'render' ]
);
}
public function trigger_event( $name, $data = NULL )
{
if ( 'admin.options_page.form.content' === $name )
{
$content = new Options_Page_Form_Ajax_Content( $this->validator, $this->ajax_action );
$content->render( 'get_editor', 't5_editor_container' );
}
}
private function is_ajax()
{
return defined( 'DOING_AJAX' ) && DOING_AJAX;
}
}
class Ajax_Editor
{
private $data;
private $validator;
private $editor_id;
private $settings = [ 'media_buttons' => FALSE ];
public function __construct( Data $data, Request_Validator $validator, $editor_id )
{
$this->data = $data;
$this->validator = $validator;
$this->editor_id = $editor_id;
}
public function render()
{
if ( ! $this->validator->is_valid( TRUE ) )
die( 'nope' );
wp_editor( $this->data->get(), $this->editor_id, $this->settings );
\_WP_Editors::enqueue_scripts();
print_footer_scripts();
\_WP_Editors::editor_js();
die();
}
public function change_settings( Array $settings )
{
$this->settings = $settings;
}
}
class Options_Page
{
private $event_handler;
private $admin_action;
public function __construct( Event_Handler $event_handler, $admin_action )
{
$this->event_handler = $event_handler;
$this->admin_action = $admin_action;
}
public function render()
{
global $title;
?>
<div class="wrap">
<h1><?=$title?></h1>
<form method="post" action="<?php print admin_url( 'admin-post.php' ); ?>">
<input type="hidden" name="action" value="<?=$this->admin_action?>">
<?php
$this->event_handler->trigger_event( 'admin.options_page.form.content' );
submit_button();
?>
</form>
</div>
<?php
}
}
class Options_Page_Form_Ajax_Content
{
private $validator;
private $ajax_action;
public function __construct( Request_Validator $validator, $ajax_action )
{
$this->validator = $validator;
$this->ajax_action = $ajax_action;
}
public function render( $button_id, $container_id )
{
wp_nonce_field(
$this->validator->get_nonce_action(),
$this->validator->get_nonce_name()
);
?>
<div id="<?=$container_id?>">
<?php
submit_button( 'Get Editor', 'secondary', $button_id, FALSE );
?>
</div>
<?php
$this->get_script( $button_id, $container_id );
}
private function get_script( $button_id, $container_id )
{
$nonce_name = $this->validator->get_nonce_name();
$nonce_value = wp_create_nonce( $this->validator->get_nonce_action() );
?>
<script>
jQuery(document).ready(function( $ ){
$('#<?=$button_id?>').click(
function() {
var data = {
<?=$nonce_name?>: '<?=$nonce_value?>',
action: '<?=$this->ajax_action?>'
};
$.post(ajaxurl, data, function( html ) {
$( "#<?=$container_id?>" ).html( html );
});
return false;
}
);
});
</script>
<?php
}
}
interface Event_Handler
{
public function trigger_event( $name, $data = NULL );
}
interface Request_Validator
{
public function __construct( $base, $network = TRUE );
public function get_nonce_name();
public function get_nonce_action();
public function is_valid( $ajax = FALSE );
}
class Basic_Request_Validator implements Request_Validator
{
private $action;
private $name;
public function __construct( $base, $network = TRUE )
{
$this->name = $base . '_name';
if ( ! $network )
$this->name .= '_' . get_current_blog_id();
$this->action = $base . '_action';
}
public function get_nonce_name()
{
return $this->name;
}
public function get_nonce_action()
{
return $this->action;
}
public function is_valid( $ajax = FALSE )
{
return wp_verify_nonce( $_REQUEST[ $this->name ], $this->action );
}
}
interface Data
{
public function get();
public function save( $value );
}
class Options_Data implements Data
{
private $name;
public function __construct( $name )
{
$this->name = $name;
}
public function get()
{
return get_option( $this->name );
}
public function save( $value )
{
update_option( $this->name, $value );
}
}
class Admin_Post_Handler
{
private $data;
private $validator;
private $post_field;
public function __construct( Data $data, Request_Validator $validator, $post_field )
{
$this->data = $data;
$this->validator = $validator;
$this->post_field = $post_field;
}
public function update()
{
if ( ! $this->validator->is_valid() )
die( 'nope' );
$content = '';
if ( isset ( $_POST[ $this->post_field ] ) )
$content = $_POST[ $this->post_field ];
$this->data->save( $content );
wp_safe_redirect( $_POST['_wp_http_referer' ] );
exit;
}
}
new Controller;
@franz-josef-kaiser
Copy link

Only briefly read over the code, but isn't there some sanitization missing?

@karlme3
Copy link

karlme3 commented Oct 23, 2014

Hi, i'm trying to get it work in my plugin. It work correctly whith one editor but if i add more, i get this issue:
second editor: http://i.imgur.com/nJB5hEq.png
first editor: http://i.imgur.com/oDMYGM5.png

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment