Having code below (given in a class)
class X
{
public function __construct()
{
add_action( 'post_updated', array( $this, 'datachangecheck' ), 10, 3 );
}
function datachangecheck( $post_id, $post_after, $post_before )
{
if ( $post_before->post_title != $post_after->post_title )
{
//do whatever
}
return;
}
}
Above works fine when checking if a title has been changed, but is there any away way of managing the same thing when having metadata related to it?
I only want to know if any data has changed (I don't need any returned values). I can't manage to find any hook where I can see like meta_post_before and meta_post_after or similar. Can someone point me into right direction? Thank you!
This was kind of tricky, so I guess someone else might have the same issue so I share some code here and hopefully someone has any help from it :-) I have the most important part in the code below:
public function __construct()
{
add_action( 'post_updated', array( $this, 'datachangecheck' ), 10, 3 );
}
/*
* datachangecheck
*
* This function is called upon hook post_updated.
* The function checks if data for updated post has been changed,
* and saves a file when data has been changed
*
* #param string $post_id id of post affected
* #param WP_Post $post_after WP_Post after post has been updated
* #param WP_Post $post_before WP_Post before post has been updated
* #return N/A
*
*/
function datachangecheck( $post_id, $post_after, $post_before )
{
//Cast postobjects into arrays, so comparision is possible with builtin-php functions
$spf = (array)$post_before;
$spa = (array)$post_after;
//These would differ every update. so remove them for possible comparision
unset ( $spf['post_modified']);
unset ( $spf['post_modified_gmt']);
unset ( $spa['post_modified']);
unset ( $spa['post_modified_gmt']);
//Check if any difference between arrays (if empty no difference)
//If not empty, save file that tells plugin that data has been changed
$ard = array_diff ( $spf, $spa);
if ( !empty ( $ard ) )
{
$this->datahaschanged_save();
}
else
{
//No change of post native data, check if any metapost data has been changed
//Remove edit_last and edit_lock because they could differ without data being changed
$this->metadata_before = get_post_meta( $post_id );
unset ( $this->metadata_before['_edit_last']);
unset ( $this->metadata_before['_edit_lock']);
add_action('updated_post_meta', array( $this, 'checkmetadata_after'), 10, 2);
}
return;
}
/*
* checkmetadata_after
*
* This function is called upon hook updated_post_meta when data has been update, but no change in native post data
* has been made and saves a file when data has been changed
*
* #param string $post_id id of post affected
* #param WP_Post $post_after WP_Post after post has been updated
* #param WP_Post $post_before WP_Post before post has been updated
* #return N/A
*
*/
function checkmetadata_after( $meta_id, $post_id )
{
//Because updated_post_meta is used, now we can grab the actual updated values
//Remove edit_last and edit_lock because they could differ without data being changed
$this->metadata_after = get_post_meta( $post_id );
unset ( $this->metadata_after['_edit_last']);
unset ( $this->metadata_after['_edit_lock']);
//Make one-level index arrays of metadata
//so array_diff works correctly down below
$arr_mdb = $this->onelevel_array( $this->metadata_before );
$arr_mda = $this->onelevel_array( $this->metadata_after );
//Compare array with metapost values before and after
//If not empty, save file that tells plugin that data has been changed
$ard_metadata = array_diff ( $arr_mdb, $arr_mda );
if (!empty ( $ard_metadata))
{
$this->datahaschanged_save();
}
return;
}
Related
I'm trying to override the number of Items Per Page that a user sets in Screen Options for a specific post type (WP Admin only, not the front end). In this case and for a specific post type, I'd like it to list all posts (regardless of user-set limit) when you click on the post type in WP Admin (edit.php).
I've searched and tried several filter and action hooks but nothing has worked. I'm confident that it is possible.
WordPress 4.9.7
PHP 7.1.17
Multisite
Thank you,
Daniel
EDIT: I forgot to mention - I'm trying to do this from a plugin.
WordPress tries to derive this from the wp_usermeta table if it exists, otherwise it just defaults to 20. You can filter the metavalue and provide a different default - the key may change if on a multisite so you should check the usermeta table if need be.
/**
* Override pagination settings
*
* #param Integer $meta_value
* #param Integer $user_id
* #param String $meta_key
*
* #return Integer $meta_value
*/
function so51343268( $meta_value, $user_id, $meta_key ) {
/**
* The specific meta key is a combination of the post type and keywords.
* For 'post' it would be: `edit_post_per_page`
* For 'category' it would be: `edit_category_per_page`
*/
if( false !== strpos( $meta_key, '_per_page' ) ) {
/* Object Type Slug => Pagination Override */
$unpaginated_types = array(
'post' => 999,
'category' => 4,
);
/* Remove prefix and postfix to get the object type */
$type = rtrim( ltrim( $meta_key, 'edit_' ), '_per_page' );
if( isset( $unpaginated_types[ $type ] ) ) {
$meta_value = $unpaginated_types[ $type ];
}
}
return $meta_value;
}
add_filter( 'get_user_metadata', 'so51343268', 10, 3 );
I am trying to add custom validation message for any field, but its show only default error message.
Here's a snippet that should fix your issue. I just ran into this myself and didn't understand why it wasn't working. Just drop it into your theme functions.php file.
<?php
add_filter( 'gform_field_validation', 'mytheme_fix_custom_validation', 10, 4 );
/**
* Fixes Gravity Forms Custom validation message.
*
* #param array $result The result array.
* #param string $value The value of the field.
* #param array $form The Gravity Form array.
* #param object $field The form field object.
*
* #return array The result array.
*/
function mytheme_fix_custom_validation( $result, $value, $form, $field ) {
if ( ! $result['is_valid'] && ! empty( $field->errorMessage ) ) {
$result['message'] = $field->errorMessage;
}
return $result;
}
Here's a gist in case it ever changes:
https://gist.github.com/solepixel/c8ab2ff61ed55bffa52b5b2a21663c0f
It uses the filter documented here:
https://docs.gravityforms.com/gform_field_validation/
I'm building a wordpress ecommerce site with the woocommerce plugin, it turns that when an user gets logged in and add products to his cart, but the user don't want to proceed to the checkout process, the user prefers to logout and continue with the checkout process later... when the user comes back and gets logged in again the cart is empty.
What is going on here?.
Is this a normal behavior of woocommerce?.
Do I have to do something else? maybe a plugin?
Thanks.
I thought that the cart is emptied when a user logs out, and I finally tracked it down.
On wp_logout() WordPress runs wp_clear_auth_cookie() function. wp_clear_auth_cookie() triggers the do_action( 'clear_auth_cookie' ); action hook.
WooCommerce's Session handler class then runs it's destroy method on this hook.
add_action( 'clear_auth_cookie', array( $this, 'destroy_session' ) );
The destroy_session() method then calls the wc_empty_cart() function, which is a wrapper for the cart class's empty_cart() method.
WC()->cart->empty_cart( false );
But the key thing here is that the parameter is false. Because when we finally track down the empty_cart() method we see that the default is true.
/**
* Empties the cart and optionally the persistent cart too.
*
* #access public
* #param bool $clear_persistent_cart (default: true)
* #return void
*/
public function empty_cart( $clear_persistent_cart = true ) {
$this->cart_contents = array();
$this->reset();
unset( WC()->session->order_awaiting_payment, WC()->session->applied_coupons, WC()->session->coupon_discount_amounts, WC()->session->cart );
if ( $clear_persistent_cart && get_current_user_id() ) {
$this->persistent_cart_destroy();
}
do_action( 'woocommerce_cart_emptied' );
}
When passing true the persistant_cart_destroy() method is called and it is this method that deletes the meta data where the user's cart is kept.
/**
* Delete the persistent cart permanently.
*
* #access public
* #return void
*/
public function persistent_cart_destroy() {
delete_user_meta( get_current_user_id(), '_woocommerce_persistent_cart' );
}
SO, all of that is to say that I do not think the cart should be emptied when a user logs out and then back in. A little more evidence is that WooCommerce attempts to load the persistent cart as soon as a user logs back in.
/**
* Load the cart upon login
*
* #param mixed $user_login
* #param integer $user
* #return void
*/
function wc_load_persistent_cart( $user_login, $user = 0 ) {
if ( ! $user )
return;
$saved_cart = get_user_meta( $user->ID, '_woocommerce_persistent_cart', true );
if ( $saved_cart )
if ( empty( WC()->session->cart ) || ! is_array( WC()->session->cart ) || sizeof( WC()->session->cart ) == 0 )
WC()->session->cart = $saved_cart['cart'];
}
add_action( 'wp_login', 'wc_load_persistent_cart', 1, 2 );
I would try disabling all other plugins to see if the behavior reverts back to what I think is the normal behavior. From there, you can re-enable them one at a time to isolate the culprit.
I faced same problem and solved it by placing following function to my functions.php file:
/**
* Load the persistent cart if exists
*
* #return void
*/
function adian_load_persistent_cart(){
global $current_user;
if( ! $current_user )
return false;
$saved_cart = get_user_meta( $current_user->ID, '_woocommerce_persistent_cart', true );
if ( $saved_cart ){
if ( empty( WC()->session->cart ) || ! is_array( WC()->session->cart ) || sizeof( WC()->session->cart ) == 0 ){
WC()->session->set('cart', $saved_cart['cart'] );
}
}
}
add_action( 'init', 'adian_load_persistent_cart', 10, 1 );
Function checks if the user has persistent cart saved and replaces session cart with it.
There are two approaches for limiting revisions in WordPress; WP_POST_REVISIONS and wp_revisions_to_keep(). Which will have higher priority ? I could not understand even after reading codex.
WP_POST_REVISIONS is constant, not a function. if you have set WP_POST_REVISIONS like this: define( 'WP_POST_REVISIONS', 3 ); then you'll get 3 when you'll call the function wp_revisions_to_keep (provided that there is no filter who is modifying the value). If WP_POST_REVISIONS is not set then it will store every revision and you'll get -1 when you'll call wp_revisions_to_keep. This is the source code of wp_revisions_to_keep.
/**
* Determine how many revisions to retain for a given post.
* By default, an infinite number of revisions are stored if a post type supports revisions.
*
* #since 3.6.0
*
* #uses post_type_supports()
* #uses apply_filters() Calls 'wp_revisions_to_keep' hook on the number of revisions.
*
* #param object $post The post object.
* #return int The number of revisions to keep.
*/
function wp_revisions_to_keep( $post ) {
$num = WP_POST_REVISIONS;
if ( true === $num )
$num = -1;
else
$num = intval( $num );
if ( ! post_type_supports( $post->post_type, 'revisions' ) )
$num = 0;
return (int) apply_filters( 'wp_revisions_to_keep', $num, $post );
}
From this, it is clear that function wp_revisions_to_keep() uses WP_POST_REVISIONS inside it. However, to be 100% sure that your suggested number of revisions work then you should hook a function to wp_revisions_to_keep. Something like this:-
add_filter('wp_revisions_to_keep', 'custom_revisions_number', 10, 2);
function custom_revisions_number($num, $post) {
$num = 5; // <---- change this accordingly.
return $num;
}
Highest priority is of the hook that will execute last.
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;
}