432 lines
16 KiB
PHP
432 lines
16 KiB
PHP
|
|
<?php
|
|||
|
|
/**
|
|||
|
|
* Migrationsskript: Wandelt inline Base64-Bilder in
|
|||
|
|
* knowledgecenter_post_versions.content in echte Dateien um.
|
|||
|
|
*
|
|||
|
|
* CLI-only. Idempotent. Per-Version-Transaktion mit Backup-Tabelle.
|
|||
|
|
*
|
|||
|
|
* Aufruf:
|
|||
|
|
* php migrate_base64_to_files.php --dry-run --post-id=417 --verbose
|
|||
|
|
* php migrate_base64_to_files.php --apply --post-id=417
|
|||
|
|
* php migrate_base64_to_files.php --apply
|
|||
|
|
*
|
|||
|
|
* Optionen:
|
|||
|
|
* --dry-run Standard. Nur analysieren, nichts schreiben.
|
|||
|
|
* --apply Wirklich migrieren (Dateien + DB).
|
|||
|
|
* --post-id=N Nur Versionen dieses Posts.
|
|||
|
|
* --version-id=N Genau diese Version.
|
|||
|
|
* --limit=N Maximal N Versionen verarbeiten.
|
|||
|
|
* --verbose Mehr Details ins Log.
|
|||
|
|
* --help Diese Hilfe.
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
if (PHP_SAPI !== 'cli') {
|
|||
|
|
fwrite(STDERR, "Dieses Skript ist CLI-only.\n");
|
|||
|
|
exit(2);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$currDir = __DIR__;
|
|||
|
|
$rootDir = dirname(dirname(dirname($currDir)));
|
|||
|
|
$mysydeDir = $rootDir . '/mysyde';
|
|||
|
|
|
|||
|
|
require_once($rootDir . '/vendor/autoload.php');
|
|||
|
|
|
|||
|
|
$envDir = $rootDir . '/config';
|
|||
|
|
$envFilePath = $envDir . '/.env';
|
|||
|
|
if (is_dir($envDir) && file_exists($envFilePath) && is_file($envFilePath) && is_readable($envFilePath)) {
|
|||
|
|
$dotenv = new \Dotenv\Dotenv($envDir);
|
|||
|
|
$dotenv->load();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
require_once($mysydeDir . '/common/common_functions.inc.php');
|
|||
|
|
$connEnv = (local_environment()) ? $mysydeDir . '/dc.config.php' : $mysydeDir . '/dc-server.config.php';
|
|||
|
|
require_once($connEnv);
|
|||
|
|
db_connect();
|
|||
|
|
|
|||
|
|
// ------- Args -------
|
|||
|
|
$opts = [
|
|||
|
|
'dry-run' => true,
|
|||
|
|
'apply' => false,
|
|||
|
|
'post-id' => null,
|
|||
|
|
'version-id' => null,
|
|||
|
|
'limit' => null,
|
|||
|
|
'verbose' => false,
|
|||
|
|
];
|
|||
|
|
foreach (array_slice($argv, 1) as $arg) {
|
|||
|
|
if ($arg === '--help' || $arg === '-h') {
|
|||
|
|
echo "Usage: php migrate_base64_to_files.php [--dry-run|--apply] [--post-id=N] [--version-id=N] [--limit=N] [--verbose]\n";
|
|||
|
|
exit(0);
|
|||
|
|
}
|
|||
|
|
if ($arg === '--apply') { $opts['apply'] = true; $opts['dry-run'] = false; continue; }
|
|||
|
|
if ($arg === '--dry-run') { $opts['dry-run'] = true; $opts['apply'] = false; continue; }
|
|||
|
|
if ($arg === '--verbose') { $opts['verbose'] = true; continue; }
|
|||
|
|
if (preg_match('/^--post-id=(\d+)$/', $arg, $m)) { $opts['post-id'] = (int) $m[1]; continue; }
|
|||
|
|
if (preg_match('/^--version-id=(\d+)$/', $arg, $m)) { $opts['version-id'] = (int) $m[1]; continue; }
|
|||
|
|
if (preg_match('/^--limit=(\d+)$/', $arg, $m)) { $opts['limit'] = (int) $m[1]; continue; }
|
|||
|
|
fwrite(STDERR, "Unbekannte Option: $arg\n");
|
|||
|
|
exit(2);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$dryRun = !$opts['apply'];
|
|||
|
|
$verbose = $opts['verbose'];
|
|||
|
|
$runId = bin2hex(random_bytes(8));
|
|||
|
|
|
|||
|
|
// ------- Logging -------
|
|||
|
|
$logDir = $rootDir . '/userdata/knowledgecenter_update/migrations';
|
|||
|
|
if (!is_dir($logDir) && !@mkdir($logDir, 0775, true)) {
|
|||
|
|
fwrite(STDERR, "Log-Verzeichnis nicht anlegbar: $logDir\n");
|
|||
|
|
exit(3);
|
|||
|
|
}
|
|||
|
|
$logFile = $logDir . '/base64_to_files_' . gmdate('Ymd_His') . '_' . $runId . '.log';
|
|||
|
|
$logFp = fopen($logFile, 'w');
|
|||
|
|
if (!$logFp) {
|
|||
|
|
fwrite(STDERR, "Log-Datei nicht öffenbar: $logFile\n");
|
|||
|
|
exit(3);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function logmsg($msg, $level = 'INFO') {
|
|||
|
|
global $logFp, $verbose;
|
|||
|
|
$line = '[' . gmdate('Y-m-d H:i:s') . " UTC] [$level] $msg\n";
|
|||
|
|
fwrite($logFp, $line);
|
|||
|
|
if ($verbose || in_array($level, ['ERROR', 'FAIL', 'OK', 'WARN', 'HEAD'], true)) {
|
|||
|
|
fwrite(STDOUT, $line);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
logmsg("Migration-Run gestartet. run_id=$runId mode=" . ($dryRun ? 'DRY-RUN' : 'APPLY')
|
|||
|
|
. " post-id=" . ($opts['post-id'] ?? 'ALL')
|
|||
|
|
. " version-id=" . ($opts['version-id'] ?? 'ALL')
|
|||
|
|
. " limit=" . ($opts['limit'] ?? 'unbeschränkt'), 'HEAD');
|
|||
|
|
|
|||
|
|
// ------- Backup-Tabelle anlegen -------
|
|||
|
|
$createBackup = "CREATE TABLE IF NOT EXISTS knowledgecenter_post_versions_migration_backup (
|
|||
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|||
|
|
version_id INT NOT NULL,
|
|||
|
|
post_id INT NOT NULL,
|
|||
|
|
language_id INT NOT NULL,
|
|||
|
|
version_number INT NOT NULL,
|
|||
|
|
original_content LONGTEXT NOT NULL,
|
|||
|
|
original_length INT NOT NULL,
|
|||
|
|
images_extracted INT NOT NULL,
|
|||
|
|
migrated_at DATETIME NOT NULL,
|
|||
|
|
script_run_id VARCHAR(32) NOT NULL,
|
|||
|
|
UNIQUE KEY uniq_version_run (version_id, script_run_id),
|
|||
|
|
KEY idx_run (script_run_id),
|
|||
|
|
KEY idx_post (post_id)
|
|||
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4";
|
|||
|
|
if (!mysqli_query($GLOBALS['mysql_con'], $createBackup)) {
|
|||
|
|
logmsg('Backup-Tabelle konnte nicht angelegt werden: ' . mysqli_error($GLOBALS['mysql_con']), 'ERROR');
|
|||
|
|
exit(4);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ------- Kandidaten finden -------
|
|||
|
|
$where = ["content LIKE '%data:image/%;base64,%'"];
|
|||
|
|
if ($opts['post-id'] !== null) $where[] = "post_id = " . (int) $opts['post-id'];
|
|||
|
|
if ($opts['version-id'] !== null) $where[] = "id = " . (int) $opts['version-id'];
|
|||
|
|
$sql = "SELECT id, post_id, language_id, version_number, content
|
|||
|
|
FROM knowledgecenter_post_versions
|
|||
|
|
WHERE " . implode(' AND ', $where) . "
|
|||
|
|
ORDER BY post_id, language_id, version_number";
|
|||
|
|
if ($opts['limit'] !== null) $sql .= " LIMIT " . (int) $opts['limit'];
|
|||
|
|
|
|||
|
|
$res = mysqli_query($GLOBALS['mysql_con'], $sql);
|
|||
|
|
if (!$res) {
|
|||
|
|
logmsg('Query fehlgeschlagen: ' . mysqli_error($GLOBALS['mysql_con']), 'ERROR');
|
|||
|
|
exit(5);
|
|||
|
|
}
|
|||
|
|
$candidateCount = mysqli_num_rows($res);
|
|||
|
|
logmsg("Gefundene Kandidaten: $candidateCount", 'HEAD');
|
|||
|
|
|
|||
|
|
// ------- Helper -------
|
|||
|
|
function mime_to_ext($mime) {
|
|||
|
|
static $map = [
|
|||
|
|
'image/jpeg' => 'jpg',
|
|||
|
|
'image/png' => 'png',
|
|||
|
|
'image/gif' => 'gif',
|
|||
|
|
'image/webp' => 'webp',
|
|||
|
|
];
|
|||
|
|
return isset($map[$mime]) ? $map[$mime] : null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function ensure_post_inline_dir($rootDir, $postId, $dryRun) {
|
|||
|
|
$hash = substr(sha1('kc_post_' . (int) $postId), 0, 24);
|
|||
|
|
$relBase = 'knowledgecenter_update/posts/' . $hash;
|
|||
|
|
$absBase = $rootDir . '/userdata/' . $relBase;
|
|||
|
|
$absInline = $absBase . '/inline';
|
|||
|
|
if ($dryRun) {
|
|||
|
|
return ['hash' => $hash, 'relBase' => $relBase, 'absBase' => $absBase, 'absInline' => $absInline, 'created' => false];
|
|||
|
|
}
|
|||
|
|
if (!is_dir($absBase) && !@mkdir($absBase, 0775, true)) {
|
|||
|
|
throw new RuntimeException("Post-Ordner nicht anlegbar: $absBase");
|
|||
|
|
}
|
|||
|
|
$sharedLink = $absBase . '/shared';
|
|||
|
|
if (!is_link($sharedLink) && !file_exists($sharedLink)) {
|
|||
|
|
@symlink('../../shared', $sharedLink);
|
|||
|
|
}
|
|||
|
|
if (!is_dir($absInline) && !@mkdir($absInline, 0775, true)) {
|
|||
|
|
throw new RuntimeException("Inline-Ordner nicht anlegbar: $absInline");
|
|||
|
|
}
|
|||
|
|
return ['hash' => $hash, 'relBase' => $relBase, 'absBase' => $absBase, 'absInline' => $absInline, 'created' => true];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function process_version($row, $rootDir, $dryRun, $runId, $verbose) {
|
|||
|
|
$versionId = (int) $row['id'];
|
|||
|
|
$postId = (int) $row['post_id'];
|
|||
|
|
$langId = (int) $row['language_id'];
|
|||
|
|
$verNum = (int) $row['version_number'];
|
|||
|
|
$content = (string) $row['content'];
|
|||
|
|
$origLen = strlen($content);
|
|||
|
|
|
|||
|
|
$tag = "version_id=$versionId post_id=$postId lang=$langId v=$verNum";
|
|||
|
|
|
|||
|
|
// DOM laden
|
|||
|
|
$wrapped = '<?xml encoding="UTF-8"?><div id="kc_root">' . $content . '</div>';
|
|||
|
|
$prev = libxml_use_internal_errors(true);
|
|||
|
|
$dom = new DOMDocument();
|
|||
|
|
$loaded = $dom->loadHTML($wrapped, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NOERROR | LIBXML_NOWARNING);
|
|||
|
|
libxml_clear_errors();
|
|||
|
|
libxml_use_internal_errors($prev);
|
|||
|
|
if (!$loaded) {
|
|||
|
|
logmsg("[FAIL] $tag DOM load fehlgeschlagen", 'FAIL');
|
|||
|
|
return ['ok' => false, 'rewrote' => 0];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$root = $dom->getElementById('kc_root');
|
|||
|
|
if (!$root) {
|
|||
|
|
logmsg("[FAIL] $tag wrapper nicht gefunden", 'FAIL');
|
|||
|
|
return ['ok' => false, 'rewrote' => 0];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$imgs = $dom->getElementsByTagName('img');
|
|||
|
|
$imgList = [];
|
|||
|
|
foreach ($imgs as $img) { $imgList[] = $img; }
|
|||
|
|
|
|||
|
|
$rewrote = 0;
|
|||
|
|
$skippedNoBase64 = 0;
|
|||
|
|
$skippedBadDecode = 0;
|
|||
|
|
$skippedBadMime = 0;
|
|||
|
|
$skippedTooBig = 0;
|
|||
|
|
$skippedSrcset = 0;
|
|||
|
|
$totalBytesIn = 0;
|
|||
|
|
|
|||
|
|
foreach ($imgList as $img) {
|
|||
|
|
if ($img->hasAttribute('srcset')) {
|
|||
|
|
$skippedSrcset++;
|
|||
|
|
logmsg("[WARN] $tag <img srcset=...> belassen", 'WARN');
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
$src = $img->getAttribute('src');
|
|||
|
|
if ($src === '' || strncmp($src, 'data:', 5) !== 0) continue;
|
|||
|
|
|
|||
|
|
if (!preg_match('#^data:(image/[a-z0-9.+-]+)?(;charset=[^;,]+)?(;base64)?,(.*)$#is', $src, $m)) {
|
|||
|
|
$skippedNoBase64++;
|
|||
|
|
logmsg("[WARN] $tag data-URI nicht parsbar, belassen", 'WARN');
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
$declaredMime = strtolower($m[1] ?? '');
|
|||
|
|
$isBase64 = !empty($m[3]);
|
|||
|
|
$payload = $m[4];
|
|||
|
|
|
|||
|
|
if (!$isBase64) {
|
|||
|
|
$skippedNoBase64++;
|
|||
|
|
logmsg("[WARN] $tag data-URI ohne ;base64, belassen", 'WARN');
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
$decoded = base64_decode($payload, true);
|
|||
|
|
if ($decoded === false || $decoded === '') {
|
|||
|
|
$skippedBadDecode++;
|
|||
|
|
logmsg("[WARN] $tag base64 nicht dekodierbar, belassen", 'WARN');
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$size = strlen($decoded);
|
|||
|
|
$totalBytesIn += $size;
|
|||
|
|
if ($size > 5 * 1024 * 1024) {
|
|||
|
|
$skippedTooBig++;
|
|||
|
|
logmsg("[WARN] $tag Bild > 5MB ($size bytes), belassen", 'WARN');
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
|||
|
|
$detectedMime = $finfo ? finfo_buffer($finfo, $decoded) : false;
|
|||
|
|
if ($finfo) finfo_close($finfo);
|
|||
|
|
$ext = mime_to_ext($detectedMime);
|
|||
|
|
if (!$ext) {
|
|||
|
|
$skippedBadMime++;
|
|||
|
|
logmsg("[WARN] $tag Mime '$detectedMime' nicht erlaubt (declared='$declaredMime'), belassen", 'WARN');
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$dir = ensure_post_inline_dir($rootDir, $postId, $dryRun);
|
|||
|
|
$sha = hash('sha256', $decoded);
|
|||
|
|
$filename = $sha . '.' . $ext;
|
|||
|
|
$finalPath = $dir['absInline'] . '/' . $filename;
|
|||
|
|
$publicUrl = '/userdata/' . $dir['relBase'] . '/inline/' . $filename;
|
|||
|
|
|
|||
|
|
if ($dryRun) {
|
|||
|
|
if ($verbose) logmsg("[DRY ] $tag would write $finalPath ($size bytes) → $publicUrl", 'INFO');
|
|||
|
|
} else {
|
|||
|
|
$writeNeeded = true;
|
|||
|
|
if (file_exists($finalPath)) {
|
|||
|
|
$existingSize = filesize($finalPath);
|
|||
|
|
if ($existingSize === $size) {
|
|||
|
|
$writeNeeded = false;
|
|||
|
|
if ($verbose) logmsg("[INFO] $tag dedup hit $finalPath", 'INFO');
|
|||
|
|
} else {
|
|||
|
|
logmsg("[WARN] $tag SHA-Kollision?! $finalPath bestehend=$existingSize bytes, neu=$size bytes – überspringe", 'WARN');
|
|||
|
|
$writeNeeded = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if ($writeNeeded) {
|
|||
|
|
$tmp = $dir['absInline'] . '/.tmp_' . bin2hex(random_bytes(6));
|
|||
|
|
if (file_put_contents($tmp, $decoded) === false) {
|
|||
|
|
logmsg("[FAIL] $tag konnte tmp-Datei nicht schreiben", 'FAIL');
|
|||
|
|
return ['ok' => false, 'rewrote' => $rewrote];
|
|||
|
|
}
|
|||
|
|
@chmod($tmp, 0664);
|
|||
|
|
if (!rename($tmp, $finalPath)) {
|
|||
|
|
@unlink($tmp);
|
|||
|
|
logmsg("[FAIL] $tag rename failed", 'FAIL');
|
|||
|
|
return ['ok' => false, 'rewrote' => $rewrote];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$img->setAttribute('src', $publicUrl);
|
|||
|
|
$rewrote++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Serialisieren
|
|||
|
|
$newContent = '';
|
|||
|
|
foreach ($root->childNodes as $child) {
|
|||
|
|
$newContent .= $dom->saveHTML($child);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$newLen = strlen($newContent);
|
|||
|
|
$report = sprintf(
|
|||
|
|
"%s images_rewritten=%d skipped(no_b64=%d bad_decode=%d bad_mime=%d too_big=%d srcset=%d) from_kb=%d to_kb=%d",
|
|||
|
|
$tag, $rewrote, $skippedNoBase64, $skippedBadDecode, $skippedBadMime, $skippedTooBig, $skippedSrcset,
|
|||
|
|
(int) round($origLen / 1024), (int) round($newLen / 1024)
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
return [
|
|||
|
|
'ok' => true,
|
|||
|
|
'rewrote' => $rewrote,
|
|||
|
|
'newContent'=> $newContent,
|
|||
|
|
'origLen' => $origLen,
|
|||
|
|
'newLen' => $newLen,
|
|||
|
|
'report' => $report,
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ------- Verarbeitung -------
|
|||
|
|
$totals = ['ok' => 0, 'rewritten_images' => 0, 'fail' => 0, 'no_change' => 0, 'bytes_saved' => 0];
|
|||
|
|
|
|||
|
|
while ($row = mysqli_fetch_assoc($res)) {
|
|||
|
|
$versionId = (int) $row['id'];
|
|||
|
|
$postId = (int) $row['post_id'];
|
|||
|
|
|
|||
|
|
if (!$dryRun) mysqli_query($GLOBALS['mysql_con'], 'START TRANSACTION');
|
|||
|
|
try {
|
|||
|
|
$result = process_version($row, $rootDir, $dryRun, $runId, $verbose);
|
|||
|
|
} catch (Throwable $e) {
|
|||
|
|
if (!$dryRun) mysqli_query($GLOBALS['mysql_con'], 'ROLLBACK');
|
|||
|
|
logmsg("[FAIL] version_id=$versionId Exception: " . $e->getMessage(), 'FAIL');
|
|||
|
|
$totals['fail']++;
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
if (!$result['ok']) {
|
|||
|
|
if (!$dryRun) mysqli_query($GLOBALS['mysql_con'], 'ROLLBACK');
|
|||
|
|
$totals['fail']++;
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ($result['rewrote'] === 0) {
|
|||
|
|
if (!$dryRun) mysqli_query($GLOBALS['mysql_con'], 'ROLLBACK');
|
|||
|
|
logmsg("[SKIP] " . $result['report'] . " (kein <img> umgeschrieben)", 'WARN');
|
|||
|
|
$totals['no_change']++;
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ($dryRun) {
|
|||
|
|
logmsg("[DRY ] " . $result['report'], 'OK');
|
|||
|
|
$totals['ok']++;
|
|||
|
|
$totals['rewritten_images'] += $result['rewrote'];
|
|||
|
|
$totals['bytes_saved'] += max(0, $result['origLen'] - $result['newLen']);
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Backup + UPDATE
|
|||
|
|
$stmt = mysqli_prepare(
|
|||
|
|
$GLOBALS['mysql_con'],
|
|||
|
|
"INSERT INTO knowledgecenter_post_versions_migration_backup
|
|||
|
|
(version_id, post_id, language_id, version_number, original_content, original_length, images_extracted, migrated_at, script_run_id)
|
|||
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, NOW(), ?)"
|
|||
|
|
);
|
|||
|
|
if (!$stmt) {
|
|||
|
|
mysqli_query($GLOBALS['mysql_con'], 'ROLLBACK');
|
|||
|
|
logmsg("[FAIL] version_id=$versionId backup-prepare: " . mysqli_error($GLOBALS['mysql_con']), 'FAIL');
|
|||
|
|
$totals['fail']++;
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
$verId = (int) $row['id'];
|
|||
|
|
$pId = (int) $row['post_id'];
|
|||
|
|
$lId = (int) $row['language_id'];
|
|||
|
|
$vNum = (int) $row['version_number'];
|
|||
|
|
$origCt = (string) $row['content'];
|
|||
|
|
$origLen = $result['origLen'];
|
|||
|
|
$rewrote = $result['rewrote'];
|
|||
|
|
mysqli_stmt_bind_param($stmt, 'iiiisiis', $verId, $pId, $lId, $vNum, $origCt, $origLen, $rewrote, $runId);
|
|||
|
|
if (!mysqli_stmt_execute($stmt)) {
|
|||
|
|
mysqli_query($GLOBALS['mysql_con'], 'ROLLBACK');
|
|||
|
|
logmsg("[FAIL] version_id=$versionId backup-insert: " . mysqli_stmt_error($stmt), 'FAIL');
|
|||
|
|
mysqli_stmt_close($stmt);
|
|||
|
|
$totals['fail']++;
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
mysqli_stmt_close($stmt);
|
|||
|
|
|
|||
|
|
$stmt2 = mysqli_prepare(
|
|||
|
|
$GLOBALS['mysql_con'],
|
|||
|
|
"UPDATE knowledgecenter_post_versions SET content = ? WHERE id = ?"
|
|||
|
|
);
|
|||
|
|
if (!$stmt2) {
|
|||
|
|
mysqli_query($GLOBALS['mysql_con'], 'ROLLBACK');
|
|||
|
|
logmsg("[FAIL] version_id=$versionId update-prepare: " . mysqli_error($GLOBALS['mysql_con']), 'FAIL');
|
|||
|
|
$totals['fail']++;
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
$newCt = $result['newContent'];
|
|||
|
|
mysqli_stmt_bind_param($stmt2, 'si', $newCt, $verId);
|
|||
|
|
if (!mysqli_stmt_execute($stmt2)) {
|
|||
|
|
mysqli_query($GLOBALS['mysql_con'], 'ROLLBACK');
|
|||
|
|
logmsg("[FAIL] version_id=$versionId update-exec: " . mysqli_stmt_error($stmt2), 'FAIL');
|
|||
|
|
mysqli_stmt_close($stmt2);
|
|||
|
|
$totals['fail']++;
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
mysqli_stmt_close($stmt2);
|
|||
|
|
|
|||
|
|
if (!mysqli_query($GLOBALS['mysql_con'], 'COMMIT')) {
|
|||
|
|
logmsg("[FAIL] version_id=$versionId COMMIT-Fehler: " . mysqli_error($GLOBALS['mysql_con']), 'FAIL');
|
|||
|
|
$totals['fail']++;
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
logmsg("[OK ] " . $result['report'], 'OK');
|
|||
|
|
$totals['ok']++;
|
|||
|
|
$totals['rewritten_images'] += $result['rewrote'];
|
|||
|
|
$totals['bytes_saved'] += max(0, $result['origLen'] - $result['newLen']);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
logmsg(sprintf(
|
|||
|
|
"Fertig. ok=%d fail=%d no_change=%d rewritten_images=%d bytes_saved=%d (%.2f MB) mode=%s",
|
|||
|
|
$totals['ok'], $totals['fail'], $totals['no_change'], $totals['rewritten_images'],
|
|||
|
|
$totals['bytes_saved'], $totals['bytes_saved'] / 1024 / 1024,
|
|||
|
|
$dryRun ? 'DRY-RUN' : 'APPLY'
|
|||
|
|
), 'HEAD');
|
|||
|
|
logmsg("Log-Datei: $logFile", 'HEAD');
|
|||
|
|
|
|||
|
|
fclose($logFp);
|
|||
|
|
exit($totals['fail'] > 0 ? 1 : 0);
|