WordPress Saving translation post when creating new post - php

I wan't to achieve following:
I have to save a duplicate of a newly created post if a user creates a new post (cpt). Afterwards I will set this new post with Polylang to another language as translation for the post created in step one.
Hook into following actions should lead to the desired result:
add_action('new_to_publish', 'duplicate_to_english');
add_action('draft_to_publish', 'duplicate_to_english');
add_action('pending_to_publish', 'duplicate_to_english');
function duplicate_to_english($post)
{
$en_post = pll_get_post($post->ID, 'en');
if(empty($en_post)) {
$new_post = (array) $post;
unset($new_post['ID']);
// INFINITE LOOP
$en_id = wp_insert_post($new_post);
pll_set_post_language($en_id, 'en');
}
}
But unfortunately this will result in an infinite loop (as expected). Now I'm looking for a possibility to avoid this loop. My first idea was to set a $_POST variable and only execute duplication if this variable is set. But I have no idea how to identify a new post. I discovered that WordPress immediately saves an auto-draft on clicking the 'New Post' button so looking for post ID = 0 doesn't work.
Any other approach is highly welcome.

For everyone who needs a solution:
I use the method called to save the meta data to accomplish duplicating my post:
add_action ( 'save_post', 'save_meta', 1, 3 ); // save the custom fields
function save_meta($post_id, $post, $update)
{
// here we check for the transient set in duplicate_post
// if existing delete it end return because we are saving only
// the duplicate. This will avoid infinite loop while saving the
// new translated post
$english = get_transient('saving_english');
if($english) {
delete_transient('saving_english');
return $post->ID;
}
// In case of auto saving draft we can return and don't duplicate the post
if (!wp_verify_nonce($_POST['nonce'], 'nonce')) {
return $post->ID;
}
if (! current_user_can ( 'edit_post', $post->ID )) {
return $post->ID;
}
// collect meta data from $_POST
$item_meta = $_POST['post_meta'];
// Updating meta data for the original post
loop_through_meta($item_meta, $post);
// looking for translated version
$en_id = pll_get_post($post->ID, 'en');
// If the translated post is missing, set transient,
// duplicate the post and category and afterwards write
// the taxonomy entry for polylang
if (empty($en_id)) {
set_transient('saving_english', true);
if ($en_id = duplicate_post($post, $item_meta['title_english'])) {
pll_save_post_translations([
'de' => $post->ID,
'en' => $en_id
]);
}
// If the translated posts already exists deregister the hook
// to avoid infinite loop.
// But note the third parameter priority: It must be the same
// priority as used for registering the hook
} else {
remove_action('save_post', 'save__meta',1);
wp_update_post([
'ID' => $en_id,
'post_title' => $item_meta['title_english']
]);
add_action ( 'save_post', 'save_meta', 1, 3 );
}
// If we have an id save new meta to the translated post
if(!empty($en_id)) {
loopt_through_meta($item_meta, get_post($en_id));
}
}
function duplicate_post ($post, $title)
{
$new_post = (array) $post;
unset($new_post['ID']);
$new_post['post_title'] = $title;
$new_id = wp_insert_post($new_post);
// Here we only need to set the custom category but not
// the Polylang taxonomy.
$taxonomies = get_object_taxonomies($post->post_type);
foreach ($taxonomies as $taxonomy) {
if($taxonomy != 'custom_categories') continue;
$post_terms = wp_get_object_terms($post->ID, $taxonomy, array('fields' => 'slugs'));
wp_set_object_terms($new_id, $post_terms, $taxonomy, false);
}
pll_set_post_language($new_id, 'en');
return $new_id;
}
That's it. Now everytime you create a new post or update an existing post a translated duplicate will be created or updated.

Related

Display or hide custom tab on product page depending on variable

I am trying to hide a display or not display a tab on the single product page depending on the value of $sizeGridNumber. So if this value is 1 or empty, I want the tab not to display. I return an empty array, but it generates errors.
/*Customise additional-information tab (Size Grid)*/
add_filter( 'woocommerce_product_tabs', 'wpb_custom_additional_information_tab', 98 );
function wpb_custom_additional_information_tab( $tabs ) {
global $post;
// var_dump($post->ID);
$sizeGridNumber = get_post_meta( $post->ID, '_selectsizeguide', true );
$sku = get_post_meta( $post->ID, '_sku', true );
// echo '<br/>';
// var_dump($sizeGridNumber);
// print_r($sizeGridNumber);
// var_dump($sku);
// print_r($sku);
if ( $sizeGridNumber == 1 || $sizeGridNumber == "") {
echo "on passe";
return array();
} else {
$tabs['additional_information']['callback'] = 'wpb_custom_additional_information_tab_content'; // Custom additional_information callback
return $tabs;
}
}
instead of
return array();
I did
unset( $tabs['additional_information'] );
return $tabs;
You should not return the empty array like doing in the in Actions,
Filters are called before the execution and reference variable passed to your function, so if you return the empty value then whole hook chain breaks So you should only do the necessary changes to that var and return it back.
Derived from #Louis Answer
Instead of returning
return array();
Make the needed the changes in the $tabs var and return it back.

Which WordPress hook fires after save all post data and post meta?

I have a custom post type crm, and i need to send a mail after each crm saved or updated. i user cmb2 for some custom meta like subject, to users etc. I know the save_post hook fires after post save (according to WordPress codex) in my case when i call save_post with two parameters (id and post) the post does not contains update values. here is my code :
function send_mail_to_user($id, $post){
$crm = $post;
$user_email = array();
if($crm->vc_all_vc == 'on'){
$args = array('orderby' => 'display_name');
$wp_user_query = new WP_User_Query($args);
$authors = $wp_user_query->get_results();
if (!empty($authors)) {
foreach ($authors as $author) {
array_push($user_email , $author->user_email );
}
}
}
else{
$to_users = $crm->vc_users;
$to_program = $crm->vc_program;
$to_group = $crm->vc_group;
$to_excode = $crm->vc_ex_code;
foreach ($to_users as $key => $value) {
$user_data = get_userdata($value);
array_push($user_email, $user_data->user_email);
}
foreach ($to_program as $key => $value) {
$users = get_users( array('meta_key' => 'programs' ) );
if($users){
foreach ($users as $index => $data) {
if(in_array($value , explode('#', $data->programs))){
if(! in_array($data->user_email, $user_email) )
{
array_push($user_email, $data->user_email);
}
}
}
}
}
foreach($to_group as $group) {
$term = get_term_by('slug', esc_attr($group), 'user-group');
$user_ids = get_objects_in_term($term->term_id, 'user-group');
foreach($user_ids as $user_id){
$fc_user = get_userdata($user_id);
if(! in_array($fc_user->user_email, $user_email) )
{
array_push($user_email, $fc_user->user_email);
}
}
}
foreach($to_excode as $codes) {
$value = explode('*',$codes)[1];
$users = get_users( array('meta_key' => 'programs' ) );
if($users){
foreach ($users as $index => $data) {
if(in_array($value , explode('#', $data->programs))){
if(! in_array($data->user_email, $user_email) )
{
array_push($user_email, $data->user_email);
}
}
}
}
}
}
foreach($user_email as $index => $email){
$to = $email;
$subject = $crm->vc_subject;
$body = $crm->post_content;
$headers = array(
'Content-Type: text/html; charset=UTF-8'
);
wp_mail($to, $subject, $body, $headers);
}
}
add_action( 'save_post', 'send_mail_to_user', 10, 2 );
And i also try publish_post hook , that works fine when new post created but when updated it works same. I have tried edit_post and post_updated hook also, but i never be able to retrieve my update data.
So how can i solve it? which action hook will give me all the new data?
thanks in advance.
You can use something like this,
function your_custom_function($meta_id, $post_id, $meta_key='', $meta_value='') {
if($meta_key=='_edit_lock') {
// if post meta is updated
}
}
add_action('updated_post_meta', 'your_custom_function', 10, 4);
Try with post_updated and use $post_after object.
https://codex.wordpress.org/Plugin_API/Action_Reference/post_updated
you can use this save_post hook with your function. change your hook priority to 100 it will give you updated post
add_action( 'save_post', 'send_mail_to_user', 100, 2 );
This might be a bit old but just wanted to give an update since from version 5.6.0 a new hook is available. The hook is wp_after_insert_post and you can find more information here . This hook is triggered after a post is created or updated and all of its terms and meta are updated. You can find an example below:
add_action( 'wp_after_insert_post', 'send_mail_to_user', 90, 4 );
/**
* Callback to: 'wp_after_insert_post'
* Fires once a post, its terms and meta data has been saved
* #param int $post_id Post ID.
* #param WP_Post $post Post object.
* #param bool $update Whether this is an existing post being updated.
* #param null|WP_Post $post_before Null for new posts, the WP_Post object prior to the update for updated posts.
*
*/
public static function sync_product_registrations_on_update( $post_id, $post, $update, $post_before ) {
if ( 'post' !== $post->post_type ) {
//Only to process the below when post type is 'post' else return
return;
}
if ( ! in_array( $post->post_status, [ 'private', 'publish' ] ) ) {
//To only process when status is private or publish
return;
}
//The rest of your code goes here
}
You can use the rest_after_insert_{$this->post_type} hook (where $this->post_type is replaced with the post type, eg 'post' or 'myposttype').
Thanks to Florian Brinkmann for this link.
add_action('rest_after_insert_myposttype', 'myfunction', 10, 3);
function myfunction($post, $request, $creating) {
// $creating is TRUE if the post is created for the first time,
// false if it's an update
// ...
}
See also here.
Some workaround is to use $_POST['meta_field'] with sanitation:
$to_users = $_POST['vc_users'];
$to_program = $_POST['vc_program'];
$to_group = $_POST['vc_group'];
$to_excode = $_POST['vc_ex_code'];
$to_users = sanitize_text_field($to_users);
$to_program = sanitize_text_field($to_program);
$to_group = sanitize_text_field($to_group);
$to_excode = sanitize_text_field($to_excode);
Pay attention to the field names, using ACF will make you use the field key.
This problem is more complicated than seems on first sight:
Our 'post_updated' hook is running before post is updated, so every attempt for getting meta data will result with the previous data. Also, Wordpress (v5.7.2) doesn't seem to have a hook for after a post was saved.
Also, 'save_post' hook is very complicated because it runs for every post saved or created including revisions and the $update boolean is still not reliable enough.
The correct and simpler answer is to use the wp_insert_post action.
https://developer.wordpress.org/reference/hooks/wp_insert_post/
An important distinction of wp_insert_post action is that it is fired
after update_post_meta has been called.
There are 3 parameters available - the $update flag tells you if this is a new or updated post.
do_action( 'wp_insert_post', int $post_ID, WP_Post $post, bool $update )
So - to implement your code after all post meta has been updated, use something like this:
add_action('wp_insert_post', 'run_after_post_updated', 10, 3);
function run_after_post_updated($post_ID, $post, $update ) {
// ...
}
You can use the save_post action wih a higher priority so that your function is called afer all meta data has been saved.
add_action( 'save_post', 'action_woocommerce_update_product', 20, 3 );
Here I have used higher priority 20

Contact form 7 to custom post type

I would like to process a contact form from contact form 7 into a custom post type.
Currently, this is what I have:
<?php
if ( 'POST' == $_SERVER['REQUEST_METHOD'] && !empty( $_POST['action'] ) && $_POST['action'] == "front_post") {
//store our post vars into variables for later use
//now would be a good time to run some basic error checking/validation
//to ensure that data for these values have been set
$title = $_POST['title'];
$content = $_POST['content'];
$Interest = $_POST['Interest'];
$post_type = 'purchase';
//the array of arguements to be inserted with wp_insert_post
$new_post = array(
'post_title' => $title,
'post_content' => $content,
'tags_input' => $tags,
'posted_data' => $Interest,
'post_status' => 'publish',
'post_category' => array('0',$_POST['cat']),
'post_type' => $post_type
);
//insert the the post into database by passing $new_post to wp_insert_post
//store our post ID in a variable $pid
//we now use $pid (post id) to help add out post meta data
$pid=wp_insert_post($new_post);
//we now use $pid (post id) to help add out post meta data
add_post_meta($pid, 'cust_key', $custom_field);
}
?>
Here is a link to the actual form: http://stage.icardpromotions.com/create-purchase-order/
I need to be able to pull in all of the info form this form into the custom post type "purchase"
As you can see, I am currently pulling in the post_content, post_title, etc.
I have also tried to pull in content from content form by input name "Interest" but it dose not work.
Does anyone have a clue how to do this?
function save_posted_data( $posted_data ) {
$args = array(
'post_type' => 'post',
'post_status'=>'draft',
'post_title'=>$posted_data['your-name'],
'post_content'=>$posted_data['your-message'],
);
$post_id = wp_insert_post($args);
if(!is_wp_error($post_id)){
if( isset($posted_data['your-name']) ){
update_post_meta($post_id, 'your-name', $posted_data['your-name']);
}
// if( isset($posted_data['your-email']) ){
// update_post_meta($post_id, 'your-email', $posted_data['your-email']);
// }
// if( isset($posted_data['your-subject']) ){
// update_post_meta($post_id, 'your-subject', $posted_data['your-subject']);
// }
if( isset($posted_data['your-message']) ){
update_post_meta($post_id, 'your-message', $posted_data['your-message']);
}
//and so on ...
return $posted_data;
}
}
add_filter( 'wpcf7_posted_data', 'save_posted_data' );
-------------------- Explaining It-------------------------
First make function and add a hook wpcf7_posted_data to it
---first step---
function save_posted_data( $posted_data ) {
}
add_filter( 'wpcf7_posted_data', 'save_posted_data' );
---second step---
and now u need to add some arguments to the post that needs to be populated using wp_insert_post();
$args = array(
'post_type' => 'post',
'post_status'=>'draft',
'post_title'=>$posted_data['your-name'],
'post_content'=>$posted_data['your-message'],
);
$post_id = wp_insert_post($args);
---third step---
check if that populated items is error or not
if(!is_wp_error($post_id)){ //do ur stuffs }
---fourth step---
now checking isset the field or not and updating the metas
eg post
if( isset($posted_data['your-name']) ){
update_post_meta($post_id, 'your-name', $posted_data['your-name']);
}
and in last return the value
return $posted_data;
Full code is above.
here is a quick tip as to how to go about achieving the above using your own code, first register your custom post
add_action('init', 'my_custom_post');
function (){
$args = array(
/*post type registration parameters*/
);
register_post_type( 'my_custom_post', $args );
}
next, you want to capture your posted data and create a new post
add_filter( 'wpcf7_posted_data', 'save_posted_data' );
function save_posted_data( $posted_data ) {
$args = array(
'post_type' => 'my_custom_post',
/*other default parameters you want to set*/
);
$post_id = wp_insert_post($args);
if(!is_wp_error($post_id)){
if( isset($posted_data['form-field-name']) ){
update_post_meta($post_id, 'form-field-name', $posted_data['form-field-name']);
}
//and so on ...
return $posted_data;
}
While the answer with the most upvotes in this thread works it has some flaws.
First of which is: you can still submit the form and thus create a post in wordpress if for instance you remove "disabled" tag from your submit button. So you can essentially bypass the validation.
Second problem that I had, was probably specific to my usecase, since this function triggers on any cf7 form submission on website. If you have more than one cf7 form this will create posts even if users submit something in some totally different form.
To solve the first problem I think the best way is to hook custom function to "wpcf7_before_send_mail" instead of "wpcf7_posted_data"
And to solve the second problem is to check for id of the form you wish to trigger the effect on.
This is how I solved these problems:
function save_cf7_data_to_cpt( $contact_form ) {
if( $contact_form->id() !== $my_form_id )
return; //dont run the rest if it is not the form you want it to be, $my_form_id we look up in admin or shortcode...
$submission = WPCF7_Submission::get_instance();
if ( $submission ) {
$posted_data = $submission->get_posted_data();
} //we get to $post_data in this way since it is not provided like in the wpcf7_posted_data approach
$args = array(
'post_type' => 'testemonial',
'post_status'=>'draft',
'post_title'=>$posted_data['your-name'],
'post_content'=>$posted_data['your-message'],
);
$post_id = wp_insert_post($args);
if(!is_wp_error($post_id)){
if( isset($posted_data['your-name']) ){
update_post_meta($post_id, 'your-name', $posted_data['your-name']);
}
if( isset($posted_data['your-message']) ){
update_post_meta($post_id, 'your-message', $posted_data['your-message']);
}
//and so on ...
return $posted_data;
}
}
add_filter( 'wpcf7_before_send_mail', 'save_cf7_data_to_cpt' ); //hook into wpcf7_before_send_mail to ensure validation is ok
Can u also use
add_action('wpcf7_mail_sent','save_my_form_data_to_my_cpt');
add_action('wpcf7_mail_failed','save_my_form_data_to_my_cpt');
function save_my_form_data_to_my_cpt($contact_form){
$submission = WPCF7_Submission::get_instance();
if (!$submission){
return;
}
$posted_data = $submission->get_posted_data();
//The Sent Fields are now in an array
//Let's say you got 4 Fields in your Contact Form
//my-email, my-name, my-subject and my-message
//you can now access them with $posted_data['my-email']
//Do whatever you want like:
$new_post = array();
if(isset($posted_data['your-name']) && !empty($posted_data['your-name'])){
$new_post['post_title'] = $posted_data['your-name'];
} else {
$new_post['post_title'] = 'Message';
}
$new_post['post_type'] = 'inquiry'; //insert here your CPT
if(isset($posted_data['tel-901'])){
$new_post['post_content'] = $posted_data['tel-901'];
} else {
$new_post['post_content'] = 'No Message was submitted';
}
$new_post['post_status'] = 'publish';
//you can also build your post_content from all of the fields of the form, or you can save them into some meta fields
if(isset($posted_data['your-email']) && !empty($posted_data['your-email'])){
$new_post['meta_input']['sender_email_address'] = $posted_data['your-email'];
}
if(isset($posted_data['checkbox-674']) && !empty($posted_data['checkbox-674'])){
//$new_post['meta_input']['sender_name'] = $posted_data['checkbox-674'];
$ChildSeat=$posted_data['checkbox-674'];
$Child_Seat='';
for($a=0;$a<count($ChildSeat);$a++)
{
$data['checkbox-674']=$_POST['checkbox-674'][$a];
$Child_Seat.=$data['checkbox-674'].'<br>';
$new_post['post_content'] = $Child_Seat;
}
}
//When everything is prepared, insert the post into your Wordpress Database
if($post_id = wp_insert_post($new_post)){
//Everything worked, you can stop here or do whatever
} else {
//The post was not inserted correctly, do something (or don't ;) )
}
return;
}
there is a plugin to do this, Post My CF7 Form.
The plugin allows you to map a CF7 form and its fields to a an existing post type or a new custom post type.
The mapping process is done using an interactive UI admin page which gives you the option to map your fields to post fields (title, content, excerpt, slug, author) as well as post meta-fields.
In addition, the plugin also introduces a save submit button to allow users to save a draft version of the form, this is especially useful for large complex forms.
The plugin can automatically load taxonomy terms in select/checkbox/radio fields that have been mapped to that taxonomy, thus enabling created posts to be automatically assigned to users selected terms.
The plugin has multiple hooks & filters to customise the process flow.

Default featured image for 'POST' type only (exclude 'page')

Hello I am trying to have a default featured image set for post_type = 'post' only excluding post_type = 'page'.
I wrote the following code in the functions file of the child theme but i keep getting this error:
Notice: Trying to get property of non-object in
/home/ossettto/public_html/wp-content/themes/sport-child/functions.php
on line 18
function wpforce_featured()
{
global $post;
$post_type = get_post_type($post->ID);
if ($post_type === 'post')
{
$already_has_thumb = has_post_thumbnail($post->ID); // If post have a featured image use that.
if (!$already_has_thumb)
{
//If post does not have a featured image then get the first post image and set as featured image.
$attached_image = get_children("post_parent=$post->ID&post_type=attachment&post_mime_type=image&numberposts=1"); // Number 1 relates to taking post image number 1 and adding it as a featured image.
if ($attached_image)
{
foreach ($attached_image as $attachment_id => $attachment)
{
set_post_thumbnail($post->ID, $attachment_id);
//$attachment_id = attachment_url_to_postid( $image_url );
//echo $attachment_id;
}
}
else
{
set_post_thumbnail($post->ID, '27264'); // Find attachment media id by right clicking the image in the Media library and selecting inspect element. Look for the data-id number. This number is then added to the post id.
}
}
}
}
//end function
add_action('the_post', 'wpforce_featured');
add_action('save_post', 'wpforce_featured');
add_action('draft_to_publish', 'wpforce_featured');
add_action('new_to_publish', 'wpforce_featured');
add_action('pending_to_publish', 'wpforce_featured');
add_action('future_to_publish', 'wpforce_featured');
Any help will be appreciated.
Thanks.
Two things are unclear:
Why you are to do this in the_post, save_post, and the other hooks all together.
It would be helpful to know which line is line 18, but I'm guessing it's this line: $post_type = get_post_type( $post->ID );.
However, the reason you are getting the notice is that these actions don't necessarily all have the $post object ready for you to global $post. Further, these actions all have different function signatures, passing $post as a parameter in different places.
Given all the filters you are hooking into, you need to create an "abstraction" or "wrapper" around your function so that you can call it properly with $post in the proper position of the arguments.
Look at the docs to see examples of where $post is being passed:
the_post action - passes as ONLY parameter
save_post action - passes as SECOND parameter
draft_to_published (and other hooks) - passes as THIRD parameter
// New function that accepts proper parameters for save action
function wpforce_featured_on_save( $post_id, $post, $update ) {
// No need to duplicate code. Instead, call your original function
// passing it the $post parameter
wpforce_featured_status( $post );
}
// New function that accepts proper parameters for these actions
function wpforce_featured_on_status_change( $new, $old, $post ) {
// No need to duplicate code. Instead, call your original function
// passing it the $post parameter
wpforce_featured( $post );
}
// Your original function with slight modifications
// NOTE: ONLY accepts $post object - no global post
function wpforce_featured( $post ) {
// REMOVED global $post - not helpful here
$post_type = get_post_type( $post->ID );
// ... the rest of your code here
}
// Your original hook, it's passing the $post_object parameter
add_action('the_post', 'wpforce_featured');
// The save hook, which passes parameters - modified to call a different function
add_action('save_post', 'wpforce_featured_save', 10, 3);
// The status change hooks, which pass parameters - modified to call a different function
add_action('draft_to_publish', 'wpforce_featured_on_status_change', 10, 3);
add_action('new_to_publish', 'wpforce_featured_on_status_change', 10, 3);
add_action('pending_to_publish', 'wpforce_featured_on_status_change', 10, 3);
add_action('future_to_publish', 'wpforce_featured_on_status_change', 10, 3);

WordPress - Update permalink automatically after posting an update

I am calculating custom title for a product using WooCommerce add product page. After the user post product's information, title is generated and saved by a save_post filter hook.
add_filter('save_post', 'modify_post_title', '99', 1);
function modify_post_title($post_id)
{
// some logic to form a new $title
// ...
if (!empty($title)) {
// update the title in database
$wpdb->update($wpdb->posts, array('post_title' => $title), array('ID' => $post_id));
// UPDATE PERMALINK
}
}
I need to know what function to use to re-generate the permalink after updating title.
Thanks in advance
add_filter( 'wp_insert_post_data', 'custom_slug_change', 50, 2 );
function custom_slug_change( $data, $postarr ) {
//Check for the post statuses you want to avoid
if ( !in_array( $data['post_status'], array( 'draft', 'pending', 'auto-draft' ) ) ) {
$data['post_name'] = sanitize_title( $data['post_title'] );
}
return $data;
}
Would you please add above code in your functions.php ?
I think you can go with the window.history.pushState to maipulate the Browser History.
I think these may help you.
window.history.pushState("object or string", "Title", surl[0]);

Categories