Hook add_action per post completamente nuovi?
publish_post viene eseguito quando un post viene pubblicato, o se viene modificato e il suo stato è "pubblicato". Argomenti della funzione action: ID del post.
Ho aggiunto l'hook publish_post a un plugin WordPress che sto scrivendo. La funzione chiamata dall'hook dovrebbe modificare le categorie di diversi post utilizzando la funzione wp_update_post.
Tuttavia questo hook non funziona poiché il risultato restituito dall'esecuzione di wp_update_post è sempre 0. La mia ipotesi è che l'esecuzione di wp_update_post causi un'altra istanza del mio hook perché ripubblica il post... il che credo porti alla condizione "...o se viene modificato e il suo stato è 'pubblicato'" della dichiarazione sopra.
Esiste un altro action-hook che posso utilizzare che verrà chiamato solo quando il post aggiunto è completamente nuovo e non modificato?
<?php
/*
Plugin Name: Plugin Cambio Categorie
Plugin URI: http://www.example.com
Description: Quando viene creato un nuovo post questo plugin causerà il
Version: 0.1
Author: Me
License: GPL2
?>
<?php
class categoryShifter {
function shiftCategories($post_ID) {
$maxNumPostsFirstTeir = 4;
$first_teir_cat = "Notizie Fresche 1";
$second_teir_cat = "Notizie Datate 2";
$firephp = FirePHP::getInstance(true);
$firephp->info('INIZIO: categoryShifter.shiftCategories()');
$firephp->log($post_ID, 'post_ID: ');
$firephp->trace('traccia fino a qui');
$first_teir_id = categoryShifter::getIDForCategory($first_teir_cat, $firephp);
$second_teir_id = categoryShifter::getIDForCategory($second_teir_cat, $firephp);
$firephp->log($first_teir_id, '$first_teir_id');
$firephp->log($second_teir_id, '$second_teir_id');
$qPostArgs = array(
'numberposts' => 100,
'order' => 'DESC',
'orderby' => 'post_date',
'post_type' => 'post',
'post_status' => 'published',
'category_name' => $first_teir_cat
);
$firstTeirPosts = get_posts($qPostArgs);
$firephp->log($firstTeirPosts, 'post ottenuti:');
$firephp->log(sizeof($firstTeirPosts), 'dimensione');
// NOTA: Questo sembra funzionare.
for($i = sizeof($firstTeirPosts)-1; $i > $maxNumPostsFirstTeir-4; $i--)
{
$newCats = array($second_teir_id);
$editingId = $firstTeirPosts->ID;
$result = wp_set_post_categories($editingId, $newCats); /* NOTA: Non funziona attualmente... restituisce un array con $second_teir_id al suo interno. */
$firephp->log($result, 'Risultato');
}
/*
$my_post = array();
$my_post['ID'] = 132;
$my_post['post_category'] = array($second_teir_id);
$firephp->log('Prima', 'Prima');
if(wp_update_post( $my_post ) == 0) {
$firephp->Error('Errore Fatale, Post non aggiornato', 'errore');
}
$firephp->log('Dopo', 'Dopo');
*/
return $post_ID;
}
function getIDForCategory($cat_name, $logger) {
$logger->Info("Inizio: getIDForCategory()");
$cats = get_categories();
$whichCatId = "";
foreach($cats as $single_cat) {
if($single_cat->name == $cat_name) {
$whichCatId = $single_cat->term_id;
break;
}
}
$logger->Info("Fine: getIDForCategory()");
return (int)$whichCatId;
}
}
/* Hook Creazione Post */
/* add_action('publish_post', array('categoryShifter','shiftCategories')); */
add_action('wp_insert_post', array('categoryShifter', 'shiftCategories'));
?>
Per il momento sono passato all'utilizzo dell'hook wp_insert_post... ma ancora non riesco a far funzionare la funzione wp_set_post_categories per modificare le categorie dei post.
Capisco che probabilmente dovrò aggiornare questo codice in modo che tenga conto delle categorie esistenti del post e modifichi solo quelle specificate dal plugin, ma per ora è davvero solo una versione alfa.

// Azioni di WordPress che attivano 'your_function' quando uno stato post cambia in "pubblicato"
add_action('new_to_publish', 'your_function'); // Da nuovo a pubblicato
add_action('draft_to_publish', 'your_function'); // Da bozza a pubblicato
add_action('pending_to_publish', 'your_function'); // Da in attesa a pubblicato

Che ne dici dell'azione wp_insert_post
? http://core.trac.wordpress.org/browser/tags/3.3.1/wp-includes/post.php#L2656

Ma queste verranno eseguite di nuovo se, ad esempio, lo stato viene cambiato da pubblicato a bozza e poi nuovamente a pubblicato?

@soulseekah L'hook wp_insert_post
verrà chiamato nuovamente quando modifichi un post esistente e lo salvi. Testato su WordPress 5.9. So che il tuo commento è del 2012 ma nel caso qualcuno fosse curioso a riguardo.

Ho letto approfonditamente il core di WordPress e ho provato tutto. wp_transition_post_status()
, new_to_publish()
, new_{post_type}()
, wp_insert_post()
.
Tutti questi, alla fine, sono inaffidabili.
wp_transition_post_status()
non è affidabile perché il nuovo stato "publish" è lo stato predefinito sia per la creazione di nuovi post che per l'aggiornamento di quelli esistenti. Lo stato precedente non è affidabile per definire cosa è un nuovo post e cosa non lo è perché può essere draft, auto-draft, publish, ecc.
new_to_publish()
non funziona per i custom post type.
new_{post_type}
passa solo $post come parametro, e non puoi sapere se è nuovo o un aggiornamento di uno esistente
wp_insert_post()
ha un parametro $update che dovrebbe essere TRUE se si stanno aggiornando post esistenti e FALSE se si stanno creando nuovi post, ma è inaffidabile perché restituisce TRUE per i nuovi post a causa di auto-draft.
Soluzione: Utilizzo di un post meta
Alla fine ho utilizzato un post meta personalizzato che ho chiamato check_if_run_once
che eseguirà una certa logica solo una volta:
/**
* Fai qualcosa quando viene creato un nuovo libro
*/
function new_book($post_id, $post, $update) {
if ( $post->post_type == 'book' && $post->post_status == 'publish' && empty(get_post_meta($post_id, 'check_if_run_once')) ) {
# Nuovo Post
# Fai qualcosa qui...
# E aggiorna il meta così non verrà eseguito di nuovo
update_post_meta( $post_id, 'check_if_run_once', true );
}
}
add_action( 'wp_insert_post', 'new_book', 10, 3 );
Opzionalmente, se hai bisogno di aggiornare i tuoi post esistenti con il meta "check_if_run_once", in modo che non esegua il codice sopra per i post esistenti creati prima di aver aggiunto quella funzione, potresti fare:
/**
* Questa è una funzione temporanea per aggiornare i post esistenti con il post meta "check_if_run_once"
* Accedi al tuo sito loggato come admin e aggiungi ?debug all'URL.
*/
function temporary_function() {
if (current_user_can('manage_options')) {
$posts = get_posts(array(
'post_type' => 'book',
'posts_per_page' => -1, // Potresti dover paginare questo in base a quanti post hai
'fields' => 'ids'
));
foreach($posts as $post_id) {
update_post_meta($post_id, 'check_if_run_once', true);
}
}
}
if (isset($_GET['debug'])) {
add_action('init', 'temporary_function');
}

Precisamente individuare la creazione di un nuovo articolo è in realtà più complicato di quanto sembri. Tecnicamente ci sono diversi modi in cui un articolo può essere creato o aggiornato e ci sono molte cose non così ovvie che tecnicamente sono anche articoli (le revisioni, ad esempio).
WordPress fornisce hook dinamici che tracciano non solo la creazione dell'articolo, ma anche cosa era e cosa è diventato. Vedi Transizioni di Stato degli Articoli nel Codex.

Più che seguire la documentazione, ho sperimentato e questo funziona per me (WP 3.3). Ricevo una chiamata all'hook transition_post_status con $new_status impostato su "auto-draft" quando creo un nuovo articolo.
function my_post_new($new_status, $old_status=null, $post=null){
if ($new_status == "auto-draft"){
// fai qualcosa qui
}
}
add_action('transition_post_status', 'my_post_new');

Questo verrà eseguito ogni volta che viene salvato un auto-draft, cioè più o meno ogni minuto durante la modifica.

Ottimo punto. Nel mio caso non è un problema, ma potrebbe esserlo per altri, a seconda di ciò che si sta facendo.

volevo votare positivamente, ma non posso ancora. quindi i complimenti vanno qui, @PapaFreud. tra l'altro, ora è documentato su http://codex.wordpress.org/Post_Status_Transitions. ancora grazie, amico

@soulseekah Punto interessante che hai sollevato, ho testato questo, e auto-draft
viene assegnato come $new_status
di un articolo al momento della sua creazione (cioè Aggiungi nuovo), una sola volta. Non appena scatta la prima procedura di AUTOSAVE
dopo 1 minuto, il valore di $new_status
viene aggiornato a draft
. Lo $old_status
rimane NULL
finché l'articolo non viene salvato manualmente o pubblicato. Quindi tecnicamente questo funzionerebbe e non verrebbe attivato ogni volta che l'editor salva automaticamente il tuo lavoro. Come misura aggiuntiva potresti verificare che lo $old_status
sia NULL
per confermare -> if ($new_status == "auto-draft" && $old_status === NULL)

Ho scoperto che l'opzione migliore è verificare lo stato del post quando viene chiamato wp_insert_post
.
Quando un nuovo post viene creato, lo stato iniziale è auto-draft
.
if (!function_exists('benyonsFunction')) {
function benyonsFunction($postId, $post) {
if ($post->post_status == "auto-draft") {
error_log('NUOVO POST!!!');
}
}
}
add_action('wp_insert_post', 'benyonsFunction', 10, 2);
Questo error_log verrà attivato solo una volta.

Il modo più pulito e affidabile per gestire questo per un tipo di post conosciuto è utilizzare l'hook {$new_status}_{$post->post_type}
che fornisce lo stato precedente e confrontarlo con "publish".
Ad esempio, se vuoi attivare un'azione quando una nuova "pagina" viene creata, useresti:
add_action( 'publish_page', function( int $post_id, \WP_Post $post, string $old_status ) {
if ( 'publish' === $old_status ) {
// Niente da fare qui.
return;
}
// La logica di business va qui.
}, 10, 3 );

No, questo verrà attivato ogni volta che una pagina passa da qualsiasi stato a 'publish', quindi si attiverà molte più volte rispetto alla sola prima creazione.
