--- a/xagio-seo/inc/xagio_core.php
+++ b/xagio-seo/inc/xagio_core.php
@@ -232,7 +232,6 @@
'domain' => XAGIO_DOMAIN,
'uploads_dir' => wp_upload_dir(),
'connected' => XAGIO_CONNECTED,
- 'api_key' => XAGIO_API::getAPIKey(),
'nonce' => wp_create_nonce('xagio_nonce'),
'_wpnonce' => wp_create_nonce('elementor_revert_kit'),
'elementor_nonce' => wp_create_nonce('elementor_ajax')
--- a/xagio-seo/modules/ocw/models/xagio_ocw.php
+++ b/xagio-seo/modules/ocw/models/xagio_ocw.php
@@ -745,6 +745,8 @@
$xagio_updatedBlocks = self::gutenbergApplyTexts($xagio_blocks, $xagio_decoded_output);
$xagio_newContent = serialize_blocks($xagio_updatedBlocks);
+ self::xagio_fix_agentx_gutenberg_menu();
+
wp_update_post([
'ID' => $post_id,
'post_content' => $xagio_newContent,
@@ -1923,7 +1925,7 @@
$xp = new DOMXPath($dom);
$block = 'self::h1 or self::h2 or self::h3 or self::h4 or self::h5 or self::h6 or self::p or self::li or self::figcaption or self::label';
- $inline = 'self::a or self::button or self::span';
+ $inline = 'self::a or self::button or self::span or self::strong or self::em or (self::div[contains(@class, "kb-count-up-title")])';
// Same selection strategy as the extractor
$nodes = [];
@@ -2242,6 +2244,140 @@
}
}
+ private static function xagio_fix_agentx_gutenberg_menu() {
+ global $wpdb;
+
+ // Find latest published kadence_navigation with exact post_title
+ $post_id = (int) $wpdb->get_var(
+ $wpdb->prepare(
+ "SELECT ID
+ FROM {$wpdb->posts}
+ WHERE post_type = %s
+ AND post_status = 'publish'
+ AND post_title = %s
+ ORDER BY post_date_gmt DESC
+ LIMIT 1",
+ 'kadence_navigation',
+ 'Header Menu'
+ )
+ );
+
+ if (!$post_id) return false;
+
+ $header = get_post($post_id);
+ if (!$header || $header->post_type !== 'kadence_navigation' || $header->post_status !== 'publish') return false;
+
+ if (!function_exists('parse_blocks') || !function_exists('serialize_blocks')) return false;
+
+ $blocks = parse_blocks($header->post_content);
+ if (!is_array($blocks) || empty($blocks)) return false;
+
+ $modified = false;
+
+ $clean_label = function($label) {
+ $label = (string) $label;
+ $label = preg_replace('/^s*Agents*X[^p{L}]*/iu', '', $label);
+ return trim($label);
+ };
+
+ $normalize_url = function($url) {
+ $url = trim((string)$url);
+ if ($url === '/home' || $url === '/home/') return '/';
+ return $url;
+ };
+
+ $force_custom = function(array &$attrs, $new_label, $new_url) {
+ $attrs['label'] = $new_label;
+ $attrs['url'] = $new_url;
+ $attrs['kind'] = 'custom';
+
+ // Remove entity pointers so Kadence can't re-resolve URL
+ foreach (['id','ID','postId','postID','objectId','objectID','type','subtype','taxonomy','termId','termID','slug','link','href','uuid'] as $k) {
+ if (array_key_exists($k, $attrs)) unset($attrs[$k]);
+ }
+ };
+
+ $walk = function (&$b, $in_service = false) use (&$walk, &$modified, $clean_label, $normalize_url, $force_custom) {
+ if (!is_array($b)) return;
+
+ if (($b['blockName'] ?? '') === 'kadence/navigation-link') {
+ $label_raw = $b['attrs']['label'] ?? '';
+ $url_raw = $b['attrs']['url'] ?? '';
+
+ $label = $clean_label($label_raw);
+ $norm = strtolower($label);
+ $url = $normalize_url($url_raw);
+
+ // remove Agent X prefix if present
+ if ($label !== (string)$label_raw) {
+ $b['attrs']['label'] = $label;
+ $modified = true;
+ }
+
+ // Home: force custom "/" and label "Home"
+ if ($norm === 'home') {
+ $force_custom($b['attrs'], 'Home', '/');
+ $modified = true;
+ }
+
+ // Service parent: force custom "#", and mark subtree
+ if ($norm === 'service' || $norm === 'services') {
+ $desired = ($norm === 'services') ? 'Services' : 'Service';
+ $force_custom($b['attrs'], $desired, '#');
+ $modified = true;
+ $in_service = true;
+ } else {
+ // Children of Service dropdown => '#'
+ if ($in_service) {
+ $cur_label = (string)($b['attrs']['label'] ?? $label_raw);
+ $force_custom($b['attrs'], $cur_label, '#');
+ $modified = true;
+ } else {
+ // Normalize /home/ => /
+ if ($url !== (string)$url_raw) {
+ $b['attrs']['url'] = $url;
+ $modified = true;
+ }
+ }
+ }
+ }
+
+ if (!empty($b['innerBlocks']) && is_array($b['innerBlocks'])) {
+ foreach ($b['innerBlocks'] as &$child) {
+ $walk($child, $in_service);
+ }
+ unset($child);
+ }
+ };
+
+ foreach ($blocks as &$root) {
+ $walk($root, false);
+ }
+ unset($root);
+
+ if (!$modified) return false;
+
+ $new_content = serialize_blocks($blocks);
+
+ $upd = wp_update_post([
+ 'ID' => $post_id,
+ 'post_content' => $new_content,
+ ], true);
+
+ if (is_wp_error($upd)) return false;
+
+ // Optional: help invalidate caches
+ clean_post_cache($post_id);
+ if (function_exists('wp_cache_flush')) {
+ wp_cache_flush();
+ }
+
+ return true;
+ }
+
+
+
+
private static function xagio_process_manifest_array(array $manifest, ?string $extracted_uploads_dir) {
$posts = is_array($manifest['posts'] ?? null) ? $manifest['posts'] : [];
$attachments = is_array($manifest['attachments'] ?? null) ? $manifest['attachments'] : [];
--- a/xagio-seo/modules/seo/models/xagio_tinymce.php
+++ b/xagio-seo/modules/seo/models/xagio_tinymce.php
@@ -73,39 +73,75 @@
public static function pixabayDownloadImage()
{
-
check_ajax_referer('xagio_nonce', '_xagio_nonce');
- if (!isset($_POST['img'], $_POST['title'])) {
- wp_die('Required parameters are missing.', 'Missing Parameters', ['response' => 400]);
+ // Block Subscribers / low-privilege users
+ if ( ! is_user_logged_in() || ! current_user_can('upload_files') ) {
+ xagio_json('error', 'Forbidden.');
+ }
+
+ if (empty($_POST['img']) || empty($_POST['title'])) {
+ xagio_json('error', 'Required parameters are missing.');
}
$uploads = wp_upload_dir();
- $image_url = sanitize_text_field(wp_unslash($_POST['img']));
- $xagio_name = sanitize_text_field(wp_unslash($_POST['title'])) . '.jpg';
+ $image_url = esc_url_raw(wp_unslash($_POST['img']));
+ $title = sanitize_text_field(wp_unslash($_POST['title']));
+
+ if (empty($image_url) || empty($title)) {
+ xagio_json('error', 'Invalid parameters.');
+ }
+
+ // Allow Pixabay only
+ $allowed_hosts = [
+ 'pixabay.com',
+ 'www.pixabay.com',
+ 'cdn.pixabay.com'
+ ];
+
+ $parts = wp_parse_url($image_url);
+ $host = strtolower($parts['host'] ?? '');
+ $scheme = strtolower($parts['scheme'] ?? '');
+
+ if (!$host || !in_array($host, $allowed_hosts, true)) {
+ xagio_json('error', 'Invalid image source.');
+ }
+
+ if (!in_array($scheme, ['http', 'https'], true)) {
+ xagio_json('error', 'Invalid image URL.');
+ }
+
+ $xagio_name = $title . '.jpg';
+ $filename = wp_unique_filename($uploads['path'], $xagio_name, NULL);
- $filename = wp_unique_filename($uploads['path'], $xagio_name, $unique_filename_callback = NULL);
$wp_file_type = wp_check_filetype($filename, NULL);
- $full_path_file_name = $uploads['path'] . "/" . $filename;
+ $full_path_file_name = trailingslashit($uploads['path']) . $filename;
$image_string = self::fetch_image($image_url);
- $fileSaved = xagio_file_put_contents($uploads['path'] . "/" . $filename, $image_string);
+ if ($image_string === false || $image_string === '') {
+ xagio_json('error', 'Failed to download image.');
+ }
+
+ $fileSaved = xagio_file_put_contents($full_path_file_name, $image_string);
if (!$fileSaved) {
xagio_json('error', 'Cannot save this selected image to server. Please contact support.');
}
$attachment = [
- 'post_mime_type' => $wp_file_type['type'],
+ 'post_mime_type' => $wp_file_type['type'] ?: 'image/jpeg',
'post_title' => preg_replace('/.[^.]+$/', '', $filename),
'post_content' => '',
'post_status' => 'inherit',
- 'guid' => $uploads['url'] . "/" . $filename,
+ 'guid' => trailingslashit($uploads['url']) . $filename,
];
- $attach_id = wp_insert_attachment($attachment, $full_path_file_name, 0);
+
+ $attach_id = wp_insert_attachment($attachment, $full_path_file_name, 0);
if (!$attach_id) {
+ wp_delete_file($full_path_file_name);
xagio_json('error', 'Failed save this selected image into the database. Please contact support.');
}
+
require_once(ABSPATH . 'wp-admin/includes/image.php');
$attach_data = wp_generate_attachment_metadata($attach_id, $full_path_file_name);
wp_update_attachment_metadata($attach_id, $attach_data);
@@ -125,30 +161,29 @@
{
if (function_exists("curl_init")) {
return self::curl_fetch_image($xagio_url);
- } else if (ini_get("allow_url_fopen")) {
- return self::fopen_fetch_image($xagio_url);
}
+
+ return false;
}
public static function curl_fetch_image($xagio_url)
{
- $xagio_response = wp_remote_get($xagio_url, [
+ $resp = wp_safe_remote_get($xagio_url, [
'timeout' => 10,
- // Adjust the timeout as needed
+ 'redirection' => 2,
+ 'headers' => ['Accept' => 'image/*'],
]);
- if (is_wp_error($xagio_response)) {
- return false; // Or handle the error as needed
+ if (is_wp_error($resp)) {
+ return false;
}
- $image = wp_remote_retrieve_body($xagio_response);
- return $image;
- }
+ $content_type = wp_remote_retrieve_header($resp, 'content-type');
+ if (!$content_type || stripos($content_type, 'image/') !== 0) {
+ return false;
+ }
- public static function xagio_fopen_fetch_image($xagio_url)
- {
- $image = xagio_file_get_contents($xagio_url, FALSE);
- return $image;
+ return wp_remote_retrieve_body($resp);
}
--- a/xagio-seo/modules/settings/page.php
+++ b/xagio-seo/modules/settings/page.php
@@ -2806,6 +2806,9 @@
<figure>
<img class="screenshot" alt="screenshot" src=""/>
</figure>
+
+ <span class="template-platform xagio-flex m-t-10 gap-5"></span>
+
<div class="actions xagio-flex-row xagio-align-center xagio-space-between m-t-20 gap-10">
<!-- add activate, preview buttons -->
<div class="template-name">
--- a/xagio-seo/modules/test/page.php
+++ b/xagio-seo/modules/test/page.php
@@ -1,25 +0,0 @@
-<?php
-/**
- * Type: SUBMENU
- * Page_Title: Developer Test
- * Menu_Title: Developer Test
- * Capability: manage_options
- * Slug: xagio-test
- * Parent_Slug: xagio-dashboard
- * Icon: /assets/img/logo-menu-xagio.webp
- * JavaScript: xagio_tagsinput
- * Css: xagio_animate,xagio_tagsinput
- * Position: 999
- * Version: 1.0.0
- */
-if (!defined('ABSPATH'))
- exit; // Exit if accessed directly
-?>
-<!-- HTML STARTS HERE -->
-<div class="xagio-content-wrapper">
-
- <h1>Developer Testing Page - Ignore</h1>
- <?php
-
- ?>
-</div>
No newline at end of file
--- a/xagio-seo/xagio-seo.php
+++ b/xagio-seo/xagio-seo.php
@@ -3,7 +3,7 @@
* Plugin Name: Xagio SEO - AI Powered SEO
* Plugin URI: https://xagio.net/
* Description: WordPress Management & AI Search Engine Optimization combined. Do everything from a single location with one software.
- * Version: 7.1.0.30
+ * Version: 7.1.0.31
* Author: Xagio
* Author URI: https://xagio.com
* License: GPLv3 or later