Files
feadulta/mu-plugins/fea-crear-traduccion-api.php

170 lines
6.0 KiB
PHP

<?php
/**
* Plugin Name: Fe Adulta — API crear traducción
* Description: Endpoint REST idempotente para crear y enlazar traducciones Polylang.
* Version: 1.0
*
* POST /wp-json/fea/v1/crear-traduccion
* es_id=<post ES>, lang=<en|fr|it|pt>, title/content/excerpt/status/model
*
* Ver issue gitea.feadulta.com/rafa/feadulta#222.
*/
if (!defined('ABSPATH')) exit;
const FEA_TRANSLATION_LANGUAGES = ['en', 'fr', 'it', 'pt'];
add_action('rest_api_init', function () {
register_rest_route('fea/v1', '/crear-traduccion', [
'methods' => WP_REST_Server::CREATABLE,
'callback' => 'fea_crear_traduccion_handle',
'permission_callback' => 'fea_crear_traduccion_can_call',
// Se valida después de autorizar para que las llamadas anónimas
// obtengan 401 aunque omitan parámetros.
'args' => [
'es_id' => [
'sanitize_callback' => 'absint',
],
'lang' => [
'sanitize_callback' => 'sanitize_key',
],
'status' => [
'sanitize_callback' => 'sanitize_key',
],
'model' => [
'sanitize_callback' => 'sanitize_text_field',
],
],
]);
});
/** Editor o superior: mismo nivel que los demás endpoints fea/v1 de escritura. */
function fea_crear_traduccion_can_call(WP_REST_Request $request) {
if (!is_user_logged_in()) {
return new WP_Error(
'fea_crear_traduccion_not_authenticated',
'Debes autenticarte para crear una traducción.',
['status' => 401]
);
}
if (!current_user_can('edit_others_posts')) {
return new WP_Error(
'fea_crear_traduccion_forbidden',
'No tienes permiso para crear traducciones.',
['status' => 403]
);
}
return true;
}
function fea_crear_traduccion_handle(WP_REST_Request $request) {
$es_id = absint($request->get_param('es_id'));
if (!$es_id) {
return new WP_Error(
'fea_crear_traduccion_invalid_source',
'es_id es obligatorio.',
['status' => 400]
);
}
$lang = (string) $request->get_param('lang');
if (!in_array($lang, FEA_TRANSLATION_LANGUAGES, true)) {
return new WP_Error(
'fea_crear_traduccion_invalid_language',
'lang debe ser en, fr, it o pt.',
['status' => 400]
);
}
$status = (string) ($request->get_param('status') ?: 'draft');
if (!in_array($status, ['draft', 'publish'], true)) {
return new WP_Error(
'fea_crear_traduccion_invalid_status',
'status debe ser draft o publish.',
['status' => 400]
);
}
$source = get_post($es_id);
if (!$source) {
return new WP_Error(
'fea_crear_traduccion_source_not_found',
'No existe el post español indicado.',
['status' => 404]
);
}
if (!function_exists('pll_get_post') || !function_exists('pll_set_post_language') || !function_exists('pll_save_post_translations')) {
return new WP_Error(
'fea_crear_traduccion_polylang_unavailable',
'Polylang no está disponible.',
['status' => 500]
);
}
// Idempotencia dura: se responde antes de interpretar un payload nuevo.
$existing_id = (int) pll_get_post($es_id, $lang);
if ($existing_id && !get_post($existing_id)) $existing_id = 0;
if ($existing_id) {
return new WP_REST_Response([
'es_id' => $es_id,
'lang' => $lang,
'translation_id' => $existing_id,
'created' => false,
'url' => get_permalink($existing_id),
], 200);
}
$title = (string) ($request->get_param('title') ?? '');
$new_id = wp_insert_post([
'post_title' => wp_slash($title),
'post_content' => wp_slash((string) ($request->get_param('content') ?? '')),
'post_excerpt' => wp_slash((string) ($request->get_param('excerpt') ?? '')),
'post_name' => sanitize_title($title),
'post_status' => $status,
'post_type' => 'post',
'post_author' => (int) $source->post_author,
'post_date' => $source->post_date,
'to_ping' => '',
'pinged' => '',
], true);
if (is_wp_error($new_id)) {
return new WP_Error(
'fea_crear_traduccion_insert_failed',
'No se pudo crear la traducción: ' . $new_id->get_error_message(),
['status' => 500]
);
}
// El idioma se asigna antes de mapear categorías para que Polylang admita
// los términos traducidos en el nuevo post.
pll_set_post_language($new_id, $lang);
$categories = wp_get_post_categories($es_id);
$mapped_categories = [];
foreach ($categories as $category_id) {
$translated_category = function_exists('pll_get_term') ? (int) pll_get_term($category_id, $lang) : 0;
$mapped_categories[] = $translated_category ?: $category_id;
}
if ($mapped_categories) {
wp_set_post_categories($new_id, array_values(array_unique($mapped_categories)));
}
$translations = function_exists('pll_get_post_translations') ? pll_get_post_translations($es_id) : ['es' => $es_id];
if (!$translations) $translations = ['es' => $es_id];
$translations[$lang] = $new_id;
pll_save_post_translations($translations);
update_post_meta($new_id, 'traduccion_automatica', '1');
update_post_meta($new_id, 'traduccion_origen', $es_id);
update_post_meta($new_id, 'traduccion_modelo', (string) ($request->get_param('model') ?? ''));
update_post_meta($new_id, 'traduccion_fecha', gmdate('c'));
return new WP_REST_Response([
'es_id' => $es_id,
'lang' => $lang,
'translation_id' => (int) $new_id,
'created' => true,
'url' => get_permalink($new_id),
], 201);
}