Gancho add_action para publicaciones completamente nuevas
publish_post se ejecuta cuando se publica una entrada o si se edita y su estado es "publicado". Argumentos de la función de acción: ID de la entrada.
-Documentación de la API del Plugin
He añadido el gancho publish_post a un plugin de WordPress que estoy escribiendo. La función llamada por el gancho está destinada a cambiar las categorías de varias entradas usando la función wp_update_post.
Sin embargo, este gancho no funciona ya que el resultado devuelto al ejecutar wp_update_post siempre es 0. Mi mejor suposición es que ejecutar wp_update_post causa otra instancia de mi gancho al volver a publicar la entrada... lo que creo que provoca el "...o si se edita y su estado es 'publicado'" de la declaración anterior.
¿Hay algún otro gancho de acción que pueda usar que solo se llame cuando la entrada agregada sea completamente nueva y no editada?
<?php
/*
Nombre del Plugin: Plugin Cambiador de Categorías
URI del Plugin: http://www.example.com
Descripción: Cuando se crea una nueva entrada, este plugin causará el
Versión: 0.1
Autor: Yo
Licencia: GPL2
?>
<?php
class categoryShifter {
function shiftCategories($post_ID) {
$maxNumPostsFirstTeir = 4;
$first_teir_cat = "Noticias Frescas 1";
$second_teir_cat = "Historias Ligeramente Antiguas 2";
$firephp = FirePHP::getInstance(true);
$firephp->info('INICIO: categoryShifter.shiftCategories()');
$firephp->log($post_ID, 'post_ID: ');
$firephp->trace('rastreo hasta aquí');
$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, 'entradas obtenidas:');
$firephp->log(sizeof($firstTeirPosts), 'tamaño');
// NOTA: Esto parece funcionar.
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: No funciona actualmente... devuelve un array con el $second_teir_id en él. */
$firephp->log($result, 'Resultado');
}
/*
$my_post = array();
$my_post['ID'] = 132;
$my_post['post_category'] = array($second_teir_id);
$firephp->log('Antes', 'Antes');
if(wp_update_post( $my_post ) == 0) {
$firephp->Error('Error Fatal, Entrada no actualizada', 'error');
}
$firephp->log('Después', 'Después');
*/
return $post_ID;
}
function getIDForCategory($cat_name, $logger) {
$logger->Info("Inicio: getIDForCategory()");
$cats = get_categories();
$whichCatId = "";
foreach($cats as $single_cat) {
if($single_cat->name == $cat_name) {
$whichCatId = $single_cat->term_id;
break;
}
}
$logger->Info("Fin: getIDForCategory()");
return (int)$whichCatId;
}
}
/* Gancho para Creación de Entrada */
/* add_action('publish_post', array('categoryShifter','shiftCategories')); */
add_action('wp_insert_post', array('categoryShifter', 'shiftCategories'));
?>
He cambiado a usar el gancho wp_insert_post por el momento... pero todavía no puedo hacer que la función wp_set_post_categories cambie las categorías de las entradas.
Entiendo que probablemente necesitaré actualizar este código para que tenga en cuenta las categorías existentes de la entrada y solo modifique las especificadas por el plugin, pero por ahora es realmente solo una versión alfa.

// Acciones que se disparan cuando un post cambia de estado a 'publicado'
add_action('new_to_publish', 'tu_funcion'); // De nuevo a publicado
add_action('draft_to_publish', 'tu_funcion'); // De borrador a publicado
add_action('pending_to_publish', 'tu_funcion'); // De pendiente a publicado

¿Qué tal la acción wp_insert_post
? http://core.trac.wordpress.org/browser/tags/3.3.1/wp-includes/post.php#L2656

Pero ¿se ejecutarán nuevamente si, por ejemplo, el estado cambia de publicado a borrador y luego a publicado otra vez?

@soulseekah El hook wp_insert_post
se llamará nuevamente al editar una entrada existente y guardar. Probado en WordPress 5.9. Sé que tu comentario es del 2012 pero por si alguien tiene curiosidad sobre esto.

He leído extensamente el núcleo de WordPress y lo he probado todo. wp_transition_post_status()
, new_to_publish()
, new_{post_type}()
, wp_insert_post()
.
Todos estos resultan, al final, poco confiables.
wp_transition_post_status()
no es confiable porque el nuevo estado "publish" es el estado predeterminado tanto para crear nuevas publicaciones como para actualizar las existentes. El estado anterior no es confiable para definir qué es una nueva publicación y qué no, ya que puede ser draft, auto-draft, publish, etc.
new_to_publish()
no funciona para tipos de publicaciones personalizados.
new_{post_type}
solo pasa $post como parámetro, y no puedes saber si es nuevo o si se está actualizando uno existente.
wp_insert_post()
tiene un parámetro $update que debería ser TRUE al actualizar publicaciones existentes y FALSE al crear nuevas, pero no es confiable porque devuelve TRUE para nuevas publicaciones debido a auto-draft.
Solución: Usar un post meta
Terminé usando un post meta personalizado al que llamé check_if_run_once
que ejecutará cierta lógica solo una vez:
/**
* Hacer algo cuando se crea un nuevo 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')) ) {
# Nueva Publicación
# Hacer algo aquí...
# Y actualizar el meta para que no se ejecute nuevamente
update_post_meta( $post_id, 'check_if_run_once', true );
}
}
add_action( 'wp_insert_post', 'new_book', 10, 3 );
Opcionalmente, si necesitas actualizar tus publicaciones existentes con el meta "check_if_run_once", para que no ejecute el código anterior en publicaciones existentes creadas antes de agregar esa función, podrías hacer:
/**
* Esta es una función temporal para actualizar publicaciones existentes con el post meta "check_if_run_once"
* Accede a tu sitio web iniciado sesión como administrador y agrega ?debug a la URL.
*/
function temporary_function() {
if (current_user_can('manage_options')) {
$posts = get_posts(array(
'post_type' => 'book',
'posts_per_page' => -1, // Puede que necesites paginar esto dependiendo de cuántas publicaciones tengas
'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');
}

Dirigir con precisión la creación de una nueva entrada es en realidad más complicado de lo que parece. Técnicamente hay múltiples formas en que una entrada puede crearse o actualizarse, y hay muchas cosas no tan obvias que técnicamente también son entradas (como las revisiones, por ejemplo).
WordPress proporciona hooks dinámicos que rastrean no solo la creación de entradas, sino también qué era antes y en qué se convirtió. Consulta Transiciones de Estado de Entradas en el Codex.

Más por experimentación que siguiendo la documentación, esto funciona para mí (WP 3.3). Obtengo una llamada al hook transition_post_status con $new_status establecido como "auto-draft" cuando creas una nueva entrada.
function my_post_new($new_status, $old_status=null, $post=null){
if ($new_status == "auto-draft"){
// hacer cosas aquí
}
}
add_action('transition_post_status', 'my_post_new');

Esto se activará cada vez que se guarde un borrador automático, es decir, aproximadamente cada minuto mientras se edita.

Buen punto. En mi caso esto no es un problema pero podría serlo para otros, dependiendo de lo que estuvieras haciendo.

Quería votar a favor, pero aún no puedo. Así que mis elogios van aquí, @PapaFreud. Por cierto, esto ahora está documentado en http://codex.wordpress.org/Post_Status_Transitions. De nuevo, gracias hombre.

@soulseekah Punto interesante que mencionaste, probé esto, y auto-draft
se asigna como el $new_status
de una publicación al momento de su creación (es decir, Agregar nuevo), una sola vez. Tan pronto como se ejecuta el primer procedimiento de AUTOSAVE
después de 1 minuto, el valor de $new_status
se actualiza a draft
. El $old_status
sigue siendo NULL
hasta que la publicación se guarda o publica manualmente. Entonces, técnicamente esto funcionaría y no se ejecutaría cada vez que el editor guarde automáticamente tu trabajo. Como medida adicional podrías verificar que $old_status
sea NULL
para confirmar -> if ($new_status == "auto-draft" && $old_status === NULL)

Encontré que la mejor opción era verificar el estado de la publicación cuando se llama a wp_insert_post
.
Cuando se crea una nueva publicación, el estado inicial es auto-draft
.
if (!function_exists('benyonsFunction')) {
function benyonsFunction($postId, $post) {
if ($post->post_status == "auto-draft") {
error_log('¡¡¡NUEVA PUBLICACIÓN!!!');
}
}
}
add_action('wp_insert_post', 'benyonsFunction', 10, 2);
Este error_log solo se ejecutará una vez.

La forma más limpia y confiable de manejar esto para un tipo de publicación conocido es usar el hook {$new_status}_{$post->post_type}
que proporciona el estado anterior y compararlo con "publish".
Por ejemplo, si quisieras activar una acción cuando se crea una nueva "página", usarías:
add_action( 'publish_page', function( int $post_id, \WP_Post $post, string $old_status ) {
if ( 'publish' === $old_status ) {
// Nada que hacer aquí.
return;
}
// Aquí va la lógica de negocio.
}, 10, 3 );

No, esto se activará cada vez que una página cambie de cualquier estado a 'publicado', por lo que se activará muchas más veces que solo en la primera creación.
