170 lines
6.0 KiB
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);
|
|
}
|