Avvertenza importante
Migrare un forum da phpBB a Flarum è un’operazione delicata. Prima di fare qualsiasi prova è essenziale eseguire un backup completo del database e dei file del vecchio forum.
Questa guida nasce da un’esperienza pratica su Altervista e vuole offrire una procedura concreta per chi, come me, non dispone di una shell SSH e preferisce lavorare via FTP e phpMyAdmin. Non è però una guida “universale”: phpBB e Flarum hanno strutture diverse, e quindi alcune installazioni potrebbero richiedere adattamenti.
Lo script qui sotto consente di migrare:
utenti
discussioni
post
tag
trasformando i forum e sottoforum di phpBB in tag di Flarum.
Va però precisato subito che:
le password non vengono migrate come credenziali già utilizzabili, quindi gli utenti dovranno normalmente reimpostarle;
il BBCode complesso potrebbe non essere convertito in maniera perfetta;
gruppi e permessi non vengono replicati integralmente.
In altre parole: questa guida è pensata come soluzione pratica e ragionevole per una migrazione di base, non come migrazione perfetta in ogni dettaglio.
Occorrente
Per seguire questa procedura servono:
- un forum phpBB già esistente;
- una nuova installazione pulita di Flarum;
- accesso al database tramite phpMyAdmin;
- accesso FTP, ad esempio con Cyberduck;
- l’estensione Tags attiva in Flarum. (Di default lo è ma sempre meglio controllare)
L’idea è semplice: phpBB organizza i contenuti tramite forum e sottoforum, mentre Flarum lavora con discussioni e tag. Di conseguenza, lo script crea i tag partendo dalla struttura dei forum phpBB e collega poi ogni discussione al tag corrispondente.
Prima di eseguire lo script
Prima di lanciare la migrazione verifica con attenzione questi punti:
Flarum deve essere già installato e funzionante.
Devi riuscire ad accedere al pannello amministrativo di Flarum.
L’estensione Tags deve essere attivata.
Devi conoscere il prefisso delle tabelle phpBB, ad esempio phpbb_.
Devi conoscere il prefisso delle tabelle Flarum, ad esempio flarum_.
È importante usare i prefissi completi con underscore finale.
Ad esempio:
$pref_p = 'phpbb_';
$pref_f = 'flarum_';
e non
$pref_p = 'phpbb';
$pref_f = 'flarum';
altrimenti si rischia di generare nomi errati come phpbbusers invece di phpbb_users.
Come usare lo script
Lo script non è pronto all’uso senza modifiche.
Prima di caricarlo sul server devi aprirlo e personalizzare la sezione iniziale con:
host database;
nome utente database;
password database;
nome database;
prefisso tabelle phpBB;
prefisso tabelle Flarum.
Inoltre devi decidere se vuoi che lo script svuoti o meno le tabelle di destinazione.
Questa variabile è molto importante:
$truncateDestination = true;
Se impostata su true, lo script prova a svuotare le tabelle principali di Flarum prima della migrazione.
Va usata solo se stai lavorando su un’installazione pulita di test o comunque su una destinazione predisposta appositamente per ricevere i dati migrati.
Se invece vuoi essere più prudente nelle prove iniziali, puoi metterla su:
$truncateDestination = false;
Anche questa variabile può essere utile:
$onlyVisibleContent = true;
Se resta su true, migrerà solo topic e post visibili/approvati.
Per la maggior parte dei casi è l’impostazione consigliata.
Script di migrazione.
Con Visual Studio Core o similare crea un Nuovo File di Testo incollando il codice qui sotto e salva il file come migra.php.
Attenzione! Devi personalizzalo nella parte iniziale e poi caricalo nella cartella principale di Flarum.
Successivamente eseguilo dal browser, ad esempio:
https://tuosito.altervista.org/tuacartella/migra.php
Ecco lo script:
<?php
declare(strict_types=1);
/**
* Migrazione base phpBB -> Flarum con creazione tag
* Testare SEMPRE su copia del database prima dell'uso reale.
*/
ini_set('display_errors', '1');
error_reporting(E_ALL);
set_time_limit(0);
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
/* =========================
1. CONFIGURAZIONE
========================= */
$db_host = 'localhost';
$db_user = 'TUO_USER_DB';
$db_pass = 'TUA_PASSWORD_DB';
$db_name = 'my_tuousername';
$pref_p = 'phpbb_'; // esempio: phpbb_
$pref_f = 'flarum_'; // esempio: flarum_
// true = svuota le tabelle Flarum di utenti/discussioni/post/tag prima della migrazione
// usare solo su installazione Flarum pulita di test o destinata alla migrazione
$truncateDestination = true;
// se true, migra solo topic e post visibili/approvati dove possibile
$onlyVisibleContent = true;
/* =========================
2. CONNESSIONE
========================= */
$conn = new mysqli($db_host, $db_user, $db_pass, $db_name);
$conn->set_charset('utf8mb4');
function h(string $s): string {
return htmlspecialchars($s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}
function out(string $msg): void {
echo $msg . "<br>\n";
@ob_flush();
@flush();
}
function tableExists(mysqli $conn, string $table): bool {
$sql = "SHOW TABLES LIKE ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param('s', $table);
$stmt->execute();
$res = $stmt->get_result();
return $res->num_rows > 0;
}
function slugify(string $text, string $fallback = 'n-a'): string {
$text = trim($text);
$text = preg_replace('~[^\pL\d]+~u', '-', $text);
$text = trim((string)$text, '-');
if (function_exists('iconv')) {
$converted = @iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $text);
if ($converted !== false) {
$text = $converted;
}
}
$text = strtolower($text);
$text = preg_replace('~[^-\w]+~', '', $text);
$text = preg_replace('~-+~', '-', $text);
$text = trim((string)$text, '-');
return $text !== '' ? $text : $fallback;
}
function randomHexColor(): string {
return str_pad(dechex(random_int(0, 0xFFFFFF)), 6, '0', STR_PAD_LEFT);
}
function xmlEscape(string $text): string {
return htmlspecialchars($text, ENT_XML1 | ENT_QUOTES, 'UTF-8');
}
/**
* Pulizia minima del testo phpBB.
* Rimuove gli identificatori interni del BBCode e conserva il testo in forma leggibile.
* Non è una conversione perfetta del BBCode avanzato.
*/
function cleanPhpbbText(string $text): string {
$text = preg_replace('/:[a-z0-9]{8}\b/i', '', $text);
$text = preg_replace('/\[quote(?:=.*?)?\](.*?)\[\/quote\]/is', "\n> $1\n", $text);
$text = preg_replace('/\[url=(.*?)\](.*?)\[\/url\]/is', '$2 ($1)', $text);
$text = preg_replace('/\[url\](.*?)\[\/url\]/is', '$1', $text);
$text = preg_replace('/\[img\](.*?)\[\/img\]/is', '$1', $text);
$text = preg_replace('/\[(b|i|u|color|size|list|\*|code|spoiler)(?:=.*?)?\]/i', '', $text);
$text = preg_replace('/\[\/(b|i|u|color|size|list|code|spoiler)\]/i', '', $text);
$text = html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8');
$text = str_replace(["\r\n", "\r"], "\n", $text);
$text = preg_replace("/\n{3,}/", "\n\n", $text);
return trim($text);
}
/**
* Converte testo semplice in XML minimale compatibile con i post comment.
*/
function flarumCommentContentFromPlainText(string $text): string {
$text = trim($text);
if ($text === '') {
return '<t><p></p></t>';
}
$paragraphs = preg_split("/\n\s*\n/", $text) ?: [];
$xml = '<t>';
foreach ($paragraphs as $p) {
$lines = explode("\n", trim($p));
$joined = implode("<br/>", array_map('xmlEscape', $lines));
$xml .= '<p>' . $joined . '</p>';
}
$xml .= '</t>';
return $xml;
}
function getTagTable(mysqli $conn, string $pref_f): string {
$candidates = [
$pref_f . 'tags',
'tags'
];
foreach ($candidates as $t) {
if (tableExists($conn, $t)) {
return $t;
}
}
throw new RuntimeException("Tabella tag non trovata. Attiva prima l'estensione Tags in Flarum.");
}
function getDiscussionTagTable(mysqli $conn, string $pref_f): string {
$candidates = [
$pref_f . 'discussion_tag',
$pref_f . 'discussions_tags',
'discussion_tag',
'discussions_tags'
];
foreach ($candidates as $t) {
if (tableExists($conn, $t)) {
return $t;
}
}
throw new RuntimeException("Tabella pivot discussion/tag non trovata. Attiva prima l'estensione Tags in Flarum.");
}
function usernameExists(mysqli $conn, string $table, string $username): bool {
$stmt = $conn->prepare("SELECT id FROM `{$table}` WHERE username = ? LIMIT 1");
$stmt->bind_param('s', $username);
$stmt->execute();
return $stmt->get_result()->num_rows > 0;
}
function emailExists(mysqli $conn, string $table, string $email): bool {
$stmt = $conn->prepare("SELECT id FROM `{$table}` WHERE email = ? LIMIT 1");
$stmt->bind_param('s', $email);
$stmt->execute();
return $stmt->get_result()->num_rows > 0;
}
function tagSlugExists(mysqli $conn, string $table, string $slug): bool {
$stmt = $conn->prepare("SELECT id FROM `{$table}` WHERE slug = ? LIMIT 1");
$stmt->bind_param('s', $slug);
$stmt->execute();
return $stmt->get_result()->num_rows > 0;
}
function makeUniqueUsername(mysqli $conn, string $table, string $username, int $id): string {
$base = trim($username);
if ($base === '') {
$base = 'utente-' . $id;
}
$candidate = $base;
$i = 1;
while (usernameExists($conn, $table, $candidate)) {
$candidate = $base . '-' . $id . '-' . $i;
$i++;
}
return $candidate;
}
function makeUniqueEmail(mysqli $conn, string $table, string $email, int $id): string {
$base = trim($email);
if ($base === '' || !filter_var($base, FILTER_VALIDATE_EMAIL)) {
return 'utente+' . $id . '@example.invalid';
}
if (!emailExists($conn, $table, $base)) {
return $base;
}
$parts = explode('@', $base, 2);
$local = $parts[0] ?? 'utente';
$domain = $parts[1] ?? 'example.invalid';
$i = 1;
do {
$candidate = $local . '+' . $id . '-' . $i . '@' . $domain;
$i++;
} while (emailExists($conn, $table, $candidate));
return $candidate;
}
function makeUniqueTagSlug(mysqli $conn, string $table, string $slug, int $id): string {
$base = trim($slug) !== '' ? trim($slug) : ('forum-' . $id);
$candidate = $base;
$i = 1;
while (tagSlugExists($conn, $table, $candidate)) {
$candidate = $base . '-' . $id . '-' . $i;
$i++;
}
return $candidate;
}
/* =========================
3. VERIFICHE
========================= */
$phpbbUsers = $pref_p . 'users';
$phpbbForums = $pref_p . 'forums';
$phpbbTopics = $pref_p . 'topics';
$phpbbPosts = $pref_p . 'posts';
$flarumUsers = $pref_f . 'users';
$flarumDiscussions = $pref_f . 'discussions';
$flarumPosts = $pref_f . 'posts';
$flarumTags = getTagTable($conn, $pref_f);
$flarumDiscussionTag = getDiscussionTagTable($conn, $pref_f);
foreach ([
$phpbbUsers,
$phpbbForums,
$phpbbTopics,
$phpbbPosts,
$flarumUsers,
$flarumDiscussions,
$flarumPosts,
$flarumTags,
$flarumDiscussionTag
] as $tbl) {
if (!tableExists($conn, $tbl)) {
throw new RuntimeException("Tabella mancante: {$tbl}");
}
}
out('<h2>Migrazione phpBB -> Flarum</h2>');
out('Tag table rilevata: <b>' . h($flarumTags) . '</b>');
out('Pivot discussion/tag rilevata: <b>' . h($flarumDiscussionTag) . '</b>');
/* =========================
4. PREPARAZIONE
========================= */
$conn->query('SET FOREIGN_KEY_CHECKS = 0');
try {
if ($truncateDestination) {
out('<b>Pulizia tabelle di destinazione...</b>');
$conn->query("TRUNCATE TABLE `{$flarumDiscussionTag}`");
$conn->query("TRUNCATE TABLE `{$flarumPosts}`");
$conn->query("TRUNCATE TABLE `{$flarumDiscussions}`");
$conn->query("TRUNCATE TABLE `{$flarumTags}`");
$conn->query("TRUNCATE TABLE `{$flarumUsers}`");
}
/* =========================
5. UTENTI
========================= */
out('<b>Fase 1: migrazione utenti...</b>');
$sqlUsers = "
SELECT user_id, username, user_email, user_regdate, user_type
FROM `{$phpbbUsers}`
WHERE user_type <> 2
ORDER BY user_id ASC
";
$resUsers = $conn->query($sqlUsers);
$stmtInsertUser = $conn->prepare("
INSERT INTO `{$flarumUsers}`
(id, username, email, is_email_confirmed, password, joined_at, last_seen_at, is_admin)
VALUES (?, ?, ?, 1, ?, ?, ?, ?)
");
$userCount = 0;
while ($u = $resUsers->fetch_assoc()) {
$id = (int)$u['user_id'];
$username = trim((string)$u['username']);
$email = trim((string)$u['user_email']);
$username = makeUniqueUsername($conn, $flarumUsers, $username, $id);
$email = makeUniqueEmail($conn, $flarumUsers, $email, $id);
$joinedTimestamp = (int)$u['user_regdate'];
$joinedAt = $joinedTimestamp > 0 ? date('Y-m-d H:i:s', $joinedTimestamp) : date('Y-m-d H:i:s');
$lastSeenAt = $joinedAt;
$password = password_hash(bin2hex(random_bytes(16)), PASSWORD_BCRYPT);
$isAdmin = ((int)$u['user_type'] === 3) ? 1 : 0;
$stmtInsertUser->bind_param(
'isssssi',
$id,
$username,
$email,
$password,
$joinedAt,
$lastSeenAt,
$isAdmin
);
$stmtInsertUser->execute();
$userCount++;
}
out("Utenti migrati: {$userCount}");
/* =========================
6. TAG DA FORUM PHPBB
========================= */
out('<b>Fase 2: creazione tag da forum phpBB...</b>');
$sqlForums = "
SELECT forum_id, parent_id, forum_name
FROM `{$phpbbForums}`
ORDER BY parent_id ASC, left_id ASC, forum_id ASC
";
$resForums = $conn->query($sqlForums);
$forumToTag = [];
$positionPrimary = 0;
$positionSecondary = 0;
$stmtInsertTagPrimary = $conn->prepare("
INSERT INTO `{$flarumTags}`
(id, name, slug, description, color, background_path, icon, position, parent_id, default_sort, is_restricted, is_hidden, discussions_count, last_posted_at, last_posted_discussion_id)
VALUES (?, ?, ?, NULL, ?, NULL, NULL, ?, NULL, NULL, 0, 0, 0, NULL, NULL)
");
$resForums->data_seek(0);
while ($f = $resForums->fetch_assoc()) {
$forumId = (int)$f['forum_id'];
$parentId = (int)$f['parent_id'];
$name = trim((string)$f['forum_name']);
if ($parentId !== 0) {
continue;
}
if ($name === '') {
$name = 'Forum ' . $forumId;
}
$slug = slugify($name, 'forum-' . $forumId);
$slug = makeUniqueTagSlug($conn, $flarumTags, $slug, $forumId);
$color = randomHexColor();
$stmtInsertTagPrimary->bind_param(
'isssi',
$forumId,
$name,
$slug,
$color,
$positionPrimary
);
$stmtInsertTagPrimary->execute();
$forumToTag[$forumId] = $forumId;
$positionPrimary++;
}
$stmtInsertTagSecondary = $conn->prepare("
INSERT INTO `{$flarumTags}`
(id, name, slug, description, color, background_path, icon, position, parent_id, default_sort, is_restricted, is_hidden, discussions_count, last_posted_at, last_posted_discussion_id)
VALUES (?, ?, ?, NULL, ?, NULL, NULL, ?, ?, NULL, 0, 0, 0, NULL, NULL)
");
$resForums->data_seek(0);
while ($f = $resForums->fetch_assoc()) {
$forumId = (int)$f['forum_id'];
$parentId = (int)$f['parent_id'];
$name = trim((string)$f['forum_name']);
if ($parentId === 0) {
continue;
}
if (!isset($forumToTag[$parentId])) {
continue;
}
if ($name === '') {
$name = 'Forum ' . $forumId;
}
$slug = slugify($name, 'forum-' . $forumId);
$slug = makeUniqueTagSlug($conn, $flarumTags, $slug, $forumId);
$color = randomHexColor();
$parentTagId = $forumToTag[$parentId];
$stmtInsertTagSecondary->bind_param(
'isssii',
$forumId,
$name,
$slug,
$color,
$positionSecondary,
$parentTagId
);
$stmtInsertTagSecondary->execute();
$forumToTag[$forumId] = $forumId;
$positionSecondary++;
}
out('Tag creati: ' . count($forumToTag));
/* =========================
7. DISCUSSIONI
========================= */
out('<b>Fase 3: migrazione discussioni...</b>');
$whereTopics = '';
if ($onlyVisibleContent) {
$whereTopics = 'WHERE topic_visibility = 1';
}
$sqlTopics = "
SELECT topic_id, forum_id, topic_title, topic_poster, topic_time, topic_last_post_time
FROM `{$phpbbTopics}`
{$whereTopics}
ORDER BY topic_id ASC
";
$resTopics = $conn->query($sqlTopics);
$stmtInsertDiscussion = $conn->prepare("
INSERT INTO `{$flarumDiscussions}`
(id, title, slug, comments_count, participants_count, number_index, created_at, user_id, first_post_id, last_post_id, last_posted_at, last_posted_user_id, is_private, is_sticky, is_locked)
VALUES (?, ?, ?, 0, 0, ?, ?, ?, NULL, NULL, ?, ?, 0, 0, 0)
");
$stmtInsertDiscussionTag = $conn->prepare("
INSERT INTO `{$flarumDiscussionTag}` (discussion_id, tag_id)
VALUES (?, ?)
");
$discussionCount = 0;
while ($t = $resTopics->fetch_assoc()) {
$topicId = (int)$t['topic_id'];
$forumId = (int)$t['forum_id'];
$title = trim((string)$t['topic_title']);
$posterId = (int)$t['topic_poster'];
$createdTs = (int)$t['topic_time'];
$lastPostedTs = (int)$t['topic_last_post_time'];
$createdAt = $createdTs > 0 ? date('Y-m-d H:i:s', $createdTs) : date('Y-m-d H:i:s');
$lastPostedAt = $lastPostedTs > 0 ? date('Y-m-d H:i:s', $lastPostedTs) : $createdAt;
if ($title === '') {
$title = 'Discussione ' . $topicId;
}
$slug = slugify($title, 'discussion-' . $topicId);
$numberIndex = $topicId;
$lastPostedUserId = $posterId > 0 ? $posterId : 1;
$userId = $posterId > 0 ? $posterId : 1;
$stmtInsertDiscussion->bind_param(
'issisissi',
$topicId,
$title,
$slug,
$numberIndex,
$createdAt,
$userId,
$lastPostedAt,
$lastPostedUserId
);
$stmtInsertDiscussion->execute();
if (isset($forumToTag[$forumId])) {
$tagId = $forumToTag[$forumId];
$stmtInsertDiscussionTag->bind_param('ii', $topicId, $tagId);
$stmtInsertDiscussionTag->execute();
}
$discussionCount++;
}
out("Discussioni migrate: {$discussionCount}");
/* =========================
8. POST
========================= */
out('<b>Fase 4: migrazione post...</b>');
$wherePosts = '';
if ($onlyVisibleContent) {
$wherePosts = 'WHERE post_visibility = 1';
}
$sqlPosts = "
SELECT post_id, topic_id, poster_id, post_time, post_text
FROM `{$phpbbPosts}`
{$wherePosts}
ORDER BY post_id ASC
";
$resPosts = $conn->query($sqlPosts);
$postCount = 0;
$postNumberByDiscussion = [];
$discussionStats = [];
$stmtInsertPost = $conn->prepare("
INSERT INTO `{$flarumPosts}`
(id, discussion_id, number, created_at, user_id, type, content, edited_at, edited_user_id, is_private, is_approved)
VALUES (?, ?, ?, ?, ?, 'comment', ?, NULL, NULL, 0, 1)
");
while ($p = $resPosts->fetch_assoc()) {
$postId = (int)$p['post_id'];
$topicId = (int)$p['topic_id'];
$posterId = (int)$p['poster_id'];
$postTs = (int)$p['post_time'];
$createdAt = $postTs > 0 ? date('Y-m-d H:i:s', $postTs) : date('Y-m-d H:i:s');
$rawText = (string)$p['post_text'];
$userId = $posterId > 0 ? $posterId : 1;
$cleanText = cleanPhpbbText($rawText);
$contentXml = flarumCommentContentFromPlainText($cleanText);
if (!isset($postNumberByDiscussion[$topicId])) {
$postNumberByDiscussion[$topicId] = 1;
} else {
$postNumberByDiscussion[$topicId]++;
}
$number = $postNumberByDiscussion[$topicId];
$stmtInsertPost->bind_param(
'iiisis',
$postId,
$topicId,
$number,
$createdAt,
$userId,
$contentXml
);
$stmtInsertPost->execute();
if (!isset($discussionStats[$topicId])) {
$discussionStats[$topicId] = [
'first_post_id' => $postId,
'last_post_id' => $postId,
'last_posted_at' => $createdAt,
'last_posted_user_id' => $userId,
'comments_count' => 1,
'participants' => []
];
} else {
$discussionStats[$topicId]['last_post_id'] = $postId;
$discussionStats[$topicId]['last_posted_at'] = $createdAt;
$discussionStats[$topicId]['last_posted_user_id'] = $userId;
$discussionStats[$topicId]['comments_count']++;
}
$discussionStats[$topicId]['participants'][$userId] = true;
$postCount++;
}
out("Post migrati: {$postCount}");
/* =========================
9. AGGIORNAMENTO DISCUSSIONI
========================= */
out('<b>Fase 5: aggiornamento contatori discussioni...</b>');
$stmtUpdateDiscussion = $conn->prepare("
UPDATE `{$flarumDiscussions}`
SET first_post_id = ?, last_post_id = ?, last_posted_at = ?, last_posted_user_id = ?, comments_count = ?, participants_count = ?
WHERE id = ?
");
foreach ($discussionStats as $discussionId => $stats) {
$firstPostId = (int)$stats['first_post_id'];
$lastPostId = (int)$stats['last_post_id'];
$lastPostedAt = (string)$stats['last_posted_at'];
$lastPostedUserId = (int)$stats['last_posted_user_id'];
$commentsCount = (int)$stats['comments_count'];
$participantsCount = count($stats['participants']);
$discussionIdInt = (int)$discussionId;
$stmtUpdateDiscussion->bind_param(
'iisiiii',
$firstPostId,
$lastPostId,
$lastPostedAt,
$lastPostedUserId,
$commentsCount,
$participantsCount,
$discussionIdInt
);
$stmtUpdateDiscussion->execute();
}
/* =========================
10. AGGIORNAMENTO TAG
========================= */
out('<b>Fase 6: aggiornamento contatori tag...</b>');
$conn->query("
UPDATE `{$flarumTags}`
SET discussions_count = 0,
last_posted_at = NULL,
last_posted_discussion_id = NULL
");
$sqlTagCounts = "
SELECT dt.tag_id, COUNT(*) AS cnt
FROM `{$flarumDiscussionTag}` dt
INNER JOIN `{$flarumDiscussions}` d ON d.id = dt.discussion_id
WHERE d.is_private = 0
GROUP BY dt.tag_id
";
$resTagCounts = $conn->query($sqlTagCounts);
$stmtUpdateTagCount = $conn->prepare("
UPDATE `{$flarumTags}`
SET discussions_count = ?
WHERE id = ?
");
while ($row = $resTagCounts->fetch_assoc()) {
$cnt = (int)$row['cnt'];
$tagId = (int)$row['tag_id'];
$stmtUpdateTagCount->bind_param('ii', $cnt, $tagId);
$stmtUpdateTagCount->execute();
}
$sqlTagLast = "
SELECT dt.tag_id, d.id AS discussion_id, d.last_posted_at
FROM `{$flarumDiscussionTag}` dt
INNER JOIN `{$flarumDiscussions}` d ON d.id = dt.discussion_id
INNER JOIN (
SELECT dt2.tag_id, MAX(d2.last_posted_at) AS max_last
FROM `{$flarumDiscussionTag}` dt2
INNER JOIN `{$flarumDiscussions}` d2 ON d2.id = dt2.discussion_id
WHERE d2.is_private = 0
GROUP BY dt2.tag_id
) x ON x.tag_id = dt.tag_id AND x.max_last = d.last_posted_at
";
$resTagLast = $conn->query($sqlTagLast);
$stmtUpdateTagLast = $conn->prepare("
UPDATE `{$flarumTags}`
SET last_posted_at = ?, last_posted_discussion_id = ?
WHERE id = ?
");
$updatedTagLast = [];
while ($row = $resTagLast->fetch_assoc()) {
$lastAt = (string)$row['last_posted_at'];
$discussionId = (int)$row['discussion_id'];
$tagId = (int)$row['tag_id'];
if (isset($updatedTagLast[$tagId])) {
continue;
}
$stmtUpdateTagLast->bind_param('sii', $lastAt, $discussionId, $tagId);
$stmtUpdateTagLast->execute();
$updatedTagLast[$tagId] = true;
}
out('<hr><b>Migrazione completata.</b>');
out('Adesso:');
out('1) entra in Flarum e verifica discussioni, post e tag;');
out('2) prova alcuni topic con BBCode complesso;');
out('3) elimina subito migra.php dal server;');
out('4) chiedi agli utenti di reimpostare la password.');
} finally {
$conn->query('SET FOREIGN_KEY_CHECKS = 1');
}
Una volta terminata la migrazione, elimina subito il file dal server.
Cosa fa esattamente questo script:
Questo script:
- legge gli utenti da phpBB e li inserisce in Flarum;
- legge i forum e sottoforum phpBB e li converte in tag Flarum;
- legge i topic phpBB e li trasforma in discussioni Flarum;
- legge i post e li converte in contenuto testuale pulito;
- collega ogni discussione al relativo tag;
- aggiorna i contatori finali di discussioni e tag.
In pratica consente una migrazione di base piuttosto completa, purché l’installazione non abbia esigenze troppo particolari.
Limiti da conoscere prima
Per correttezza, è bene sapere che questa procedura ha alcuni limiti.
Questo resta uno script base, non una migrazione perfetta al 100%
La parte più delicata resta sempre la compatibilità con la struttura esatta delle tabelle Flarum che hai sul tuo Altervista.
- Password
Gli utenti non manterranno normalmente una password già pronta all’accesso. Dopo la migrazione conviene invitarli a usare la funzione di recupero password.
- BBCode avanzato
Lo script pulisce il BBCode e cerca di conservare il contenuto in modo leggibile, ma non promette una conversione perfetta di tutto. Quote complesse, spoiler, liste articolate, allegati e formattazioni particolari potrebbero richiedere una revisione manuale.
- Gruppi e permessi
La migrazione riguarda soprattutto struttura e contenuti. Non replica automaticamente tutta la logica dei gruppi e dei permessi di phpBB.
Test preliminare
La procedura va provata prima su una copia del database.
È il modo migliore per evitare problemi sul forum definitivo.
Ricapitolando, il suggerimento migliore è questo:
- installa Flarum pulito;
- attiva l’estensione Tags;
- fai un backup completo;
- esegui una prova su copia di test;
- controlla utenti, tag, discussioni e post;
- solo dopo, ripeti la procedura sull’installazione definitiva.
Nota personale
Nel mio caso questa strada si è rivelata utile soprattutto perché su hosting condiviso come Altervista non sempre è comodo usare procedure più avanzate via shell o strumenti esterni. Proprio per questo ho preferito una soluzione semplice, leggibile e modificabile.