206 lines
9.1 KiB
PHP
206 lines
9.1 KiB
PHP
<?php
|
||
require_once($_SERVER['DOCUMENT_ROOT'] . "/config.php");
|
||
|
||
if (!$_SESSION['superadmin']) { die('Доступ запрещён'); }
|
||
|
||
// Карта id → slug (из существующих PHP-заглушек)
|
||
$slugMap = [
|
||
1 => 'poisk-obektov-nedvizhimosti-scanner',
|
||
2 => 'multilistingovaya-sistema-partnerskikh-sdelok',
|
||
3 => 'voronki-prodazh-v-crm',
|
||
4 => 'avtopublikaciya-obyavlenij-na-200-sajtov',
|
||
5 => 'ip-telefoniya-i-kolltraking-v-crm',
|
||
6 => 'dokumentooborot-v-crm-dlja-rieltorov',
|
||
7 => 'baza-kommercheskoj-nedvizhimosti',
|
||
8 => 'kabinet-klienta-v-crm',
|
||
9 => 'odobrjaem-ipoteku-onlajn-broker',
|
||
10 => 'skvoznaja-analitika-v-nedvizhimosti',
|
||
11 => 'reklama-kvartiry-v-socsetyah',
|
||
12 => 'crm-dlja-agenta-reklama-200-sajtov',
|
||
13 => 'poisk-obektov-na-tematicheskih-sajtah',
|
||
14 => 'smart-voronka-prodazh-v-crm',
|
||
15 => 'integraciya-crm-s-ip-telefoniej',
|
||
16 => 'zakaz-svedenij-egrn-iz-crm',
|
||
17 => 'nakoplenie-klientskoj-bazy-targetirovannaya-reklama',
|
||
18 => 'dokumentooborot-rieltora-v-crm',
|
||
19 => 'avtomaticheskij-poisk-v-crm',
|
||
20 => 'upravlenie-tipami-razmeshchenij-obektov',
|
||
21 => 'internet-marketing-v-nedvizhimosti',
|
||
22 => 'kak-reklamirovat-nedvizhimost-na-ploshchadkah',
|
||
23 => 'strategii-prodvizheniya-agentstva-2023',
|
||
24 => 'analiz-rynka-rijeltorskih-uslug',
|
||
25 => 'servisy-po-poisku-nedvizhimosti',
|
||
26 => 'marketing-dlja-zastrojshhikov',
|
||
27 => 'analiz-obektov-nedvizhimosti',
|
||
28 => 'prezentacija-obektov-nedvizhimosti',
|
||
29 => 'parser-nedvizhimosti',
|
||
30 => 'poisk-obektov-nedvizhimosti-v-rezhime-online',
|
||
31 => 'kak-uvelichit-prodazhi-nedvizhimosti',
|
||
32 => 'na-kakih-sajtah-prodavat-nedvizhimost',
|
||
33 => 'avtomatizacija-prodazh-nedvizhimosti',
|
||
34 => 'instrumenty-rijeltora-nedvizhimosti',
|
||
35 => 'lidy-dlja-agentstva-nedvizhimosti',
|
||
36 => 'cel-vnedrenija-crm',
|
||
37 => 'rijeltor-nedvizhimosti-s-nulja',
|
||
38 => 'houmstejdzhing-kvartiry',
|
||
39 => 'prognoz-rynka-nedvizhimosti-2024',
|
||
40 => 'otchet-rijeltora-o-rabote',
|
||
41 => 'baza-novostroek-ot-zastrojshhika',
|
||
42 => 'klienty-na-nedvizhimost',
|
||
43 => 'opredelit-rynochnuju-stoimost-obekta',
|
||
44 => 'avtovygruzka-nedvizhimosti-na-avito',
|
||
45 => 'kak-otkryt-agentstvo-nedvizhimosti',
|
||
46 => 'flipping-v-nedvizhimosti',
|
||
47 => 'cifrovye-tehnologii-v-nedvizhimosti',
|
||
48 => 'cenoobrazovanie-na-rynke-nedvizhimosti',
|
||
49 => 'crm-sdelki-s-nedvizhimostju',
|
||
50 => 'upravlenie-reklamnymi-kampanijami',
|
||
51 => 'marketing-dlja-zastrojshhikov-vazhnost',
|
||
52 => 'srm-dlja-stroitelnoj-kompanii',
|
||
53 => 'kak-rijeltoru-najti-klientov',
|
||
54 => 'sajt-dlja-agentstva-nedvizhimosti',
|
||
55 => 'brend-agentstva-nedvizhimosti',
|
||
56 => 'reklama-dlja-rijeltora',
|
||
57 => 'franshiza-agentstva-nedvizhimosti',
|
||
58 => 'skript-holodnogo-zvonka',
|
||
59 => 'biznes-plan-agentstva-nedvizhimosti',
|
||
60 => 'struktura-agentstva-nedvizhimosti',
|
||
61 => 'oshibki-pri-vnedrenii-crm',
|
||
62 => 'cifrovizacija-nedvizhimosti',
|
||
63 => 'segmentacija-klientov-srm',
|
||
64 => 'utp-dlja-nedvizhimosti',
|
||
65 => 'avtomatizacija-raboty-rieltora',
|
||
66 => 'avtomatizaciya-otdela-prodazh-nedvizhimosti',
|
||
67 => 'otsenka-kvartiry-brokerom',
|
||
68 => 'servis-dlya-analiza-nedvizhimosti',
|
||
];
|
||
|
||
$src = file_get_contents($_SERVER['DOCUMENT_ROOT'] . '/article.php');
|
||
|
||
// Извлекаем скалярные поля метаданных через regex (без eval)
|
||
function extractArtField($src, $id, $field) {
|
||
if (preg_match('/\$art' . $id . '\[\'' . $field . '\'\]\s*=\s*"([^"]*?)"/u', $src, $m)) return $m[1];
|
||
if (preg_match("/\\\$art{$id}\\['{$field}'\\]\\s*=\\s*'([^']*?)'/u", $src, $m)) return $m[1];
|
||
return '';
|
||
}
|
||
|
||
function extractArtAuthorField($src, $id, $field) {
|
||
if (preg_match('/\$art' . $id . '\[\'author\'\]\s*=\s*\[.*?\'' . $field . '\'\s*=>\s*\'([^\']*?)\'/su', $src, $m)) return $m[1];
|
||
if (preg_match('/\$art' . $id . '\[\'author\'\]\s*=\s*\[.*?\'' . $field . '\'\s*=>\s*"([^"]*?)"/su', $src, $m)) return $m[1];
|
||
return '';
|
||
}
|
||
|
||
// Извлекаем HTML-контент из блоков if ($id == X) { ... }
|
||
function extractArtContent($src, $id) {
|
||
if (preg_match('/\$id == ' . $id . '\) \{ \?>(.*?)(?=<\?php \})/su', $src, $m)) {
|
||
return trim($m[1]);
|
||
}
|
||
return '';
|
||
}
|
||
|
||
function convertDate($d) {
|
||
$parts = explode('.', $d);
|
||
if (count($parts) === 3) return $parts[2] . '-' . $parts[1] . '-' . $parts[0];
|
||
return date('Y-m-d');
|
||
}
|
||
|
||
// Генерируем description из первого абзаца статьи (для SEO и карточек блога)
|
||
function generateDescription($content) {
|
||
if (!$content) return '';
|
||
// Берём первый <p> в статье
|
||
if (preg_match('/<p[^>]*>(.*?)<\/p>/s', $content, $m)) {
|
||
$text = strip_tags($m[1]);
|
||
$text = preg_replace('/\s+/u', ' ', $text);
|
||
$text = trim(html_entity_decode($text, ENT_QUOTES, 'UTF-8'));
|
||
// Обрезаем до ~160 символов (стандарт meta description), не разрезая слова
|
||
if (mb_strlen($text, 'UTF-8') > 160) {
|
||
$text = mb_substr($text, 0, 160, 'UTF-8');
|
||
$lastSpace = mb_strrpos($text, ' ', 0, 'UTF-8');
|
||
if ($lastSpace !== false) $text = mb_substr($text, 0, $lastSpace, 'UTF-8');
|
||
$text .= '…';
|
||
}
|
||
return $text;
|
||
}
|
||
return '';
|
||
}
|
||
|
||
// Считаем количество <script> блоков в контенте (для отчёта в dry-run)
|
||
function countScripts($content) {
|
||
return preg_match_all('/<script[\s\S]*?<\/script>/i', $content);
|
||
}
|
||
|
||
$dryRun = isset($_GET['dry']) && $_GET['dry'] == '1';
|
||
$results = [];
|
||
$errors = [];
|
||
|
||
for ($i = 1; $i <= 68; $i++) {
|
||
$title = extractArtField($src, $i, 'title');
|
||
if (!$title) {
|
||
$errors[] = "Нет title для статьи #$i";
|
||
continue;
|
||
}
|
||
|
||
$slug = isset($slugMap[$i]) ? $slugMap[$i] : Article::makeSlug($title);
|
||
$content = extractArtContent($src, $i);
|
||
$dateRaw = extractArtField($src, $i, 'date');
|
||
$updateRaw = extractArtField($src, $i, 'update');
|
||
|
||
$data = [
|
||
'slug' => $slug,
|
||
'title' => $title,
|
||
'page_title' => extractArtField($src, $i, 'pageTitle') ?: $title,
|
||
'description' => generateDescription($content),
|
||
'content' => $content,
|
||
'date_created' => $dateRaw ? convertDate($dateRaw) : date('Y-m-d'),
|
||
'date_updated' => $updateRaw ? convertDate($updateRaw) : date('Y-m-d'),
|
||
'reading_time' => max(1, (int)extractArtField($src, $i, 'time')),
|
||
'image' => extractArtField($src, $i, 'image'),
|
||
'author_name' => extractArtAuthorField($src, $i, 'name') ?: 'Станислав Дмитриев',
|
||
'author_img' => extractArtAuthorField($src, $i, 'img') ?: '/images/articles/rukofodJW.png',
|
||
'author_job' => extractArtAuthorField($src, $i, 'job') ?: 'руководитель JoyWork',
|
||
'published' => 1,
|
||
'sort_order' => $i,
|
||
];
|
||
|
||
$scriptsCount = countScripts($content);
|
||
|
||
if (!$dryRun) {
|
||
if (!Article::slugExists($slug)) {
|
||
Article::create($data);
|
||
$scriptsNote = $scriptsCount > 0 ? " | scripts: {$scriptsCount}" : '';
|
||
$results[] = "✓ #{$i}: {$title}{$scriptsNote}";
|
||
} else {
|
||
$results[] = "— #{$i}: пропущена (slug уже есть): {$slug}";
|
||
}
|
||
} else {
|
||
$contentInfo = strlen($content) ? strlen($content) . " симв." : '❌ пусто';
|
||
$scriptsInfo = $scriptsCount > 0 ? " | scripts: {$scriptsCount}" : '';
|
||
$descrInfo = $data['description'] ? ' | descr: ✓' : ' | descr: ❌';
|
||
$results[] = "[DRY] #{$i}: {$title} → {$slug} | контент: {$contentInfo}{$scriptsInfo}{$descrInfo}";
|
||
}
|
||
}
|
||
|
||
?><!DOCTYPE html>
|
||
<html><head><meta charset="utf-8"><title>Миграция статей</title>
|
||
<style>body{font-family:Arial;padding:20px;max-width:900px;} .ok{color:green;} .err{color:red;} pre{background:#f5f5f5;padding:15px;border-radius:4px;font-size:13px;line-height:1.6;white-space:pre-wrap;}</style>
|
||
</head><body>
|
||
<h2>Миграция статей из PHP в БД</h2>
|
||
<?php if ($dryRun): ?>
|
||
<p style="background:#fff3cd;padding:10px;border-radius:4px;">⚠️ Режим dry-run — данные <strong>не записаны</strong>. Проверьте результаты и <a href="?">запустите реально</a>.</p>
|
||
<?php else: ?>
|
||
<p><a href="?dry=1">Dry-run (без записи)</a> | <a href="/admin/articles.php">← К списку статей</a></p>
|
||
<?php endif; ?>
|
||
|
||
<h3>Результат (<?= count($results) ?> / 68):</h3>
|
||
<pre><?php foreach ($results as $r) echo htmlspecialchars($r) . "\n"; ?></pre>
|
||
|
||
<?php if ($errors): ?>
|
||
<h3 class="err">Ошибки (<?= count($errors) ?>):</h3>
|
||
<pre class="err"><?php foreach ($errors as $e) echo htmlspecialchars($e) . "\n"; ?></pre>
|
||
<?php endif; ?>
|
||
|
||
<?php if (!$dryRun && empty($errors) && count($results) > 0): ?>
|
||
<p class="ok" style="font-size:16px;"><strong>✓ Готово! <a href="/admin/articles.php">Перейти к статьям →</a></strong></p>
|
||
<?php endif; ?>
|
||
</body></html>
|