I'm having difficulties with wp_editor(), tinyMCE and the_content filter in relation to oEmbed of video.
I am outputting a front end wp_editor() form to allow registered users to create new posts from the front end of the site. The new post created is a custom post type.
The target behaviour is:
The registered user enters content & clicks submit
The form is processed by jQuery/Ajax, with form data passed to a PHP function via post()
A new post created, and a response is generated for an Ajax callback
The response is a JSON array that contains the HTML of the new post content
The returned HTML has 'the_content' filter applied - embedded video should display properly
The Ajax callback removes the original form and appends the post HTML to a div
Everything works as expected with the exception of video oEmbed.
If a video link is added to the content (on a new line within the wp_editor), the content built by the Ajax callback includes the video URL wrapped in paragraph tags - oEmbed hasn't worked, even though the HTML has had 'the_content' filter applied.
Refreshing the page displays the new post in a loop, with content displayed by the_content() tag - and the video is displayed properly (oEmbed has worked).
Setting 'wpautop' => false in the wp_editor arguments doesn't help - messes up formatting, doesn't fix video.
Is there a tinyMCE setting that I'm missing?
Is there a problem with how I'm applying 'the_content' filter and/or building a HTML string for the Ajax callback?
Relevant code shown below.
Thanks!
JQuery
(function( $ ) { 'use strict';
$(function() {
$('#student-submission-button').click( function(event) {
// Prevent default action
// -----------------------
event.preventDefault();
var submission_nonce_id = $('#the_nonce_field').val();
var submission_title = $('#inputTitle').val();
tinyMCE.triggerSave();
var submission_content = $('#editor').val();
var Data = {
action: 'student_submission',
nonce: submission_nonce_id,
workbook_ID: submission_workbook_ID,
content: submission_content,
title: submission_title,
};
// Do AJAX request
$.post( ajax_url, Data, function(Response) {
if( Response ) {
var submissionStatus = Response.status;
var submissionMessage = Response.report;
var postHTML = Response.content;
if ( 'success' == submissionStatus ) {
$('#user-feedback').html( submissionMessage );
$('#new-post').append( postHTML );
}
// Hide the form
$('.carawebs-frontend-form').hide(800, function() {
$(this).remove();
});
}
});
});
});
})( jQuery );
PHP
/**
* Return data via Ajax (excerpt)
*
*
*/
$response = array();
if( is_int( $new_submission_ID ) ) {
// Build a success response
// ------------------------
$new_post = get_post( $new_submission_ID, OBJECT );
$new_post_content = $new_post->post_content;
$return_content = "<h2>$new_post->post_title</h2>";
$return_content .= apply_filters( 'the_content', $new_post_content );
$response['status'] = "success";
$response['report'] = "New post created, ID: $new_submission_ID";
$response['content'] = $return_content;
} else {
// error report
}
wp_send_json( $response ); // send $response as a JSON object
Form HTML and wp_editor()
<form action="<?php echo $_SERVER['REQUEST_URI']; ?>" method="post" enctype="multipart/form-data" class="carawebs-frontend-form">
<label for="inputTitle">Title</label>
<input type="text" class="form-control" id="inputTitle" name="inputTitle" placeholder="Title" value="" />
<label for="inputContent" class="topspace">Your Content</label>
<?php
$args = array(
'textarea_rows' => 45,
'teeny' => false,
'editor_height' => 400,
'editor_class' => 'cwfrontendadmin',
'quicktags' => false,
'textarea_name' => 'cw_content',
'tinymce' => array(
'content_css' => get_template_directory_uri() . '/assets/css/editor-style.css'
),
);
wp_editor( 'Enter your content...', 'editor', $args );
wp_nonce_field('name_of_action','the_nonce_field', true, true ); // name of action, name of nonce field
?>
<input id="student-submission-button" class="btn btn-primary" type="submit" name="submission-form" value="Save Content" />
Update
I've narrowed this down to the way that the_content filter is applied. I think filtered content is cached, so oEmbed may not get applied to all content if the post content is returned outside the loop.
Now I have video oEmbed working - using a different method of inserting post_content into a variable:
<?php
global $post;
$post = get_post($new_submission_ID);
setup_postdata( $post );
$new_content = apply_filters('the_content', get_the_content());
$new_post_link = get_the_permalink();
$new_post_title = get_the_title();
wp_reset_postdata( $post );
This works fine, but it would be good if someone could explain why the original method of building the HTML didn't work.
The oEmbed filter does not get applied if the post content is returned outside the loop, and global $post is not set.
This is because the video embed content is cached in the postmeta table, and is is inaccessible if the post content is returned outside the loop. The WP_Embed class which is hooked onto by the_content filter is not designed for use outside the loop - this is referenced on Trac here.
The following method returns a working Video oEmbed as per the original scenario. global $post needs to be set to make the cached video embed data available:
<?php
global $post;
$post = get_post( $new_submission_ID );
setup_postdata( $post );
$new_content = apply_filters( 'the_content', get_the_content() );
wp_reset_postdata( $post );
// return what you need via Ajax callback -
// $new_content contains the proper video embed HTML
TL;DR Version
If you need oEmbed filters to be applied outside the loop, you need to set the global post variable so that the WP_Embed class can access the cached video embed html in the post's meta.
The reason, according to other sources is because the WP_Embed::shortcode() (tasked with swapping out the video for the embedded html) is calling on the global $post variable for information.
This means you need to be sure that the global $post variable contains the information of the post you're requesting.
My AJAX response method now incorporates the global $post variable:
global $post;
$post = get_post( $id );
echo apply_filters( 'the_content', $post->post_content );
die();
This is essentially just an extension of your own findings, but I thought it might help to have a clear answer/solution to the problem of oEmbed + AJAX for WordPress.
Related
I have a shortcode that generates a gallery, given the gallery ID.
function rb_scroll_gallery_shortcode( $atts, $content ) {
$a = shortcode_atts( array(
'id' => -1,
), $atts );
$gallery_ID = $a['id'];
$output = '';
if($gallery_ID != -1){
ob_start();
$gallery = new RB_Scroll_Gallery($gallery_ID);
$gallery->render();
$output = ob_get_clean();
}
return $output;
}
add_shortcode( 'rb_scroll_gallery', 'rb_scroll_gallery_shortcode' );
Now, I've made a Gutenberg block that works perfectly in the editor. You can select a gallery and it will save. However, I dont want to repeat code and have the html in the save property and in the php code.
So I was wondering if there is a way to use that same rb_scroll_gallery_shortcode function to render the block in the front end.
I've seen that you can use register_block_type and set the render_callback to rb_scroll_gallery_shortcode, but I need the ID selected in the block to send it to the function in the $atts array
//This uses the shortcode funtion, but doesn't gives the gallery ID
register_block_type( 'cgb/block-rb-scroll-gallery-block', array(
'render_callback' => 'rb_scroll_gallery_shortcode',
) );
You can try to Convert a Shortcode to a Gutenberg Block and after use in your theme,
Registering the Dynamic Block Callback in PHP
/**
* Register the GitHub Gist shortcode
*/
function gggb_init() {
add_shortcode( 'github-gist', 'gggb_render_shortcode' );
register_block_type( 'github-gist-gutenberg-block/github-gist', array(
'render_callback' => 'gggb_render_shortcode',
) );
}
add_action( 'init', 'gggb_init' );
When your block is rendered on the frontend, it will be processed by your render callback:
function gggb_render_shortcode( $atts ) {
if ( empty( $atts['url'] )
|| 'gist.github.com' !== parse_url( $atts['url'], PHP_URL_HOST ) ) {
return '';
}
return sprintf(
'<script src="%s"></script>',
esc_url( rtrim( $atts['url'], '/' ) . '.js' )
);
}
**Note:** this render callback is intentionally different than the Gutenberg block’s edit callback. Our preference is to use GitHub’s provided JavaScript embed code because this lets GitHub change the embed’s behavior at a future date without requiring a developer to make changes.
Refer link for get more information, https://pantheon.io/blog/how-convert-shortcode-gutenberg-block
I've found out the little thing that messed up my code. The problem wasn't that the render_callback() wasn't getting any attributes (though it wasn't), but it was that the editor wasn't saving them because I forgot to remove some testing data from the attribute galleryID
In the registerBlockType:
The save() method should return null.
The attribute should not have a selector data, since it is used to find the value on the markup return by the save(), wich in this case returns null. I've forgot to remove this data, thats why the attribute wasn't being saved.
attributes: {
galleryID: {
type: 'string',
//This data should only be set if the value can be found in the markup return by save().
//Since save() returns null, we let it out
/*source: 'attribute',
/*attribute: 'data-gallery-id',
/*selector: '.rb-scroll-gallery-holder',*/
},
}
New to Wordpress and struggling with the meta boxes. Unable to get my piece of code to work/show, nor was I able find examples that work. Nothing just wants to show up in the admin panel and have no clue what I'm missing / doing wrong.
Until now it looks like it's not triggering the callback function or something.
What I want to do:
Show a checkbox to hide the page's title
What I see:
No checkbox is showing up on the page edit screen
My code in functions.php:
function twentyseventeenchild_hide_title() {
add_meta_box(
'no-title', // Unique id
__( 'Hide title' ), // Title
'twentyseventeenchild_hide_title_callback', // Callback
'post', // Screen (such as post type, link or comment)
'normal', // Context (normal, advanced, side)
'default' // Priority (default, core, high, low)
// Callback arguments
);
}
add_action( 'add_meta_boxes', 'twentyseventeenchild_hide_title' );
/**
* Meta box display callback.
*
* #param WP_Post $post Current post object.
*/
function twentyseventeenchild_hide_title_callback( $post ) {
$meta = get_post_meta($post->ID, '_title', true);
?>
<label><?php __('Hide title') ?></label>
<input id="no_title" type="checkbox" autocomplete="off" value="<?=esc_attr($meta)?>" name="page_title">
<?php
}
You're using checkbox incorrectly.
See input checkbox: https://www.w3schools.com/tags/att_input_type_checkbox.asp
Your method need to be like:
function twentyseventeenchild_hide_title_callback( $post ) {
$meta = get_post_meta($post->ID, '_title', true);
?>
<input id="no_title" type="checkbox" <?php ($meta === '1' ? 'selected' : ''); ?> name="page_title">
<label for="no_title"><?php _e('Hide title') ?></label>
<?php
}
_e() is for translate and echo, __() only return string and not print it.
($meta === '1' ? 'selected' : ''); is because when you save metabox value truewill be saved as string 1. With this ternary you can show as selected this metabox if value is euqals 1 (as string).
I have a custom post type called request created with Toolset plugin.
I need to add a button in these admin pages (only for this custom post type):
new request (post-new.php)
edit request (post.php)
When admin submit the button I need to send an email (no problem for this) with info about the post.
How can I add the button and a callback to submit?
One way to add new elements to the Post Edit screen is via metaboxes.
Please check the following code:
/**
* Adds a call-to-action metabox to the right side of the screen under the "Publish" box.
*/
function wp645397_add_cta_metabox() {
add_meta_box(
'wp645397_request_cta',
'Call-to-action title here', /* This is the title of the metabox */
'wp645397_request_cta_html',
'request', /* the request post type */
'side',
'high'
);
}
add_action( 'add_meta_boxes', 'wp645397_add_cta_metabox' );
/**
* Output the HTML for the call-to-action metabox.
*/
function wp645397_request_cta_html() {
global $post;
// Nonce field to validate form request came from current site
wp_nonce_field( 'request_send_post_details', 'request_cta_nonce' );
// Output the call-to-action button
?>
Call-to-action button text here
<script>
/**
* We'll handle the button via Ajax to avoid having to reload the screen.
*/
jQuery(function($){
// Handle button click
$( "#wp645397_request_cta #btn-call-to-action" ).on("click", function(e){
e.preventDefault();
// Get the security nonce
var nonce = $(this).parent().find("#request_cta_nonce").val();
// Send the data via AJAX
$.post(
ajaxurl,
{
action: 'send_request_email',
nonce: nonce,
postid: <?php echo $post->ID; ?>
},
function( response ){
// Do something after the data has been sent
if ( 'OK' == response ) {
alert( 'Info sent' );
}
else {
alert( 'Something went wrong, try again' );
}
}
);
});
});
</script>
<?php
}
/**
* Handles CTA AJAX.
*/
function wp645397_send_request_email(){
// Invalid nonce
if ( !isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'request_send_post_details' ) ) {
wp_die( 'error' );
}
$post_ID = $_POST['postid'];
// Get post data
$post = get_post( $post_ID );
// Get post title
$post_title = $post->post_title;
// Get custom field, etc
// #TODO
// Send e-mail with data
// #TODO
wp_die('OK');
}
add_action( 'wp_ajax_send_request_email', 'wp645397_send_request_email' );
What it does:
Adds a custom metabox right above the Publish box on the right.
When the user clicks on the "Call-to-action button text here" button (remember to rename it!), it'll send the ID of the current post (request) via Ajax to a function called wp645397_send_request_email, where you need to process the data and send the e-mail.
I added some comments in the code that should explain what's going on. If you have any questions don't hesitate to ask, ok?
I am not sure what options toolset plugin provides but if I were to do this programatically, I would do something like
$post_types = array('post', 'page');
$args = array(
'public' => true,
'_builtin' => false,
);
$output = 'names'; // names or objects, note names is the default
$operator = 'and'; // 'and' or 'or'
$fetch_post_types = get_post_types($args, $output, $operator);
foreach ($fetch_post_types as $key => $value) {
if ($value != 'vocabulary' && $value != 'augmentation') {
array_push($post_types, $value);
}
}
add_meta_box('email_sender_post_type_metabox', __('Email Options', 'seoaipdl'), 'email_sender_post_type_metabox', $post_types, 'side', 'high', null);
function email_sender_post_type_metabox() {
//Button HTML code here
}
add_action('save_post', 'save_meta_data_function', 10, 2);
function save_meta_data_function($id, $data) {
if (!current_user_can("edit_post", $id)) {
return $data;
}
if (defined("DOING_AUTOSAVE") && DOING_AUTOSAVE) {
return $data;
}
\\$_POST variable will have your submit button here.
}
Alternatively You can add a button using jquery and trigger your email function using ajax on this button click.
To do so you will need to look into admin_enqueue_script and wp_localize_script
i'm trying to learn the way for uploading image file through custom field but cant get the easiest code to do it. i just done a bit here:
add_action('admin_init', 'create_image_box');
function create_image_box() {
add_meta_box( 'meta-box-id', 'Image Field', 'display_image_box', 'post', 'normal', 'high' );
}
//Display the image_box
function display_image_box() {
global $post;
$image_id = get_post_meta($post->ID,'xxxx_image', true);
echo 'Upload an image: <input type="file" name="xxxx_image" id="xxxx_image" />';
// Upload done: show it now...(as thmbnail or 60 x 50)
anybody please take me to next step and show the way to display the image in blog page too.
Lets go Stepwise here:
Create custom field Meta Box for inserting Image Url in post type => post.
Update/Save the custom field value in back end.
Display the custom field value in front end.
Seeing your code it seems that you are missing #2. Try the code below to save custom field:
function save_joe_details($post_id){
global $post;
if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE )
return $post_id;
update_post_meta($post->ID, "custom_field_image", $_POST["custom_field_image"] );
}
add_action('save_post', 'save_joe_details');
Code for #3 that displaying the custom field will be:
<?php global $post;
$custom_image = get_post_custom($post->ID); ?>
<img src="<?php echo $custom_image["custom_field_image"][0] ?>" />
you could try wrap it in wp_get_attachment_url like this:-
wp_get_attachment_url( $custom_image["custom_field_image"][0] );
I wanted to create a plugin to batch manage posts' custom field data. I know I can add post meta by adding a meta box in the post edit screen and using add_action('save_post', 'function_to_update_meta') to trigger add meta functions.
But I don't know how to trigger the add_post_meta function in an admin menu page (such as a custom admin menu). How to do that?
Thank you in advance!
The example given in Wordpress' codex is probably the best and most secure in the way of processing information:
Add Meta Box
Copy and paste it and then fiddle around with it to get a good idea on how to control your posts and pages.
The nice part is that you don't need to worry about checking if you need to Add vs Update a given Post Meta field. Using Update Post Meta will ensure that the proper action is taken for you, even if the field doesn't exist.
The same goes for Update Option if you want to add some global controls that your plugin/theme might use.
BREAKDOWN EXAMPLE:
add_action( 'add_meta_boxes', 'myplugin_add_custom_box' );
add_action( 'save_post', 'myplugin_save_postdata' );
These are the action hooks. The first one is executed when meta boxes are being populated within the post editor, and the second is executed when a post is added OR updated.
function myplugin_add_custom_box()
{
add_meta_box(
'myplugin_sectionid',
__( 'My Post Section Title', 'myplugin_textdomain' ),
'myplugin_inner_custom_box',
'post'
);
add_meta_box(
'myplugin_sectionid',
__( 'My Post Section Title', 'myplugin_textdomain' ),
'myplugin_inner_custom_box',
'page'
);
}
This function is called by the 'add_meta_boxes' action hook. Notice the name of the function and the second argument of the action hook are exactly the same. This registers your meta boxes, which post types they're supposed to appear, and what callback is used to generate the form contained inside.
function myplugin_inner_custom_box( $post )
{
wp_nonce_field( plugin_basename( __FILE__ ), 'myplugin_noncename' );
$value = get_post_meta($post->ID, 'myplugin_new_field') ? get_post_meta($post->ID, 'myplugin_new_field') : 'New Field';
echo '<label for="myplugin_new_field">';
_e("Description for this field", 'myplugin_textdomain' );
echo '</label> ';
echo '<input type="text" id="myplugin_new_field" name="myplugin_new_field" value="'.$value.'" size="25" />';
}
This is the function that is called by the registered meta boxes to generate the form automatically. Notice how the function is called 'myplugin_inner_custom_box' and the 3rd argument in your meta box registration is also called 'myplugin_inner_custom_box'.
The wp_nonce_field() generates a hidden field in your form to verify that data being sent to the form actually came from Wordpress, and can also be used to end the function in case other plugins are using the 'save_post' action hook.
Notice also that the $post object is being passed in as an argument. This will allow you to use certain properties from the post object. I've taken the liberty of checking to see if there is get_post_meta() returns anything with the given post ID. If so, the field is filled with that value. If not, it is filled with 'New Field'.
function myplugin_save_postdata( $post_id )
{
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
return;
if ( !wp_verify_nonce( $_POST['myplugin_noncename'], plugin_basename( __FILE__ ) ) )
return;
if ( 'page' == $_POST['post_type'] )
{
if ( !current_user_can( 'edit_page', $post_id ) )
return;
}
else
{
if ( !current_user_can( 'edit_post', $post_id ) )
return;
}
$mydata = $_POST['myplugin_new_field'];
update_post_meta($post_id, 'myplugin_new_field', $mydata);
}
This is the function that is called by the 'save_post' action hook. Notice how the second argument of the second action hook and this function are both called 'myplugin_save_postdata'. First, there are a series of verifications our plugin must pass before it can actually save any data.
First, we don't want our meta boxes to update every time the given post is auto-updating. If the post is auto-updating, cancel the process.
Secondly, we want to make sure the nonce data is available and verify it. If no nonce data is available or is not verified, cancel the process.
Thirdly, we want to make sure the given user has the edit_page permission. The function first checks the post type, and then checks the appropriate permission. If the user does not have that permission, cancel the process.
Lastly, our plugin has finally been verified and we want to save the information. I took the liberty of adding in the final update_post_meta() line to show you how it all comes together.
Notice how $post_id was passed into the function as an argument. This is one of the pieces needed for the update_post_meta() function. The key was named 'myplugin_new_field' and the value of that metadata is now saved as whatever you input into that custom input field in your custom meta box.
That's about as easy as I can explain the whole process. Just study it, and get your hands dirty with code. The best way to learn is through application rather than theory.
The answer was from the same question I asked somewhere else
And I created my version of example
I added some console.log function for testing, but this is basically doning the same thing as #Chris_() answer:
Menu callback function to generate menu content (PHP):
function ajax_menu_callback() {
?>
<div class="wrap">
<div id="icon-themes" class="icon32"></div>
<h2>Test</h2>
<br />
<form>
<input id="meta" type ="text" name="1" value="<?php echo esc_html( get_post_meta( 1, 'your_key', true) ); ?>" />
<?php submit_button(); ?>
</form>
</div>
<?php
}
Then the javascript to print on the admin side (javascript, don't forget to include a jquery library):
jQuery(document).ready(function() {
$("form").submit(function() {
console.log('Submit Function');
var postMeta = $('input[name="1"]').val();
console.log(postMeta);
var postID = 1;
var button = $('input[type="submit"]');
button.val('saving......');
$.ajax({
data: {action: "update_meta", post_id: postID, post_meta: postMeta, },
type: 'POST',
url: ajaxurl,
success: function( response ) { console.log('Well Done and got this from sever: ' + response); }
}); // end of ajax()
return false;
}); // end of document.ready
}); // end of form.submit
Then the PHP function handle update_post_meta (PHP):
add_action( 'wp_ajax_update_meta', 'my_ajax_callback' );
function my_ajax_callback() {
$post_id = $_POST['post_id'];
$post_meta = $_POST['post_meta'];
update_post_meta( $post_id, 'your_key', $post_meta );
echo 'Meta Updated';
die();
} // end of my_ajax_callback()