thanks in advance for explaining the idea of creating multiple content blocks in Wordpress post editor in the admin panel. I tried searching for similar thread before asking and couldn't find any answer.
What I need is to create an additional content field along with the default one. What functions do I need to implement please? I found a plugin "Multiple Content Blocks" in wordpress plugin library but I believe this simple task will require fewer codes. I hope I have explained well what I need. Thanks again!
Oldish question, but since it was the first hit on my google search I'll add my method - there are a few major issues with the accepted answer, primarily:
using a meta box, when the documentation clearly states that it is not safe for wp_editor().
using sanitize_text_field() to sanitize html (which removes all html!)
This file can be dropped into your theme folder or at the bottom of your functions.php, and all posts and pages will get an additional editor.
If using a separate file just remember to include it from your functions.php:
include __DIR__ . '/my_extra_content.php';
Obviously you should do a search for "my_" and replace with something meaningful - "extra_content" may also be a tad too generic, so come up with something more exotic.
<?php
//Use a class to avoid conflicts
class my_extra_content {
/**
* Called on instantiation, this is where we hook functions
*/
function __construct() {
/* Using add_meta_box seems like the correct way to do this, but since
* we're inserting a TinyMCE editor we cannot (should not) - from codex:
* ---
* Once instantiated, the WYSIWYG editor cannot be moved around in the
* DOM. What this means in practical terms, is that you cannot put it in
* meta-boxes that can be dragged and placed elsewhere on the page.
* Instead use 'edit_page_form' (for pages) or 'edit_form_advanced'
* (for other post types).
*/
add_action( 'edit_page_form', array($this, 'my_extra_content_custom_box') );
add_action( 'edit_form_advanced', array($this, 'my_extra_content_custom_box') );
/* This one saves the content */
add_action( 'save_post', array($this, 'save_postdata' ));
}
/**
* This actually outputs the tinyMCE box
*/
function my_extra_content_custom_box( $post ) {
/* Always use a nonce */
wp_nonce_field( 'my_extra_content_custom_box', 'my_extra_content_custom_box_nonce' );
/* Get the content */
$content = self::get_content($post);
/* Insert the editor */
wp_editor( $content, "my_extra_content");
}
/**
* Saves the content
*/
function save_postdata( $post_id ) {
/* Check that nonce was sent */
if ( ! isset( $_POST['my_extra_content_custom_box_nonce'] ) ) {
return $post_id;
}
/* Check that nonce is valid */
if ( ! wp_verify_nonce( $_POST['my_extra_content_custom_box_nonce'], 'my_extra_content_custom_box' ) ) {
return $post_id;
}
/* Don't try to do anything on autosave (custom fields aren't included) */
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return $post_id;
}
/* Check permissions */
if ( 'page' === get_post_type( $post_id ) ) {
if ( ! current_user_can( 'edit_page', $post_id ) ) {
return $post_id;
}
} else {
if ( ! current_user_can( 'edit_post', $post_id ) ) {
return $post_id;
}
}
/* Sanitize content - we don't use sanitize_text_field() as it strips all
* HTML, which is clearly not wanted with a wysiwyg - wp_kses_post()
* should do what we want */
$sane_content = wp_kses_post( $_POST['my_extra_content'] );
/* Save content - notice the underscore in the meta name - it hides the
* field from the "normal" custom field editor */
update_post_meta( $post_id, '_my_extra_content_content', $sane_content );
}
/**
* Static function makes it easy to get the value wherever you need it.
* - for example:
* $my_extra_content = my_extra_content::get_content()
*/
static function get_content($post_or_post_id = null) {
/* First find the post id */
$post_id = false;
if ($post_or_post_id === null) {
/* If nothing was passed, try to get it from global post object */
global $post;
$post_or_post_id = $post->ID;
}
if (is_a($post_or_post_id, 'WP_Post')) {
/* If a post object was passed, or we're using the global $post */
$post_id = $post_or_post_id->ID;
} elseif (is_numeric($post_or_post_id)) {
/* If a number (hopefully a post id) was passed */
$post_id = intval($post_or_post_id);
}
/* Try to get the value */
$value = get_post_meta($post_id, '_my_extra_content_content', true );
/* If we didn't get a valid string return an empty one */
if (!is_string($value)) {
return '';
}
return $value;
}
/**
* Static function to very easily output the content in a template
* - for example:
* my_extra_content::echo_content()
*/
static function echo_content( $post_or_post_id = null ) {
$output = self::get_content($post_or_post_id);
/* do_shortcode makes sure we support shortcodes (if that is wanted) */
// $output = do_shortcode($output);
/* the_content filter will apply all normal filters (including
* do_shortcode) to the content (not required!) */
$output = apply_filters( 'the_content', $output);
/* print it */
echo $output;
}
}
/* Instantiate the class - because of the static functions used to fetch the
* content we won't need to ever use this variable, we just need __construct()
* to be called, so our hooks are added */
$extra_content_throwaway_var = new my_extra_content();
First of all adding content editors to Wordpress edit pages is a lot harder than it sounds, so if you are not familiar with the save/update cycle and metaboxes then I would recommend using a plugin. I like "Advanced Custom Fields" but I'm sure "Multiple Content Blocks" is good too.
In any case I have outlined a general Custom Meta Box solution here. So here we go:
The wp_editor() function is what we use to create an editor instance. http://codex.wordpress.org/Function_Reference/wp_editor
However, I would call this within a meta box.
http://codex.wordpress.org/add_meta_box
Here is some sample code that creates a meta box with a content editor in it.
This plugin stores the value of the content editor in a custom field called _hurtigtech_extra_content that gets saved when the post/page is updated update.
You can drop this plugin into the plugins folder /wp-content/plugins/ and play with it there. Feel free to leave comments if you need help with this, I know it is a lot of code so again the plugins might be best, but this is also a good baseline if you feel confident.
<?php
/**
* Plugin Name: Extra Metabox Content Editor
*/
/**
* Adds a box to the main column on the Post and Page edit screens.
*/
function hurtigtech_add_custom_box() {
$screens = array( 'post', 'page' );
foreach ( $screens as $screen ) {
add_meta_box(
'hrutigtech_extra_content_section',
__( 'My Post Extra Content', 'hurtigtech_translations' ),
'hurtigtech_inner_custom_box',
$screen
);
}
}
add_action( 'add_meta_boxes', 'hurtigtech_add_custom_box' );
/**
* Prints the box content.
*
* #param WP_Post $post The object for the current post/page.
*/
function hurtigtech_inner_custom_box( $post ) {
// Add an nonce field so we can check for it later.
wp_nonce_field( 'hurtigtech_inner_custom_box', 'hurtigtech_inner_custom_box_nonce' );
/*
* Use get_post_meta() to retrieve an existing value
* from the database and use the value for the form.
*/
$value = get_post_meta( $post->ID, '_hurtigtech_extra_content', true );
echo '<br />';
wp_editor( $value, "hurtigtech_extra_content_editor");
}
/**
* When the post is saved, saves our custom data.
*
* #param int $post_id The ID of the post being saved.
*/
function hurtigtech_save_postdata( $post_id ) {
/*
* We need to verify this came from the our screen and with proper authorization,
* because save_post can be triggered at other times.
*/
// Check if our nonce is set.
if ( ! isset( $_POST['hurtigtech_inner_custom_box_nonce'] ) )
return $post_id;
$nonce = $_POST['hurtigtech_inner_custom_box_nonce'];
// Verify that the nonce is valid.
if ( ! wp_verify_nonce( $nonce, 'hurtigtech_inner_custom_box' ) )
return $post_id;
// If this is an autosave, our form has not been submitted, so we don't want to do anything.
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
return $post_id;
// Check the user's permissions.
if ( 'page' == $_POST['post_type'] ) {
if ( ! current_user_can( 'edit_page', $post_id ) )
return $post_id;
} else {
if ( ! current_user_can( 'edit_post', $post_id ) )
return $post_id;
}
/* OK, its safe for us to save the data now. */
// Sanitize user input.
$mydata = sanitize_text_field( $_POST['hurtigtech_extra_content_editor'] );
// Update the meta field in the database.
update_post_meta( $post_id, '_hurtigtech_extra_content', $mydata );
}
add_action( 'save_post', 'hurtigtech_save_postdata' );
NOTE: There is a style issue with the content box background. This needs to be added to an editor-style.css file to fix it.
.hurtigtech_extra_content_editor {
background: #fff;
}
Related
I have a php class inside my plugin that generates custom metaboxes for my post types.
Theses metaboxes contain inputs that I want to save with the "save_post" hook, as I have done a million times :
class CcmComponentMetaboxElement{
public function __construct($config){
/* Some logic */
$this->config = $config;
$this->ccm_metabox_display();
$this->ccm_metabox_save();
}
public function ccm_metabox_display(){
$metabox_id = $this->config->id;
$context = property_exists($this->config, 'context') ? $this->config->context : 'advanced';
$priority = property_exists($this->config, 'priority') ? $this->config->priority : 'default';
add_meta_box(
$metabox_id,
$this->config->title,
array($this, 'ccm_metabox_display_callback'),
$this->config->screens,
$context,
$priority
);
}
public function ccm_metabox_display_callback(){
/* Some logic that displays metabox with custom post_metadata inside */
}
public function ccm_metabox_save(){
foreach($this->config->screens as $screen){
$action_name = 'save_post_' . $screen;
add_action($action_name, function($post_id){
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}
if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
return;
}
if(empty($_POST)){
error_log( 'EMPTY POST' );
return;
}
if ( ! array_key_exists( $this->nonce_name, $_POST ) ||
!wp_verify_nonce( $_POST[$this->nonce_name], $this->nonce_value )) {
error_log( 'NONCE NOT VERIFIED' );
return;
}
foreach($this->config->fields as $field){
if(array_key_exists( $field->id, $_POST )){
update_post_meta( $post_id, $field->id, $_POST[$field->id]);
}
}
}, 10, 1);
}
}
}
The issue i'm facing this time, is that when I'm saving a post with a custom metabox, my hook from my plugin is well executed, but the script stops when checking if $_POST is empty (it records the 'EMPTY POST' inside my debug.log file).
After clicking on "update", fetch executes the save action but returns 302 from the server
The system always returns a 302 status code and $_POST is always empty, no matter what I write in my function, except when I var_dump the $_SERVER variable (for some reason, when I do this it works fine, but the Worpress fetch api doesn't like it).
Does anyone have any suggestions to help me ?
Thanks !
I tried many solutions that didn't work in my case, including refreshing permalinks, forcing https in wp-config, ....
I only activated my plugin and an empty theme of mine with the bare minimum.
I have the same problem with the hook 'restafterinsert_post'
Testing the cart page cross-browser I noticed that the cart-item-thumbnail image disappears on some instances, when adding, removing items or altering addresses on the cart page.
I found out, that the presence of the srcset attribute is causing this.
I tried to disable the srcset attribute for the cart page like so:
function cart_disable_srcset() {
if ( is_cart()) {
return false;
}
}
add_filter( 'wp_calculate_image_srcset', 'cart_disable_srcset' );
The codition if ( is_cart()) is ignored, and the srcset is removed site-wide.
Then I found a function, that removes the srcset for the_post_thumbnail( 'full' ) and
the_post_thumbnail():
/**
* Remove the srcset attribute from post thumbnails
* that are called with the 'full' size string: the_post_thumbnail( 'full' )
*
* #link http://wordpress.stackexchange.com/a/214071/26350
*/
add_filter( 'post_thumbnail_size', function( $size )
{
if( is_string( $size ) && 'full' === $size )
add_filter(
'wp_calculate_image_srcset_meta',
'__return_null_and_remove_current_filter'
);
return $size;
} );
// Would be handy, in this example, to have this as a core function ;-)
function __return_null_and_remove_current_filter ( $var )
{
remove_filter( current_filter(), __FUNCTION__ );
return null;
}
In my cart.php template if have the following line:
$thumbnail = apply_filters( 'woocommerce_cart_item_thumbnail', $_product->get_image('my_small_size'), $cart_item, $cart_item_key );
I tried to use this with the function mentioned above:
add_filter( 'woocommerce_cart_item_thumbnail', function( $size )
{
if( is_string( $size ) && 'my_small_size' === $size )
add_filter(
'wp_calculate_image_srcset_meta',
'__return_null_and_remove_current_filter'
);
return $size;
} );
But that doesn’t bear the desired result.
How can I configure the above mentioned function to remove the srcset for the cart-item-thumbnail and all other images that are referenced with my_small_size? Can this be achieved at all with this function, or is there another way?
Hook into wp_calculate_image_srcset read: detailed explanation.
$source is an array that holds all images for the srcset attribute.
The custom image size 65*65, which is displayed on my cart-page, can be found by conditionally checking the $size_array. If the $size_array has 65, then unset all unwanted sizes.
The srcset is gone. No more vanishing cart-item-thumbnails when altering the cart page items on certain browsers. The rest is configuring the srcset according to my needs. It may be helpful as an example.
add_filter( 'wp_calculate_image_srcset', 'my_custom_image_srcset', 10, 5);
function my_custom_image_srcset($sources, $size_array, $image_src, $image_meta, $attachment_id) {
//remove unwanted sizes from $source array
$remove = ['150', '300'];
$sources = array_diff_key($sources, array_flip($remove));
//remove unwanted sizes from $source array if thumbnail width is 65
if ($size_array[0] === 65){
$remove = ['160', '490','768','800'];
$sources = array_diff_key($sources, array_flip($remove));
}
//remove unwanted sizes for archive pages
else if (is_archive()){
$remove = ['65', '768','800'];
$sources = array_diff_key($sources, array_flip($remove));
}
//remove unwanted sizes for product pages
else if (is_product()){
$remove = ['65', '160'];
$sources = array_diff_key($sources, array_flip($remove));
}
//check results
//print_r($size_array);
//print_r($sources);
return $sources;
}
Unfortunately nobody commented the function mentioned in the question.
But this hook works for me.
I'm using the wpjobmanager plugin.
There's a field called 'company logo' that displays a default logo on every job. I'm trying to disable it and only show a logo when a user has specifically uploaded one.
I created this IF statement, but for some reason, the default logo is not considered empty so it continues to display it. I've tried null too and get the same result.
$is_logo = get_post($post->ID, '_company_logo', true);
if (!empty($is_logo)) {
the_company_logo();
}
Could someone point me in the right direction here? I'm at a loss.
Thanks
Here is the code for get_the_company_logo which returns empty string if not set.
/**
* Gets the company logo.
*
* #since 1.0.0
* #param int|WP_Post $post (default: null).
* #param string $size
* #return string Image SRC.
*/
function get_the_company_logo( $post = null, $size = 'thumbnail' ) {
$post = get_post( $post );
if ( has_post_thumbnail( $post->ID ) ) {
$src = wp_get_attachment_image_src( get_post_thumbnail_id( $post->ID ), $size );
return $src ? $src[0] : '';
} elseif ( ! empty( $post->_company_logo ) ) {
// Before 1.24.0, logo URLs were stored in post meta.
return apply_filters( 'the_company_logo', $post->_company_logo, $post );
}
return '';
}
So if you did
if (!empty(get_the_company_logo($post->ID)){
the_company_logo();
}
This would seem like it should work. I am not using this plugin, so I can't really test this.
Try this the_company_logo() function:
if(!empty(the_company_logo())) {
get_the_company_logo()
}
I read this function here -> https://wpjobmanager.com/document/template-tags/.
Say me if you need some help.
I have a home page and shop page. I don't want to set my home page to shop page.
Home page is full of custom content and widgets, and right before this content I would like to have list of products with same pagination as on shop page.
This means I could not use shortcodes to display products as default shortcodes do not support pagination.
Have gone through all the docs and tried possible ways. But it always breaks something.
Any advice will appreciated.
Thx.
P.S. While you reading this I'm still trying to find solution, so I might post something soon :)
Finally found awesome solution right here.
I'm using shortcode [product_category per_page="4" category="category-slug"]
To make pagination alive, you need to activate this pagination.
Here is the exact code snippet that need to be pasted into the theme's functions.php:
if ( defined('WC_VERSION') ) {
// ---------------------- WooCommerce active -------------------
/**
* Set Pagination for shortcodes custom loop on single-pages.
* #uses $woocommerce_loop;
*/
add_action( 'pre_get_posts', 'kli_wc_pre_get_posts_query' );
function kli_wc_pre_get_posts_query( $query ) {
global $woocommerce_loop;
// Get paged from main query only
// ! frontpage missing the post_type
if ( is_main_query() && ( $query->query['post_type'] == 'product' ) || ! isset( $query->query['post_type'] ) ){
if ( isset($query->query['paged']) ){
$woocommerce_loop['paged'] = $query->query['paged'];
}
}
if ( ! $query->is_post_type_archive || $query->query['post_type'] !== 'product' ){
return;
}
$query->is_paged = true;
$query->query['paged'] = $woocommerce_loop['paged'];
$query->query_vars['paged'] = $woocommerce_loop['paged'];
}
/** Prepare Pagination data for shortcodes on pages
* #uses $woocommerce_loop;
*/
add_action( 'loop_end', 'kli_query_loop_end' );
function kli_query_loop_end( $query ) {
if ( ! $query->is_post_type_archive || $query->query['post_type'] !== 'product' ){
return;
}
// Cache data for pagination
global $woocommerce_loop;
$woocommerce_loop['pagination']['paged'] = $woocommerce_loop['paged'];
$woocommerce_loop['pagination']['found_posts'] = $query->found_posts;
$woocommerce_loop['pagination']['max_num_pages'] = $query->max_num_pages;
$woocommerce_loop['pagination']['post_count'] = $query->post_count;
$woocommerce_loop['pagination']['current_post'] = $query->current_post;
}
/**
* Pagination for shortcodes on single-pages
* #uses $woocommerce_loop;
*/
add_action( 'woocommerce_after_template_part', 'kli_wc_shortcode_pagination' );
function kli_wc_shortcode_pagination( $template_name ) {
if ( ! ( $template_name === 'loop/loop-end.php' && is_page() ) ){
return;
}
global $wp_query, $woocommerce_loop;
if ( ! isset( $woocommerce_loop['pagination'] ) ){
return;
}
$wp_query->query_vars['paged'] = $woocommerce_loop['pagination']['paged'];
$wp_query->query['paged'] = $woocommerce_loop['pagination']['paged'];
$wp_query->max_num_pages = $woocommerce_loop['pagination']['max_num_pages'];
$wp_query->found_posts = $woocommerce_loop['pagination']['found_posts'];
$wp_query->post_count = $woocommerce_loop['pagination']['post_count'];
$wp_query->current_post = $woocommerce_loop['pagination']['current_post'];
// Custom pagination function or default woocommerce_pagination()
kli_woocommerce_pagination();
}
/**
* Custom pagination for WooCommerce instead the default woocommerce_pagination()
* #uses plugin Prime Strategy Page Navi, but added is_singular() on #line16
*/
remove_action('woocommerce_after_shop_loop', 'woocommerce_pagination', 10);
add_action( 'woocommerce_after_shop_loop', 'kli_woocommerce_pagination', 10);
function kli_woocommerce_pagination() {
woocommerce_pagination();
}
}// END WOOCOMMERCE
Nice way to go for this try. The next thing is to make pagination ajax based without page reloading...
If I have lots of pages... page_ids 1-100... how do I link between the two in the editor?? I guess I can use Link but that's not user friendly... I want to do something like Link but that doesn't work either. Is there a handy plugin?
Use a shortcode.
Add the following to your themes’ functions.php:
if ( ! function_exists('toscho_id_to_link') )
{
/**
* Creates a link from the post id.
*
* Usage: [link id=42 title="The Meaning of Life?" class="pseudophilosphical"]Guess![/link]
*
* Inspired by Sergej Müller
* #see http://playground.ebiene.de/2388/wordpress-shortcode-links/
* #param array $atts id (numeric) and additional HTML attributes
* #param string $data
* #return string
*/
function toscho_id_to_link($atts, $data)
{
// incomplete
if ( ! isset ( $atts['id'] ) or ! is_numeric($atts['id']) )
{
return $data;
}
// test
$url = get_permalink($atts['id']);
// No entry with this ID.
if ( ! $url )
{
return $data;
}
unset ( $atts['id'] );
$attributes = '';
// more attributes?
if ( ! empty ($atts) )
{
foreach ($atts as $key => $value )
{
$attributes .= " $key='$value'";
}
}
return "<a href='$url'$attributes>$data</a>";
}
add_shortcode('link', 'toscho_id_to_link');
}
You may find this plugin helpful: Simply show IDs.
We use RB-Internal-Links. It allows you to link using a shortcode and slug, or even has a WYSIWYG interface.
There are plugins that you can use to insert PHP in your posts or pages. Maybe using one of those will let you use your second suggestion.
You should really use complete and full URLs for all links in WordPress. http://example.com/index.php?page_id=123 , for example.
Using partial links will result in strange behaviors in feeds, on category archives, etc.