198 lines
7.1 KiB
PHP
198 lines
7.1 KiB
PHP
<?php
|
|
/**
|
|
* fix_joomla_links.php
|
|
*
|
|
* Replaces Joomla internal links in WordPress post_content with correct WP URLs.
|
|
*
|
|
* Handles:
|
|
* 1. index.php?option=com_content&view=article&id=NNN → jos_content ID → WP post_name
|
|
* 2. es/.../NNN-slug.html (relative) → K2 item ID → WP post_name
|
|
* 3. http://feadulta.com/es/.../NNN-slug.html → K2 item ID → WP post_name
|
|
* 4. https://farmer.taild3aaf6.ts.net/fea/es/.../NNN-slug.html → K2 ID → WP post_name
|
|
*
|
|
* Usage: php fix_joomla_links.php [--dry-run]
|
|
*/
|
|
|
|
$dry_run = in_array('--dry-run', $argv ?? []);
|
|
|
|
// DB config
|
|
$db_host = 'wordpress-mysql';
|
|
$db_name = 'wordpress_db';
|
|
$db_user = 'wordpress_user';
|
|
$db_pass = 'wordpress_pass';
|
|
$wp_site_url = 'https://farmer.taild3aaf6.ts.net/fea';
|
|
|
|
$pdo = new PDO("mysql:host=$db_host;dbname=$db_name;charset=utf8mb4", $db_user, $db_pass, [
|
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
|
]);
|
|
|
|
echo "=== Fix Joomla Internal Links ===\n";
|
|
echo $dry_run ? "[DRY RUN - no changes will be saved]\n\n" : "[LIVE RUN - changes will be saved]\n\n";
|
|
|
|
// -------------------------------------------------------------------------
|
|
// Step 1: Build lookup maps from wp_postmeta
|
|
// -------------------------------------------------------------------------
|
|
echo "Building lookup maps from wp_postmeta...\n";
|
|
|
|
// Map: K2 item ID → WP post_name
|
|
$k2_map = [];
|
|
$stmt = $pdo->query("
|
|
SELECT pm.meta_value AS k2_id, p.post_name
|
|
FROM wp_postmeta pm
|
|
JOIN wp_posts p ON pm.post_id = p.ID
|
|
WHERE pm.meta_key = '_fgj2wp_old_k2_id'
|
|
AND p.post_status IN ('publish', 'draft')
|
|
AND p.post_type = 'post'
|
|
AND p.post_name != ''
|
|
");
|
|
foreach ($stmt as $row) {
|
|
$k2_map[(int)$row['k2_id']] = $row['post_name'];
|
|
}
|
|
echo " K2 map: " . count($k2_map) . " entries\n";
|
|
|
|
// Map: jos_content ID → WP post_name
|
|
$joomla_map = [];
|
|
$stmt = $pdo->query("
|
|
SELECT pm.meta_value AS joomla_id, p.post_name
|
|
FROM wp_postmeta pm
|
|
JOIN wp_posts p ON pm.post_id = p.ID
|
|
WHERE pm.meta_key = '_fgj2wp_old_id'
|
|
AND p.post_status IN ('publish', 'draft')
|
|
AND p.post_type = 'post'
|
|
AND p.post_name != ''
|
|
");
|
|
foreach ($stmt as $row) {
|
|
$joomla_map[(int)$row['joomla_id']] = $row['post_name'];
|
|
}
|
|
echo " jos_content map: " . count($joomla_map) . " entries\n\n";
|
|
|
|
// -------------------------------------------------------------------------
|
|
// Step 2: Fetch posts with Joomla links
|
|
// -------------------------------------------------------------------------
|
|
$stmt = $pdo->query("
|
|
SELECT ID, post_title, post_content
|
|
FROM wp_posts
|
|
WHERE post_type = 'post'
|
|
AND post_status IN ('publish', 'draft')
|
|
AND (
|
|
post_content LIKE '%index.php?option=%'
|
|
OR post_content LIKE '%\"es/%'
|
|
OR post_content LIKE '%/es/%'
|
|
OR post_content LIKE '%feadulta.com%'
|
|
OR post_content LIKE '%farmer.taild3aaf6%'
|
|
)
|
|
");
|
|
$posts = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
echo "Posts to process: " . count($posts) . "\n\n";
|
|
|
|
// -------------------------------------------------------------------------
|
|
// Step 3: Process each post
|
|
// -------------------------------------------------------------------------
|
|
$stats = [
|
|
'posts_changed' => 0,
|
|
'posts_skipped' => 0,
|
|
'links_replaced' => 0,
|
|
'links_not_found'=> 0,
|
|
];
|
|
$not_found_log = [];
|
|
|
|
// Regex patterns (note: href values may be HTML-entity encoded: & → &)
|
|
$patterns = [
|
|
|
|
// Pattern A: index.php?option=com_content&[amp;]view=article&[amp;]id=NNN[;alias]
|
|
'joomla_content' => '/href="index\.php\?option=com_content(?:&(?:amp;)?)[^"]*?(?:&(?:amp;)?)id=(\d+)[^"]*"/i',
|
|
|
|
// Pattern B: K2 item links — only on known Joomla-origin domains/paths
|
|
// Matches:
|
|
// href="es/[path/]NNN-slug.html" (relative)
|
|
// href="http://feadulta.com/[path/]NNN-slug.html" (old domain)
|
|
// href="https://farmer.taild3aaf6.ts.net/fea/[path/]NNN-slug.html" (staging domain)
|
|
'k2_item' => '/href="(?:(?:https?:\/\/feadulta\.com|https?:\/\/farmer\.taild3aaf6\.ts\.net\/fea)\/)?es\/[^"]*?\/(\d+)-[^"\/]+\.html[^"]*"/i',
|
|
];
|
|
|
|
$update_stmt = $pdo->prepare("UPDATE wp_posts SET post_content = ? WHERE ID = ?");
|
|
|
|
foreach ($posts as $post) {
|
|
$original = $post['post_content'];
|
|
$content = $original;
|
|
$changed = false;
|
|
|
|
// --- Pattern A: jos_content links ---
|
|
$content = preg_replace_callback(
|
|
$patterns['joomla_content'],
|
|
function ($m) use ($joomla_map, $wp_site_url, &$stats, &$not_found_log, $post) {
|
|
$id = (int)$m[1];
|
|
if (isset($joomla_map[$id])) {
|
|
$stats['links_replaced']++;
|
|
$new_url = $wp_site_url . '/' . $joomla_map[$id] . '/';
|
|
return 'href="' . $new_url . '"';
|
|
}
|
|
$stats['links_not_found']++;
|
|
$not_found_log[] = "jos_content ID=$id not found (post {$post['ID']}: {$post['post_title']})";
|
|
return $m[0]; // keep original
|
|
},
|
|
$content
|
|
);
|
|
|
|
// --- Pattern B: K2 item links ---
|
|
$content = preg_replace_callback(
|
|
$patterns['k2_item'],
|
|
function ($m) use ($k2_map, $wp_site_url, &$stats, &$not_found_log, $post) {
|
|
$id = (int)$m[1];
|
|
|
|
// Skip if ID 0, or if this looks like a year (4 digits in 1900-2100 range) in a date URL
|
|
if ($id === 0) return $m[0];
|
|
|
|
// Skip pure numbers that are years in date-based URLs (e.g. /2024/01/post.html)
|
|
// We check: if the full match contains /YYYY/ before the filename, skip
|
|
if ($id >= 1990 && $id <= 2100 && preg_match('/\/\d{4}\//', $m[0])) {
|
|
return $m[0];
|
|
}
|
|
|
|
if (isset($k2_map[$id])) {
|
|
$stats['links_replaced']++;
|
|
$new_url = $wp_site_url . '/' . $k2_map[$id] . '/';
|
|
return 'href="' . $new_url . '"';
|
|
}
|
|
|
|
$stats['links_not_found']++;
|
|
$not_found_log[] = "K2 ID=$id not found in map (post {$post['ID']}: {$post['post_title']}) | original: " . substr($m[0], 0, 100);
|
|
return $m[0]; // keep original
|
|
},
|
|
$content
|
|
);
|
|
|
|
if ($content !== $original) {
|
|
$changed = true;
|
|
$stats['posts_changed']++;
|
|
if (!$dry_run) {
|
|
$update_stmt->execute([$content, $post['ID']]);
|
|
} else {
|
|
echo " [DRY] Would update post {$post['ID']}: {$post['post_title']}\n";
|
|
}
|
|
} else {
|
|
$stats['posts_skipped']++;
|
|
}
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// Step 4: Summary
|
|
// -------------------------------------------------------------------------
|
|
echo "\n=== Results ===\n";
|
|
echo "Posts changed: {$stats['posts_changed']}\n";
|
|
echo "Posts unchanged: {$stats['posts_skipped']}\n";
|
|
echo "Links replaced: {$stats['links_replaced']}\n";
|
|
echo "Links not resolved: {$stats['links_not_found']}\n";
|
|
|
|
if (!empty($not_found_log)) {
|
|
$log_path = '/tmp/fix_joomla_links_unresolved.log';
|
|
file_put_contents($log_path, implode("\n", $not_found_log) . "\n");
|
|
echo "\nUnresolved links logged to: $log_path\n";
|
|
echo "First 10 unresolved:\n";
|
|
foreach (array_slice($not_found_log, 0, 10) as $line) {
|
|
echo " $line\n";
|
|
}
|
|
}
|
|
|
|
echo "\nDone.\n";
|