'Bearer ' . citami_get_api_key(), 'Accept' => 'application/json', 'User-Agent' => 'CitamiConnector/' . CITAMI_PLUGIN_VERSION . '; ' . home_url('/'), ); } /** GET on the platform; array('ok' => bool, 'data' => array|null, 'error' => string). */ function citami_articles_get($path) { if (citami_get_api_key() === '') { return array('ok' => false, 'data' => null, 'error' => 'no_key'); } $res = wp_remote_get(citami_get_endpoint() . $path, array('timeout' => 30, 'headers' => citami_articles_headers())); if (is_wp_error($res)) { return array('ok' => false, 'data' => null, 'error' => $res->get_error_message()); } $code = (int) wp_remote_retrieve_response_code($res); $data = json_decode((string) wp_remote_retrieve_body($res), true); if ($code < 200 || $code >= 300 || !is_array($data)) { return array('ok' => false, 'data' => null, 'error' => 'HTTP ' . $code); } return array('ok' => true, 'data' => $data, 'error' => ''); } /** The site's articles on the platform (ready, to review, published), cached 10 minutes. */ function citami_articles_list($force = false) { $cached = get_transient(CITAMI_ARTICLES_TRANSIENT); if (!$force && is_array($cached)) { return $cached; } $r = citami_articles_get('/api/plugin/articles'); $out = array('ok' => !empty($r['ok']), 'articles' => array(), 'error' => $r['error'], 'fetched_at' => time()); if (!empty($r['ok']) && isset($r['data']['articles']) && is_array($r['data']['articles'])) { foreach ($r['data']['articles'] as $a) { if (!is_array($a) || empty($a['id'])) { continue; } $out['articles'][] = array( 'id' => (string) $a['id'], 'title' => (string) (isset($a['title']) ? $a['title'] : ''), 'status' => (string) (isset($a['status']) ? $a['status'] : ''), 'keywords' => isset($a['keywords']) && is_array($a['keywords']) ? array_map('strval', $a['keywords']) : array(), 'word_count' => isset($a['word_count']) ? (int) $a['word_count'] : null, 'quality_score' => isset($a['quality_score']) ? (int) $a['quality_score'] : null, 'created_at' => (string) (isset($a['created_at']) ? $a['created_at'] : ''), 'published_url' => (string) (isset($a['published_url']) ? $a['published_url'] : ''), 'wp_post_id' => isset($a['wp_post_id']) ? (int) $a['wp_post_id'] : 0, ); } set_transient(CITAMI_ARTICLES_TRANSIENT, $out, CITAMI_ARTICLES_TTL); } elseif (is_array($cached)) { $cached['stale_error'] = $r['error']; return $cached; } return $out; } /** Ready-to-publish count for the Overview (cache only when fresh, one fetch per 10 minutes otherwise). */ function citami_articles_ready_count() { if (citami_get_api_key() === '') { return 0; } $l = citami_articles_list(); $n = 0; foreach ($l['articles'] as $a) { if ($a['status'] === 'ready' || $a['status'] === 'needs_review') { $n++; } } return $n; } /** * Creates the post from a platform payload (title, content, status, * excerpt, slug, categories, author, schema_jsonld). Shared with the REST * route the dashboard calls, so both publish paths produce the same post. * Returns array('id' => int, 'link' => string) or WP_Error. */ function citami_publish_article_payload(array $data) { $title = (string) (isset($data['title']) ? $data['title'] : ''); $content = (string) (isset($data['content']) ? $data['content'] : ''); if (trim($title) === '' || trim($content) === '') { return new WP_Error('citami_publish_missing_fields', 'title e content sono obbligatori.', array('status' => 400)); } $status = (string) (isset($data['status']) ? $data['status'] : 'draft'); if (!in_array($status, array('draft', 'publish', 'pending'), true)) { $status = 'draft'; } // Autore: quello richiesto se esiste, altrimenti il primo amministratore. $author = absint(isset($data['author']) ? $data['author'] : 0); if ($author === 0 || !get_user_by('id', $author)) { $admins = get_users(array('role' => 'administrator', 'number' => 1, 'orderby' => 'ID', 'order' => 'ASC', 'fields' => 'ID')); $first = !empty($admins) ? $admins[0] : 0; $author = is_object($first) ? (int) $first->ID : (int) $first; } $postarr = array( 'post_title' => sanitize_text_field($title), 'post_content' => wp_kses_post($content), 'post_status' => $status, 'post_type' => 'post', 'post_excerpt' => sanitize_text_field((string) (isset($data['excerpt']) ? $data['excerpt'] : '')), ); if ($author > 0) { $postarr['post_author'] = $author; } $slug = sanitize_title((string) (isset($data['slug']) ? $data['slug'] : '')); if ($slug !== '') { $postarr['post_name'] = $slug; } if (isset($data['categories']) && is_array($data['categories'])) { $cat_ids = array_values(array_filter(array_map('absint', $data['categories']))); if (!empty($cat_ids)) { $postarr['post_category'] = $cat_ids; } } $post_id = wp_insert_post($postarr, true); if (is_wp_error($post_id)) { return new WP_Error('citami_publish_failed', $post_id->get_error_message(), array('status' => 500)); } $schema = isset($data['schema_jsonld']) ? $data['schema_jsonld'] : ''; if (is_string($schema) && $schema !== '') { update_post_meta($post_id, '_citami_schema_jsonld', wp_slash($schema)); } if (function_exists('citami_sideload_post_images')) { $imported = citami_sideload_post_images((int) $post_id, $postarr['post_content']); if (is_string($imported) && $imported !== $postarr['post_content']) { wp_update_post(array('ID' => (int) $post_id, 'post_content' => $imported)); } } return array('id' => (int) $post_id, 'link' => get_permalink($post_id)); } /** The plugin-side publish: pull the article, create the post, report back. */ function citami_articles_publish($article_id, $status = 'draft') { $article_id = preg_replace('/[^a-f0-9-]/i', '', (string) $article_id); if ($article_id === '') { return array('ok' => false, 'message' => citami_t('Articolo non valido.', 'Invalid article.')); } $map = get_option(CITAMI_ARTICLES_MAP_OPT, array()); if (is_array($map) && !empty($map[$article_id]) && get_post((int) $map[$article_id])) { return array('ok' => false, 'message' => citami_t('Questo articolo è già stato pubblicato da qui.', 'This article was already published from here.'), 'post_id' => (int) $map[$article_id]); } $r = citami_articles_get('/api/plugin/articles/' . rawurlencode($article_id)); if (empty($r['ok']) || empty($r['data']['article']) || !is_array($r['data']['article'])) { return array('ok' => false, 'message' => sprintf(citami_t('Articolo non scaricato dalla piattaforma (%s).', 'Could not fetch the article from the platform (%s).'), $r['error'] !== '' ? $r['error'] : '?')); } $a = $r['data']['article']; if (!empty($a['wp_post_id'])) { return array('ok' => false, 'message' => citami_t('La piattaforma lo segna già come pubblicato.', 'The platform already marks it as published.')); } $status = in_array($status, array('draft', 'publish', 'pending'), true) ? $status : 'draft'; $data = array( 'title' => isset($a['title']) ? $a['title'] : '', 'content' => isset($a['content']) ? $a['content'] : '', 'excerpt' => isset($a['excerpt']) ? $a['excerpt'] : '', 'slug' => isset($a['slug']) ? $a['slug'] : '', 'schema_jsonld' => isset($a['schema_jsonld']) ? $a['schema_jsonld'] : '', 'status' => $status, 'author' => get_current_user_id(), ); $post = citami_publish_article_payload($data); if (is_wp_error($post)) { return array('ok' => false, 'message' => $post->get_error_message()); } update_post_meta($post['id'], '_citami_article_id', $article_id); $map = is_array($map) ? $map : array(); $map[$article_id] = (int) $post['id']; update_option(CITAMI_ARTICLES_MAP_OPT, $map, false); // Report: the platform marks it published and starts the keyword tracking. $reported = false; $res = wp_remote_post(citami_get_endpoint() . '/api/plugin/articles/' . rawurlencode($article_id) . '/published', array( 'timeout' => 30, 'headers' => citami_articles_headers() + array('Content-Type' => 'application/json'), 'body' => wp_json_encode(array('post_id' => (int) $post['id'], 'url' => (string) $post['link'], 'status' => $status)), )); if (!is_wp_error($res)) { $code = (int) wp_remote_retrieve_response_code($res); $reported = $code >= 200 && $code < 300; } delete_transient(CITAMI_ARTICLES_TRANSIENT); if (function_exists('citami_activity_log')) { citami_activity_log('article', sprintf(citami_t('Articolo pubblicato dalla piattaforma: "%s" (%s)', 'Article published from the platform: "%s" (%s)'), $data['title'], $status === 'publish' ? citami_t('online', 'live') : citami_t('bozza', 'draft')), 1, array('post' => (int) $post['id'], 'article' => $article_id)); } $message = $status === 'publish' ? sprintf(citami_t('"%s" è online.', '"%s" is live.'), $data['title']) : sprintf(citami_t('"%s" è in bozza: rivedila e pubblica dall\'editor.', '"%s" is a draft: review it and publish from the editor.'), $data['title']); if (!$reported) { $message .= ' ' . citami_t('La piattaforma non ha ricevuto la conferma: il rendimento dell\'articolo partirà al prossimo sync.', 'The platform did not get the confirmation: the article\'s performance tracking starts with the next sync.'); } return array('ok' => true, 'message' => $message, 'post_id' => (int) $post['id'], 'link' => (string) $post['link'], 'reported' => $reported); } /** 2.59 — the full article for the preview row, cached an hour; null on failure. */ function citami_articles_preview_fetch($article_id) { $r = citami_articles_get('/api/plugin/articles/' . rawurlencode($article_id)); if (empty($r['ok']) || empty($r['data']['article']) || !is_array($r['data']['article'])) { return null; } $a = $r['data']['article']; $content = (string) (isset($a['content']) ? $a['content'] : ''); $pv = array( 'title' => (string) (isset($a['title']) ? $a['title'] : ''), 'slug' => (string) (isset($a['slug']) ? $a['slug'] : ''), 'excerpt' => (string) (isset($a['excerpt']) ? $a['excerpt'] : ''), 'content' => wp_kses_post($content), 'words' => str_word_count(wp_strip_all_tags($content)), 'images' => preg_match_all('/ !empty($a['schema_jsonld']), ); set_transient('citami_article_preview_' . md5($article_id), $pv, HOUR_IN_SECONDS); return $pv; } /* ---------------------------------------------------------------- */ /* Admin: action handler + tab */ /* ---------------------------------------------------------------- */ function citami_articles_form_open($do, $extra = '', $id = '', $class = 'citami-inline-form') { return '
' . wp_nonce_field('citami_articles', '_citami_articles_nonce', true, false) . '' . $extra; } function citami_handle_articles_action() { if (!current_user_can('publish_posts')) { wp_die('Permesso negato.', '', array('response' => 403)); } if (!wp_verify_nonce(isset($_POST['_citami_articles_nonce']) ? sanitize_text_field(wp_unslash($_POST['_citami_articles_nonce'])) : '', 'citami_articles')) { wp_die('Nonce non valido.', '', array('response' => 403)); } $do = isset($_POST['do']) ? sanitize_key((string) $_POST['do']) : 'refresh'; if ($do === 'preview') { // 2.59 — read before publishing: the full text is fetched and shown under its row. $id = isset($_POST['article_id']) ? preg_replace('/[^a-f0-9-]/i', '', sanitize_text_field(wp_unslash($_POST['article_id']))) : ''; $pv = $id !== '' ? citami_articles_preview_fetch($id) : null; if ($pv === null) { set_transient('citami_action_notice', array('type' => 'error', 'message' => citami_t('Anteprima non disponibile: la piattaforma non ha risposto. Riprova.', 'Preview not available: the platform did not answer. Try again.')), 30); wp_safe_redirect(citami_admin_url('articles')); exit; } wp_safe_redirect(citami_admin_url('articles', array('preview' => $id)) . '#citami-art-' . substr(md5($id), 0, 8)); exit; } if ($do === 'publish') { $status = isset($_POST['status']) ? sanitize_key((string) $_POST['status']) : 'draft'; $r = citami_articles_publish(isset($_POST['article_id']) ? sanitize_text_field(wp_unslash($_POST['article_id'])) : '', $status); $notice = array('type' => !empty($r['ok']) ? 'success' : 'error', 'message' => $r['message']); if (!empty($r['ok']) && !empty($r['post_id'])) { // 2.59 — land on the article's row in "Published", with its edit link. set_transient('citami_action_notice', $notice, 30); wp_safe_redirect(citami_admin_url('articles', array('done' => (int) $r['post_id'])) . '#citami-pub-' . (int) $r['post_id']); exit; } } else { $l = citami_articles_list(true); $notice = !empty($l['ok']) ? array('type' => 'success', 'message' => sprintf(citami_t('Elenco aggiornato: %d articoli.', 'List refreshed: %d articles.'), count($l['articles']))) : array('type' => 'error', 'message' => sprintf(citami_t('Aggiornamento non riuscito (%s).', 'Refresh failed (%s).'), $l['error'] !== '' ? $l['error'] : '?')); } set_transient('citami_action_notice', $notice, 30); wp_safe_redirect(admin_url('admin.php?page=citami-connector&tab=articles')); exit; } add_action('admin_post_citami_articles_action', 'citami_handle_articles_action'); function citami_articles_status_label($status) { switch ($status) { case 'ready': return array(citami_t('Pronto', 'Ready'), 'citami-pill--ok'); case 'needs_review': return array(citami_t('Da rivedere', 'To review'), 'citami-pill--warn'); case 'published': return array(citami_t('Pubblicato', 'Published'), 'citami-pill--ok'); case 'writing': case 'generating': case 'in_progress': return array(citami_t('In scrittura', 'Being written'), ''); case 'queued': case 'pending': return array(citami_t('In coda', 'Queued'), ''); case 'draft': return array(citami_t('Bozza sulla piattaforma', 'Draft on the platform'), ''); case 'failed': case 'error': return array(citami_t('Non riuscito', 'Failed'), 'citami-pill--warn'); } return array($status !== '' ? $status : citami_t('sconosciuto', 'unknown'), ''); } /** 2.60 — only these can be published from here; the rest is still being made on the platform. */ function citami_articles_is_publishable($status) { return in_array((string) $status, array('ready', 'needs_review'), true); } function citami_render_articles_tab() { $connected = citami_get_api_key() !== ''; $l = $connected ? citami_articles_list() : array('ok' => false, 'articles' => array(), 'error' => 'no_key'); $map = get_option(CITAMI_ARTICLES_MAP_OPT, array()); $map = is_array($map) ? $map : array(); $ready = array(); $preparing = array(); $published = array(); foreach ($l['articles'] as $a) { if ($a['status'] === 'published') { $published[] = $a; } elseif (citami_articles_is_publishable($a['status'])) { $ready[] = $a; } else { $preparing[] = $a; } } $dashboard = citami_lang_url((function_exists('citami_brand_site') ? citami_brand_site() : citami_get_endpoint()) . '/dashboard/agents/content'); ?>

>


', $fid . '-d'), citami_kses_allowed()); ?> ', $fid . '-p'), citami_kses_allowed()); ?> ', $fid . '-v'), citami_kses_allowed()); ?>


0 ? get_post_status($pid) : false; ?> >

— — 0 && $st): ?>