<?php

/**
 * @package     Dotclear
 *
 * @copyright   Olivier Meunier & Association Dotclear
 * @copyright   AGPL-3.0
 */
declare(strict_types=1);

namespace Dotclear\Plugin\importExport;

use ArrayObject;
use Dotclear\App;
use Dotclear\Database\MetaRecord;
use Dotclear\Database\Record;
use Dotclear\Helper\Crypt;
use Dotclear\Helper\Html\Form\Div;
use Dotclear\Helper\Html\Form\Form;
use Dotclear\Helper\Html\Form\Hidden;
use Dotclear\Helper\Html\Form\Input;
use Dotclear\Helper\Html\Form\Label;
use Dotclear\Helper\Html\Form\Li;
use Dotclear\Helper\Html\Form\Note;
use Dotclear\Helper\Html\Form\Number;
use Dotclear\Helper\Html\Form\Option;
use Dotclear\Helper\Html\Form\Para;
use Dotclear\Helper\Html\Form\Password;
use Dotclear\Helper\Html\Form\Select;
use Dotclear\Helper\Html\Form\Set;
use Dotclear\Helper\Html\Form\Submit;
use Dotclear\Helper\Html\Form\Text;
use Dotclear\Helper\Html\Form\Ul;
use Dotclear\Helper\Html\Html;
use Dotclear\Helper\Network\Http;
use Dotclear\Helper\Text as Txt;
use Dotclear\Interface\Database\ConnectionInterface;
use Dotclear\Plugin\blogroll\Blogroll;
use Exception;

/**
 * @brief   The DC1 import module handler.
 * @ingroup importExport
 *
 * @todo switch to SqlStatement
 */
class ModuleImportDc1 extends Module
{
    protected ConnectionInterface $con;

    protected string $prefix;

    protected string $blog_id;

    protected ?string $action = null;

    protected int $step = 1;

    protected int $post_offset = 0;

    protected int $post_limit = 20;

    protected int $post_count = 0;

    /**
     * @var array<string, bool>     $has_table
     */
    protected array $has_table = [];

    /**
     * @var array{
     *     db_driver: string,
     *     db_host: string,
     *     db_name: string,
     *     db_user: string,
     *     db_pwd: string,
     *     db_prefix: string,
     *     post_limit: int,
     *     cat_ids: array<int, int>
     * }    $vars
     */
    protected $vars;

    /**
     * @var array{
     *     db_driver: string,
     *     db_host: string,
     *     db_name: string,
     *     db_user: string,
     *     db_pwd: string,
     *     db_prefix: string,
     *     post_limit: int,
     *     cat_ids: array<int, int>
     * }    $base_vars
     */
    protected $base_vars = [
        'db_driver'  => 'mysqli',
        'db_host'    => '',
        'db_name'    => '',
        'db_user'    => '',
        'db_pwd'     => '',
        'db_prefix'  => 'dc_',
        'post_limit' => 20,
        'cat_ids'    => [],
    ];

    protected function setInfo(): void
    {
        $this->type        = 'import';
        $this->name        = __('Dotclear 1.2 import');
        $this->description = __('Import a Dotclear 1.2 installation into your current blog.');
    }

    public function init(): void
    {
        $this->con     = App::db()->con();
        $this->prefix  = App::db()->con()->prefix();
        $this->blog_id = App::blog()->id();

        $this->readVars();

        if ($this->vars['post_limit'] > 0) {
            $this->post_limit = $this->vars['post_limit'];
            $this->writeVars();
        }
    }

    protected function readVars(): void
    {
        if (is_array(App::session()->get('dc1_import_vars'))) {
            /**
             * @var array{
             *     db_driver: string,
             *     db_host: string,
             *     db_name: string,
             *     db_user: string,
             *     db_pwd: string,
             *     db_prefix: string,
             *     post_limit: int,
             *     cat_ids: array<int, int>
             * }    $vars
             */
            $vars       = App::session()->get('dc1_import_vars');
            $this->vars = $vars;
        } else {
            $this->resetVars();
        }
    }

    protected function writeVars(): void
    {
        App::session()->set('dc1_import_vars', $this->vars);
    }

    public function resetVars(): void
    {
        $this->vars = $this->base_vars;
        $this->writeVars();
    }

    public function process(string $do): void
    {
        $this->action = $do;
    }

    /**
     * Processes the import/export with GUI feedback.
     *
     * We handle process in another way to always display something to user
     *
     * @param   string  $do     action
     */
    protected function guiprocess(string $do): void
    {
        switch ($do) {
            case 'step1':
                $this->vars['db_driver']  = is_string($_POST['db_driver']) ? $_POST['db_driver'] : '';
                $this->vars['db_host']    = is_string($_POST['db_host']) ? $_POST['db_host'] : '';
                $this->vars['db_name']    = is_string($_POST['db_name']) ? $_POST['db_name'] : '';
                $this->vars['db_user']    = is_string($_POST['db_user']) ? $_POST['db_user'] : '';
                $this->vars['db_pwd']     = is_string($_POST['db_pwd']) ? $_POST['db_pwd'] : '';
                $this->vars['db_prefix']  = is_string($_POST['db_prefix']) ? $_POST['db_prefix'] : '';
                $this->vars['post_limit'] = is_numeric($_POST['post_limit']) ? (int) $_POST['post_limit'] : 0;

                $db = $this->db();
                $db->close();
                $this->writeVars();
                $this->step = 2;
                echo $this->progressBar(1);

                break;
            case 'step2':
                $this->step = 2;
                $this->importUsers();
                $this->step = 3;
                echo $this->progressBar(3);

                break;
            case 'step3':
                $this->step = 3;
                $this->importCategories();
                if (App::plugins()->moduleExists('blogroll')) {
                    $this->step = 4;
                    echo $this->progressBar(5);
                } else {
                    $this->step = 5;
                    echo $this->progressBar(7);
                }

                break;
            case 'step4':
                $this->step = 4;
                $this->importLinks();
                $this->step = 5;
                echo $this->progressBar(7);

                break;
            case 'step5':
                $this->step        = 5;
                $this->post_offset = is_numeric($_REQUEST['offset']) ? (int) $_REQUEST['offset'] : 0;
                $percent           = 0;
                if ($this->importPosts($percent) === -1) {
                    Http::redirect($this->getURL() . '&do=ok');
                } else {
                    echo $this->progressBar((int) ceil((float) $percent * 0.93) + 7);
                }

                break;
            case 'ok':
                $this->resetVars();
                App::blog()->triggerBlog();
                $this->step = 6;
                echo $this->progressBar(100);

                break;
        }
    }

    public function gui(): void
    {
        try {
            $this->guiprocess((string) $this->action);
        } catch (Exception $exception) {
            $this->error($exception);
        }

        # db drivers
        $db_drivers = [
            new Option('mysqli', 'mysqli'),
        ];

        switch ($this->step) {
            case 1:
                echo (new Para())
                    ->items([
                        (new Text(null, sprintf(
                            __('Import the content of a Dotclear 1.2\'s blog in the current blog: %s.'),
                            '<strong>' . Html::escapeHTML(App::blog()->name()) . '</strong>'
                        ))),
                    ])
                ->render();

                echo (new Note())
                    ->class('warning')
                    ->text(__('Please note that this process will empty your categories, blogroll, entries and comments on the current blog.'))
                ->render();

                $text = (new Set())
                    ->items([
                        (new Para())
                            ->items([
                                (new Text(null, __('We first need some information about your old Dotclear 1.2 installation.'))),
                            ]),
                        (new Para())
                            ->items([
                                (new Select('db_driver'))
                                    ->items($db_drivers)
                                    ->default(Html::escapeHTML($this->vars['db_driver']))
                                    ->label((new Label(__('Database driver:'), Label::OUTSIDE_TEXT_BEFORE))),
                            ]),
                        (new Para())
                            ->items([
                                (new Input('db_host'))
                                    ->size(30)
                                    ->maxlength(255)
                                    ->value(Html::escapeHTML($this->vars['db_host']))
                                    ->label((new Label(__('Database Host Name:'), Label::OUTSIDE_TEXT_BEFORE))),
                            ]),
                        (new Para())
                            ->items([
                                (new Input('db_name'))
                                    ->size(30)
                                    ->maxlength(255)
                                    ->value(Html::escapeHTML($this->vars['db_name']))
                                    ->label((new Label(__('Database Name:'), Label::OUTSIDE_TEXT_BEFORE))),
                            ]),
                        (new Para())
                            ->items([
                                (new Input('db_user'))
                                    ->size(30)
                                    ->maxlength(255)
                                    ->value(Html::escapeHTML($this->vars['db_user']))
                                    ->label((new Label(__('Database User Name:'), Label::OUTSIDE_TEXT_BEFORE))),
                            ]),
                        (new Para())
                            ->items([
                                (new Password('db_pwd'))
                                    ->size(30)
                                    ->maxlength(255)
                                    ->value(Html::escapeHTML($this->vars['db_pwd']))
                                    ->label((new Label(__('Database Password:'), Label::OUTSIDE_TEXT_BEFORE))),
                            ]),
                        (new Para())
                            ->items([
                                (new Input('db_prefix'))
                                    ->size(30)
                                    ->maxlength(255)
                                    ->value(Html::escapeHTML($this->vars['db_prefix']))
                                    ->label((new Label(__('Database Tables Prefix:'), Label::OUTSIDE_TEXT_BEFORE))),
                            ]),
                        (new Text('h3', __('Entries import options')))->class('vertical-separator'),
                        (new Para())
                            ->items([
                                (new Number('post_limit', 0, 999))
                                    ->value(Html::escapeHTML((string) $this->vars['post_limit']))
                                    ->label((new Label(__('Number of entries to import at once:'), Label::INSIDE_TEXT_BEFORE))),
                            ]),
                    ])
                ->render();

                printf(
                    $this->imForm(1, __('General information'), __('Import my blog now')),
                    $text
                );

                break;
            case 2:
                printf(
                    $this->imForm(2, __('Importing users')),
                    $this->autoSubmit()
                );

                break;
            case 3:
                printf(
                    $this->imForm(3, __('Importing categories')),
                    $this->autoSubmit()
                );

                break;
            case 4:
                printf(
                    $this->imForm(4, __('Importing blogroll')),
                    $this->autoSubmit()
                );

                break;
            case 5:
                $text = sprintf(
                    __('Importing entries from %d to %d / %d'),
                    $this->post_offset,
                    min([$this->post_offset + $this->post_limit, $this->post_count]),
                    $this->post_count
                );
                printf(
                    $this->imForm(5, $text),
                    (new Hidden(['offset'], (string) $this->post_offset))->render() .
                    $this->autoSubmit()
                );

                break;
            case 6:
                echo (new Set())
                    ->items([
                        (new Text('h3', __('Please read carefully')))->class('vertical-separator'),
                        (new Ul())->items([
                            (new Li())->text(__('Every newly imported user has received a random password and will need to ask for a new one by following the "I forgot my password" link on the login page (Their registered email address has to be valid.)')),
                            (new Li())->text(sprintf(
                                __('Please note that Dotclear 2 has a new URL layout. You can avoid broken links by installing <a href="%s">DC1 redirect</a> plugin and activate it in your blog configuration.'),
                                'https://plugins.dotaddict.org/dc2/details/dc1redirect'
                            )),
                        ]),
                    ])
                ->render();

                $this->congratMessage();

                break;
        }
    }

    /**
     * Return a simple form for step by step process.
     *
     * @param   int     $step           The step
     * @param   string  $legend         The legend
     * @param   string  $submit_value   The submit value
     */
    protected function imForm(int $step, string $legend, ?string $submit_value = null): string
    {
        if (!$submit_value) {
            $submit_value = __('next step') . ' >';
        }

        return (new Form('im-form'))
            ->method('post')
            ->action($this->getURL(true))
            ->fields([
                (new Text('h3', $legend))->class('vertical-separator'),
                ...My::hiddenFields(),
                (new Div())->items([
                    (new Hidden(['do'], 'step' . $step)),
                    (new Text(null, '%s')),
                ]),
                (new Para())->class('vertical-separator')->items([
                    (new Submit('im-form-submit', $submit_value)),
                ]),
                (new Note())
                    ->class(['form-note', 'info'])
                    ->text(__('Depending on the size of your blog, it could take a few minutes.')),
            ])
        ->render();
    }

    /**
     * Error display.
     *
     * @param   Exception   $e  The error
     */
    protected function error(Exception $e): void
    {
        App::backend()->notices()->error('<strong>' . __('Errors:') . '</strong>' . '<p>' . $e->getMessage() . '</p>', false, false);
    }

    /**
     * DB init.
     *
     * @throws  Exception
     */
    protected function db(): ConnectionInterface
    {
        $db = App::db()->newCon($this->vars['db_driver'], $this->vars['db_host'], $this->vars['db_name'], $this->vars['db_user'], $this->vars['db_pwd']);

        $rs = $db->select("SHOW TABLES LIKE '" . $this->vars['db_prefix'] . "%'");
        if ($rs->isEmpty()) {
            throw new Exception(__('Dotclear tables not found.'));
        }

        while ($rs->fetch()) {
            $table = is_string($rs->f(0)) ? $rs->f(0) : '';
            if ($table !== '') {
                $this->has_table[$table] = true;
            }
        }

        // Set this to read data as they were written in Dotclear 1
        try {
            $db->execute('SET NAMES DEFAULT');
        } catch (Exception) {
        }

        $db->execute('SET CHARACTER SET DEFAULT');
        $db->execute('SET COLLATION_CONNECTION = DEFAULT');
        $db->execute('SET COLLATION_SERVER = DEFAULT');
        $db->execute('SET CHARACTER_SET_SERVER = DEFAULT');
        $db->execute('SET CHARACTER_SET_DATABASE = DEFAULT');

        $this->post_count = is_numeric($post_count = $db->select(
            'SELECT COUNT(post_id) FROM ' . $this->vars['db_prefix'] . 'post '
        )->f(0)) ? (int) $post_count : 0;

        return $db;
    }

    /**
     * User import.
     */
    protected function importUsers(): void
    {
        $db         = $this->db();
        $dc1_prefix = $this->vars['db_prefix'];
        $rs         = $db->select('SELECT * FROM ' . $dc1_prefix . 'user');

        try {
            $this->con->begin();

            while ($rs->fetch()) {
                $user_id = is_string($user_id = $rs->user_id) ? $user_id : '';
                if ($user_id !== '' && !App::users()->userExists($user_id)) {
                    $user_nom    = is_string($user_nom = $rs->user_nom) ? $user_nom : '';
                    $user_prenom = is_string($user_prenom = $rs->user_prenom) ? $user_prenom : '';
                    $user_pseudo = is_string($user_pseudo = $rs->user_pseudo) ? $user_pseudo : '';
                    $user_email  = is_string($user_email = $rs->user_email) ? $user_email : '';
                    $user_lang   = is_string($user_lang = $rs->user_lang) ? $user_lang : '';

                    $cur = App::auth()->openUserCursor();
                    $cur->setStrField('user_id', $user_id);
                    $cur->setStrField('user_name', $user_nom);
                    $cur->setStrField('user_firstname', $user_prenom);
                    $cur->setStrField('user_displayname', $user_pseudo);
                    $cur->setStrField('user_pwd', Crypt::createPassword());
                    $cur->setStrField('user_email', $user_email);
                    $cur->setStrField('user_lang', $user_lang);
                    $cur->setStrField('user_tz', App::blog()->settings()->get('system')->getStr('blog_timezone'));
                    $cur->setIntField('user_post_status', $rs->user_post_pub ? App::status()->post()::PUBLISHED : App::status()->post()::PENDING);
                    $cur->setField('user_options', new ArrayObject([
                        'edit_size'   => is_numeric($rs->user_edit_size) ? (int) $rs->user_edit_size : 0,
                        'post_format' => $rs->user_post_format,
                    ]));

                    $permissions = [];
                    switch ($rs->user_level) {
                        case '0':
                            $cur->setIntField('user_status', App::status()->user()::DISABLED);

                            break;
                        case '1': # editor
                            $permissions['usage'] = true;

                            break;
                        case '5': # advanced editor
                            $permissions['contentadmin'] = true;
                            $permissions['categories']   = true;
                            $permissions['media_admin']  = true;

                            break;
                        case '9': # admin
                            $permissions['admin'] = true;

                            break;
                    }

                    App::users()->addUser($cur);
                    App::users()->setUserBlogPermissions(
                        $user_id,
                        $this->blog_id,
                        $permissions
                    );
                }
            }

            $this->con->commit();
            $db->close();
        } catch (Exception $exception) {
            $this->con->rollback();
            $db->close();

            throw $exception;
        }
    }

    /**
     * Categories import.
     */
    protected function importCategories(): void
    {
        $db         = $this->db();
        $dc1_prefix = $this->vars['db_prefix'];
        $rs         = $db->select('SELECT * FROM ' . $dc1_prefix . 'categorie ORDER BY cat_ord ASC');

        try {
            $this->con->execute(
                'DELETE FROM ' . $this->prefix . App::blog()->categories()::CATEGORY_TABLE_NAME . ' ' .
                "WHERE blog_id = '" . $this->con->escapeStr($this->blog_id) . "' "
            );

            $ord = 2;
            while ($rs->fetch()) {
                $cat_title = is_string($rs->cat_libelle) ? $rs->cat_libelle : '';
                $cat_desc  = is_string($rs->cat_desc) ? $rs->cat_desc : '';
                $cat_url   = is_string($rs->cat_libelle_url) ? $rs->cat_libelle_url : '';
                $cat_id    = is_numeric($rs->cat_id) ? (int) $rs->cat_id : 0;

                $cur = App::blog()->categories()->openCategoryCursor();
                $cur->setStrField('blog_id', $this->blog_id);
                $cur->setStrField('cat_title', Txt::cleanStr(htmlspecialchars_decode($cat_title)));
                $cur->setStrField('cat_desc', Txt::cleanStr($cat_desc));
                $cur->setStrField('cat_url', Txt::cleanStr($cat_url));
                $cur->setIntField('cat_lft', $ord++);
                $cur->setIntField('cat_rgt', $ord++);

                $new_cat_id = is_numeric($new_cat_id = (new MetaRecord($this->con->select(
                    'SELECT MAX(cat_id) FROM ' . $this->prefix . App::blog()->categories()::CATEGORY_TABLE_NAME
                )))->f(0)) ? (int) $new_cat_id : 0;
                $new_cat_id++;

                $cur->setIntField('cat_id', $new_cat_id);
                $this->vars['cat_ids'][$cat_id] = $new_cat_id;
                $cur->insert();
            }

            $db->close();
            $this->writeVars();
        } catch (Exception $exception) {
            $db->close();

            throw $exception;
        }
    }

    /**
     * Blogroll import.
     */
    protected function importLinks(): void
    {
        $db         = $this->db();
        $dc1_prefix = $this->vars['db_prefix'];
        $rs         = $db->select('SELECT * FROM ' . $dc1_prefix . 'link ORDER BY link_id ASC');

        try {
            $this->con->execute(
                'DELETE FROM ' . $this->prefix . Blogroll::LINK_TABLE_NAME . ' ' .
                "WHERE blog_id = '" . $this->con->escapeStr($this->blog_id) . "' "
            );

            while ($rs->fetch()) {
                $link_href     = is_string($link_href = $rs->href) ? $link_href : '';
                $link_title    = is_string($link_title = $rs->label) ? $link_title : '';
                $link_desc     = is_string($link_desc = $rs->title) ? $link_desc : '';
                $link_lang     = is_string($link_lang = $rs->lang) ? $link_lang : '';
                $link_xfn      = is_string($link_xfn = $rs->rel) ? $link_xfn : '';
                $link_position = is_numeric($link_position = $rs->position) ? (int) $link_position : 0;

                $cur = $this->con->openCursor($this->prefix . Blogroll::LINK_TABLE_NAME);
                $cur->setStrField('blog_id', $this->blog_id);
                $cur->setStrField('link_href', Txt::cleanStr($link_href));
                $cur->setStrField('link_title', Txt::cleanStr($link_title));
                $cur->setStrField('link_desc', Txt::cleanStr($link_desc));
                $cur->setStrField('link_lang', Txt::cleanStr($link_lang));
                $cur->setStrField('link_xfn', Txt::cleanStr($link_xfn));
                $cur->setIntField('link_position', $link_position);

                $link_id = is_numeric($link_id = (new MetaRecord($this->con->select(
                    'SELECT MAX(link_id) FROM ' . $this->prefix . Blogroll::LINK_TABLE_NAME
                )))->f(0)) ? (int) $link_id : 0;
                $link_id++;

                $cur->setIntField('link_id', $link_id);
                $cur->insert();
            }

            $db->close();
        } catch (Exception $exception) {
            $db->close();

            throw $exception;
        }
    }

    /**
     * Entries import.
     *
     * @param   int     $percent    The progress (in %)
     */
    protected function importPosts(int &$percent): ?int
    {
        $db         = $this->db();
        $dc1_prefix = $this->vars['db_prefix'];

        $rs = $db->select(
            'SELECT * FROM ' . $dc1_prefix . 'post ORDER BY post_id ASC ' .
            $db->limit($this->post_offset, $this->post_limit)
        );

        try {
            if ($this->post_offset === 0) {
                $this->con->execute(
                    'DELETE FROM ' . $this->prefix . App::blog()::POST_TABLE_NAME . ' ' .
                    "WHERE blog_id = '" . $this->con->escapeStr($this->blog_id) . "' "
                );
            }

            while ($rs->fetch()) {
                $this->importPost($rs, $db);
            }

            $db->close();
        } catch (Exception $exception) {
            $db->close();

            throw $exception;
        }

        if ($rs->count() < $this->post_limit) {
            return -1;
        }

        $this->post_offset += $this->post_limit;

        $percent = $this->post_offset > $this->post_count ? 100 : (int) ($this->post_offset * 100 / $this->post_count);

        return null;
    }

    /**
     * Entry import.
     *
     * @param   Record                  $rs     The record
     * @param   ConnectionInterface     $db     The database
     */
    protected function importPost(Record $rs, ConnectionInterface $db): void
    {
        $user_id = is_string($user_id = $rs->user_id) ? $user_id : '';
        if ($user_id === '') {
            return;
        }

        $cat_id             = is_numeric($cat_id = $rs->cat_id) ? (int) $cat_id : 0;
        $post_title         = is_string($post_title = $rs->post_titre) ? $post_title : '';
        $post_dt            = is_string($post_dt = $rs->post_dt) ? $post_dt : '';
        $post_id            = is_numeric($post_id = $rs->post_id) ? (int) $post_id : 0;
        $post_url           = is_string($post_url = $rs->post_titre_url) ? $post_url : '';
        $post_content_xhtml = is_string($post_content_xhtml = $rs->post_content) ? $post_content_xhtml : '';
        $post_excerpt_xhtml = is_string($post_excerpt_xhtml = $rs->post_chapo) ? $post_excerpt_xhtml : '';
        $post_content_wiki  = is_string($post_content_wiki = $rs->post_content_wiki) ? $post_content_wiki : '';
        $post_excerpt_wiki  = is_string($post_excerpt_wiki = $rs->post_chapo_wiki) ? $post_excerpt_wiki : '';
        $post_notes         = is_string($post_notes = $rs->post_notes) ? $post_notes : '';
        $post_status        = is_numeric($post_status = $rs->post_pub) ? (int) $post_status : App::status()->post()::UNPUBLISHED;
        $post_selected      = is_numeric($post_selected = $rs->post_selected) ? (int) $post_selected : 0;
        $post_open_comment  = is_numeric($post_open_comment = $rs->post_open_comment) ? (int) $post_open_comment : 0;
        $post_open_tb       = is_numeric($post_open_tb = $rs->post_open_tb) ? (int) $post_open_tb : 0;
        $post_lang          = is_string($post_lang = $rs->post_lang) ? $post_lang : '';
        $post_creadt        = is_string($post_creadt = $rs->post_creadt) ? $post_creadt : '';
        $post_upddt         = is_string($post_upddt = $rs->post_upddt) ? $post_upddt : '';

        $cur = App::blog()->openPostCursor();
        $cur->setStrField('blog_id', $this->blog_id);
        $cur->setStrField('user_id', $user_id);
        $cur->setIntField('cat_id', $this->vars['cat_ids'][$cat_id]);
        $cur->setStrField('post_dt', $post_dt);
        $cur->setStrField('post_creadt', $post_creadt);
        $cur->setStrField('post_upddt', $post_upddt);
        $cur->setStrField('post_title', Html::decodeEntities(Txt::cleanStr($post_title)));

        $cur->setStrField('post_url', date('Y/m/d/', (int) strtotime($post_dt)) . $post_id . '-' . $post_url);
        $cur->setStrField('post_url', substr($cur->strField('post_url'), 0, 255));

        $cur->setStrField('post_format', $post_content_wiki === '' ? 'xhtml' : 'wiki');
        $cur->setStrField('post_content_xhtml', Txt::cleanStr($post_content_xhtml));
        $cur->setStrField('post_excerpt_xhtml', Txt::cleanStr($post_excerpt_xhtml));

        if ($cur->strField('post_format') === 'wiki') {
            $cur->setStrField('post_content', Txt::cleanStr($post_content_wiki));
            $cur->setStrField('post_excerpt', Txt::cleanStr($post_excerpt_wiki));
        } else {
            $cur->setStrField('post_content', Txt::cleanStr($post_content_xhtml));
            $cur->setStrField('post_excerpt', Txt::cleanStr($post_excerpt_xhtml));
        }

        $cur->setStrField('post_notes', Txt::cleanStr($post_notes));
        $cur->setIntField('post_status', $post_status);
        $cur->setBoolField('post_selected', $post_selected);
        $cur->setBoolField('post_open_comment', $post_open_comment);
        $cur->setBoolField('post_open_tb', $post_open_tb);
        $cur->setStrField('post_lang', $post_lang);

        $cur->setStrField('post_words', implode(' ', Txt::splitWords(
            $cur->strField('post_title') . ' ' .
            $cur->strField('post_excerpt_xhtml') . ' ' .
            $cur->strField('post_content_xhtml')
        )));

        $new_post_id = is_numeric($new_post_id = (new MetaRecord($this->con->select(
            'SELECT MAX(post_id) FROM ' . $this->prefix . App::blog()::POST_TABLE_NAME
        )))->f(0)) ? (int) $new_post_id : 0;
        $new_post_id++;

        $cur->setIntField('post_id', $new_post_id);

        $cur->insert();
        $this->importComments($post_id, $new_post_id, $db);
        $this->importPings($post_id, $new_post_id, $db);

        # Load meta if we have some in DC1
        if (isset($this->has_table[$this->vars['db_prefix'] . 'post_meta'])) {
            $this->importMeta($post_id, $new_post_id, $db);
        }
    }

    /**
     * Comments import.
     *
     * @param   int                     $post_id        The post identifier
     * @param   int                     $new_post_id    The new post identifier
     * @param   ConnectionInterface     $db             The database
     */
    protected function importComments(int $post_id, int $new_post_id, ConnectionInterface $db): void
    {
        $count_c = 0;
        $count_t = 0;
        $rs      = $db->select(
            'SELECT * FROM ' . $this->vars['db_prefix'] . 'comment ' .
            'WHERE post_id = ' . $post_id . ' '
        );

        while ($rs->fetch()) {
            $comment_author    = is_string($comment_author = $rs->comment_auteur) ? $comment_author : '';
            $comment_status    = is_numeric($comment_status = $rs->comment_pub) ? (int) $comment_status : App::status()->comment()::UNPUBLISHED;
            $comment_email     = is_string($comment_email = $rs->comment_email) ? $comment_email : '';
            $comment_content   = is_string($comment_content = $rs->comment_content) ? $comment_content : '';
            $comment_trackback = is_numeric($comment_trackback = $rs->comment_trackback) ? (int) $comment_trackback : 0;
            $comment_site      = is_string($comment_site = $rs->comment_site) ? $comment_site : '';
            $comment_dt        = is_string($comment_dt = $rs->comment_dt) ? $comment_dt : '';
            $comment_upddt     = is_string($comment_upddt = $rs->comment_upddt) ? $comment_upddt : '';
            $comment_ip        = is_string($comment_ip = $rs->comment_ip) ? $comment_ip : '';

            $cur = App::blog()->openCommentCursor();
            $cur->setIntField('post_id', $new_post_id);
            $cur->setStrField('comment_author', Txt::cleanStr($comment_author));
            $cur->setIntField('comment_status', $comment_status);
            $cur->setStrField('comment_dt', $comment_dt);
            $cur->setStrField('comment_upddt', $comment_upddt);
            $cur->setStrField('comment_email', Txt::cleanStr($comment_email));
            $cur->setStrField('comment_content', Txt::cleanStr($comment_content));
            $cur->setStrField('comment_ip', $comment_ip);
            $cur->setBoolField('comment_trackback', $comment_trackback);

            $cur->setStrField('comment_site', Txt::cleanStr($comment_site));
            if ($comment_site !== '' && !preg_match('!^http(s)?://.*$!', $comment_site)) {
                // Use https protocol rather than http (since 2.37)
                $cur->setStrField('comment_site', substr('https://' . $comment_site, 0, 255));
            }

            if ($rs->exists('spam') && $rs->spam && $comment_status === App::status()->comment()::UNPUBLISHED) {
                $cur->setIntField('comment_status', App::status()->comment()::JUNK);
            }

            $cur->setStrField('comment_words', implode(' ', Txt::splitWords($cur->strField('comment_content'))));

            $new_comment_id = is_numeric($new_comment_id = (new MetaRecord($this->con->select(
                'SELECT MAX(comment_id) FROM ' . $this->prefix . App::blog()::COMMENT_TABLE_NAME
            )))->f(0)) ? (int) $new_comment_id : 0;
            $new_comment_id++;

            $cur->setIntField('comment_id', $new_comment_id);
            $cur->insert();

            if ($cur->intField('comment_status') === App::status()->comment()::PUBLISHED) {
                if ($cur->boolField('comment_trackback')) {
                    $count_t++;
                } else {
                    $count_c++;
                }
            }
        }

        if ($count_t > 0 || $count_c > 0) {
            $this->con->execute(
                'UPDATE ' . $this->prefix . App::blog()::POST_TABLE_NAME . ' SET ' .
                'nb_comment = ' . $count_c . ', ' .
                'nb_trackback = ' . $count_t . ' ' .
                'WHERE post_id = ' . $new_post_id . ' '
            );
        }
    }

    /**
     * Pings import.
     *
     * @param   int                     $post_id        The post identifier
     * @param   int                     $new_post_id    The new post identifier
     * @param   ConnectionInterface     $db             The database
     */
    protected function importPings(int $post_id, int $new_post_id, ConnectionInterface $db): void
    {
        $urls = [];

        $rs = $db->select(
            'SELECT * FROM ' . $this->vars['db_prefix'] . 'ping ' .
            'WHERE post_id = ' . $post_id
        );

        while ($rs->fetch()) {
            $ping_url = is_string($ping_url = $rs->ping_url) ? $ping_url : '';
            $ping_dt  = is_string($ping_dt = $rs->ping_dt) ? $ping_dt : '';

            $url = Txt::cleanStr($ping_url);
            if ($url === '') {
                continue;
            }

            if (isset($urls[$url])) {
                continue;
            }

            $cur = App::trackback()->openTrackbackCursor();
            $cur->setIntField('post_id', $new_post_id);
            $cur->setStrField('ping_url', $url);
            $cur->setStrField('ping_dt', $ping_dt);
            $cur->insert();

            $urls[$url] = true;
        }
    }

    /**
     * Meta import.
     *
     * @param   int                     $post_id        The post identifier
     * @param   int                     $new_post_id    The new post identifier
     * @param   ConnectionInterface     $db             The database
     */
    protected function importMeta(int $post_id, int $new_post_id, ConnectionInterface $db): void
    {
        $rs = $db->select(
            'SELECT * FROM ' . $this->vars['db_prefix'] . 'post_meta ' .
            'WHERE post_id = ' . $post_id . ' '
        );

        if ($rs->isEmpty()) {
            return;
        }

        while ($rs->fetch()) {
            $meta_key   = is_string($meta_key = $rs->meta_key) ? $meta_key : '';
            $meta_value = is_string($meta_value = $rs->meta_value) ? $meta_value : '';

            App::meta()->setPostMeta($new_post_id, Txt::cleanStr($meta_key), Txt::cleanStr($meta_value));
        }
    }
}
