¿Cuál es la mejor manera de manejar acciones de páginas personalizadas en plugins?
Me encuentro constantemente con la misma molestia, así que pensé en ver si hay algunas ideas o experiencias al respecto...
He creado un plugin que usa su propia página de administración. Es necesario. Ahora que he resuelto los asuntos con WP_List_Table(), debo decir que es genial... pero...
Las páginas personalizadas de plugins siempre se cargan como admin.php?page=...
a menos que quiera cargarlas directamente desde el directorio del plugin, lo cual no quiero. Ahora, si realizo una 'acción' desde esa página, necesito procesarla de alguna manera y luego redirigir de vuelta a la página sin el parámetro de acción. No importa si lo hago por GET o POST, realmente.
En todas sus páginas internas, WordPress hace esto en la misma página, comprueba si hay una acción, si es así la procesa y luego redirecciona a sí misma sin la acción. Esto es posible porque en estas páginas el admin-header
aún no se ha cargado.
Sin embargo, si intentas hacerlo en tu propia página, la mitad de la interfaz de administración ya ha sido enviada al navegador, por lo que una redirección ya no es posible. Claramente, la solución es hacer POST/GET directamente a otra página, cargar el framework de WP en ella, hacer el procesamiento y luego redirigir de vuelta a la página original... pero... esto es un poco molesto porque... mi página original se carga a través de un callback, por lo que se ejecuta dentro de un método de mi clase. Eso es hermoso.
Si cargo una página separada, tengo que incluir manualmente wp-load.php
y estoy fuera de mi clase, lo cual es molesto, y en mi caso particular me molesta especialmente porque solo estoy instanciando mi clase de plugin de forma anónima para que nadie pueda acceder a ella desde el exterior.
Entonces, después de esta larga historia... ¿alguien ha encontrado una buena solución para cargar otra página a través de un callback sin tener toda la interfaz de administración ya configurada alrededor?
(Conozco una solución alternativa... puedo enganchar una función en load-....
que verifique el parámetro de acción y realice el procesamiento y la redirección. Pero me pregunto si hay una mejor manera.)
Gracias.

Como regla general, deberías usar una solicitud POST para la mayoría de las acciones, para asegurarte de que no se ejecuten por accidente. Pero también es una buena práctica redirigir a una página normal después de una solicitud POST, para evitar una ejecución duplicada cuando el usuario actualiza la página.
Así que el flujo es el siguiente:
- La página de tu plugin con un formulario POST, que envía a
- Una página que maneja la solicitud, que redirige a
- La página de tu plugin, que muestra el resultado de la acción
La página intermedia no tiene que ser la de tu plugin. Esto significa que puedes usar el "manejador genérico de POST" que se incluyó hace tres años, el hook 'admin_action_' . $_REQUEST['action']
en admin.php
.
Un ejemplo de uso es el plugin Akismet. Si quieres usarlo de manera confiable, debes enviar directamente a admin.php
, no a otra página que incluya admin.php
.
Aquí tienes un ejemplo muy básico de cómo usarlo:
add_action( 'admin_action_wpse10500', 'wpse10500_admin_action' );
function wpse10500_admin_action()
{
// Haz tus cosas aquí
wp_redirect( $_SERVER['HTTP_REFERER'] );
exit();
}
add_action( 'admin_menu', 'wpse10500_admin_menu' );
function wpse10500_admin_menu()
{
add_management_page( 'Página de prueba WPSE 10500', 'Página de prueba WPSE 10500', 'administrator', 'wpse10500', 'wpse10500_do_page' );
}
function wpse10500_do_page()
{
?>
<form method="POST" action="<?php echo admin_url( 'admin.php' ); ?>">
<input type="hidden" name="action" value="wpse10500" />
<input type="submit" value="¡Hazlo!" />
</form>
<?php
}

Hola, voy a revisar el código nuevamente, obviamente no lo había visto, pero solo para confirmar... ¿lo que estás diciendo es que si llamo a admin.php directamente sin un parámetro de página, omite toda la carga de páginas y solo hace algo de inicialización y ejecuta el hook? Eso sería genial... más o menos (todavía no entiendo por qué no pusieron el hook antes de la carga de páginas).

@wyrfel: Sí, llamar directamente a admin.php
es el "truco" que me enseñó el código fuente de Akismet. Tienes razón cuando muestras un formulario y quieres mostrarlo nuevamente en caso de errores: entonces sería fácil si el destino es la página de tu plugin pero el hook está al inicio (así podrías redirigir si es exitoso, o mostrar el formulario nuevamente con mensajes de error si no). ¿Quizás sugerirlo en un ticket de Trac?

Voy a crear un ticket. Como solución temporal, encontré que el hook 'load-<pagehook>'
funciona... se llama antes de que la página se cargue... pero el concepto de admin_action_...
parece mucho más agradable y específico. Además, como nota, los mensajes de error siguen siendo problemáticos si haces POST's y no quieres reenviar al recargar, pero ese es otro tema.

@wyrfel: ¿Por qué los mensajes de error seguirían siendo problemáticos? Si hay un mensaje de error, permanece en la página y muestra el formulario nuevamente con los mensajes (por supuesto, un refresco no tendría mucho sentido aquí, pero tampoco haría daño, porque los errores seguirían estando presentes y no se ejecutará ninguna acción). Si no hay errores, ejecuta la acción y redirige a una página de resumen "segura". Esto funcionaría si el hook admin_action_
se moviera antes del cargador de páginas de plugins.

He ampliado esta publicación y habilité estos mismos métodos 'admin-action' para ser llamados desde URLs de 'post-row-action'. La solución utiliza jQuery para mapear una URL GET a la solicitud POST del FORM que esta respuesta aborda. http://wordpress.stackexchange.com/questions/82761/how-can-i-link-post-row-actions-with-a-custom-action-function

Enfoqué esto de manera ligeramente diferente simplemente agregando noheader=true a la URL de acción en la página donde el usuario envía la acción.
Mi manejador luego realiza la acción (es decir, típicamente agregar, actualizar o eliminar) y finaliza con un wp_redirect() a la siguiente acción de página (ej. página de agregar -> página de editar, página de eliminar -> página de listado, página de editar -> página de editar). También paso un mensaje en la URL para poder mostrar un estado como actualización exitosa o fallida.
Este enfoque mantiene todas las acciones: listar, agregar, editar, eliminar, eliminación masiva, etc. en la misma clase y con el mismo slug de administración, por lo que es bastante fácil de mantener y entender.
