From 3c9db1b28b233781f3881fd3c50424404a93955b Mon Sep 17 00:00:00 2001 From: alejoasotelo Date: Thu, 8 Jun 2023 20:37:31 -0300 Subject: [PATCH] =?UTF-8?q?[+]=20Agregu=C3=A9=20la=20importaci=C3=B3n=20de?= =?UTF-8?q?=20categor=C3=AD=C3=A1s=20y=20post=20de=20wordpress=20a=20jooml?= =?UTF-8?q?a?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/release.yml | 36 +++ build/build_changelog.php | 111 +++++++++ build/build_plugin.php | 57 +++++ .../sql/install.mysql.utf8.sql | 19 ++ .../sql/uninstall.mysql.utf8.sql | 2 + .../sql/updates/mysql/1.0.3.sql | 23 ++ .../sql/updates/mysql/1.0.4.sql | 2 + .../sql/updates/mysql/1.0.5.sql | 2 + .../AlejoASotelo/Adapter/WordpressAdapter.php | 217 ++++++++++++++++++ .../Adapter/Wp2JoomlaAdapterInterface.php | 31 +++ .../Console/MigrateArticlesCommand.php | 179 +++++++++++++++ .../Console/MigrateCategoriesCommand.php | 212 ++--------------- .../src/AlejoASotelo/Import/Importer.php | 116 ++++++++++ .../AlejoASotelo/Table/ArticleFinalTable.php | 55 +++++ .../AlejoASotelo/Table/CategoryFinalTable.php | 55 +++++ .../Table/MigratorArticleTable.php | 29 +++ .../Table/MigratorCategoryTable.php | 56 +++++ plg_system_wp2joomla/wp2joomla.php | 16 +- plg_system_wp2joomla/wp2joomla.xml | 18 +- readme.md | 20 ++ 20 files changed, 1057 insertions(+), 199 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 build/build_changelog.php create mode 100644 build/build_plugin.php create mode 100644 plg_system_wp2joomla/sql/install.mysql.utf8.sql create mode 100644 plg_system_wp2joomla/sql/uninstall.mysql.utf8.sql create mode 100644 plg_system_wp2joomla/sql/updates/mysql/1.0.3.sql create mode 100644 plg_system_wp2joomla/sql/updates/mysql/1.0.4.sql create mode 100644 plg_system_wp2joomla/sql/updates/mysql/1.0.5.sql create mode 100644 plg_system_wp2joomla/src/AlejoASotelo/Adapter/WordpressAdapter.php create mode 100644 plg_system_wp2joomla/src/AlejoASotelo/Adapter/Wp2JoomlaAdapterInterface.php create mode 100644 plg_system_wp2joomla/src/AlejoASotelo/Console/MigrateArticlesCommand.php create mode 100644 plg_system_wp2joomla/src/AlejoASotelo/Import/Importer.php create mode 100644 plg_system_wp2joomla/src/AlejoASotelo/Table/ArticleFinalTable.php create mode 100644 plg_system_wp2joomla/src/AlejoASotelo/Table/CategoryFinalTable.php create mode 100644 plg_system_wp2joomla/src/AlejoASotelo/Table/MigratorArticleTable.php create mode 100644 plg_system_wp2joomla/src/AlejoASotelo/Table/MigratorCategoryTable.php create mode 100644 readme.md diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..d4648e6 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,36 @@ +# Workflow que genera un release en github con el zip del componente usando softprops/action-gh-release +# 1. Se ejecuta cuando se crea un tag en la rama master +# 2. ejecuta `php build/build-component.php` para generar el zip +# 3. Crea un release en github con el zip generado en dist/plg_system_wp2joomla.zip +# 4. Crea un tag en github con el nombre del tag creado en master + +name: Release +on: + push: + tags: + - "*.*.*" +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Build plugin + run: php build/build_plugin.php + - name: Build CHANGELOG.md + run: php build/build_changelog.php --from=$(git tag --sort=version:refname | tail -n2 | sort -r | tail -n1) --dest=dist/CHANGELOG.md + - name: Get release name + id: getReleaseName + run: echo ::set-output name=RELEASE_NAME::${GITHUB_REF/refs\/tags\//} + - name: Create Release + uses: softprops/action-gh-release@v1 + with: + files: dist/plg_system_wp2joomla.zip + name: v${{ steps.getReleaseName.outputs.RELEASE_NAME }} + tag_name: ${{ steps.getReleaseName.outputs.RELEASE_NAME }} + body_path: dist/CHANGELOG.md + draft: false + prerelease: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/build/build_changelog.php b/build/build_changelog.php new file mode 100644 index 0000000..858ebbd --- /dev/null +++ b/build/build_changelog.php @@ -0,0 +1,111 @@ +:' . PHP_TAB . 'El tag de la versión desde la cual se van a obtener los commits (ex: `tags/3.8.6`, `4.0-dev`)' . PHP_EOL; + echo PHP_TAB . PHP_TAB . '--version :' . PHP_TAB . 'La versión para agregar en el changelog' . PHP_EOL; + echo PHP_TAB . PHP_TAB . '--help:' . PHP_TAB . PHP_TAB . PHP_TAB . 'Show this help output' . PHP_EOL . PHP_EOL; + echo PHP_TAB . PHP_TAB . 'Ejemplo: php build_changelog.php --from=1.12.0 --version=1.13.0' . PHP_EOL; + echo PHP_EOL; +} + +if (version_compare(PHP_VERSION, '5.4', '<')) +{ + echo "The build script requires PHP 5.4.\n"; + + exit(1); +} + +$time = time(); + +// Set path to git binary (e.g., /usr/local/git/bin/git or /usr/bin/git) +ob_start(); +passthru('which git', $systemGit); +$systemGit = trim(ob_get_clean()); + +if (empty($systemGit)) { + ob_start(); + passthru('where git', $systemGit); + $systemGit = trim(ob_get_clean()); +} + +if (empty($systemGit)) +{ + die('Install Git'); +} + +// Make sure file and folder permissions are set correctly +umask(022); + +// Shortcut the paths to the repository root and build folder +$repo = dirname(__DIR__); +$here = __DIR__; + +// Set paths for the build packages +$tmp = $here . '/tmp'; +$fullpath = $tmp . '/' . $time; + +// Parse input options +$options = getopt('', array('help', 'from::', 'version::', 'dest::')); + +$from = $options['from']; +$version = isset($options['version']) ? $options['version'] : ''; +$dest = isset($options['dest']) ? $options['dest'] : $repo . '/CHANGELOG.md'; +$showHelp = isset($options['help']); + +if ($showHelp) +{ + usage($argv[0]); + die; +} + +if (empty($from)){ + usage($argv[0]); + die; +} + +chdir($repo); +ob_start(); +system('"'.$systemGit . '" log --pretty=oneline HEAD...' . $from, $commits); +$commits = explode("\n", trim(ob_get_clean())); + +/* +echo "Start build for remote $remote.\n"; +echo "Delete old release folder.\n"; +system('rm -rf ' . $tmp); +mkdir($tmp); +mkdir($fullpath); +*/ +$data = '# Versión'.(!empty($version) ? ' ' . $version : '').' - '.date('d/m/Y'); +$data .= "\n\n"; + +foreach ($commits as &$commit) { + + $parts = explode(' ', $commit); + unset($parts[0]); + $commit = implode(' ', array_values($parts)); + + if (strpos($commit, '[*]') === false && + strpos($commit, '[+]') === false && + strpos($commit, '[-]') === false && + strpos($commit, '[x]') === false) + { + continue; + } + + $data .= '- ' . $commit."\n"; +} + +$filename = $dest; + +if (file_exists($filename)) { + $content = file_get_contents($filename); + $data .= "\n".$content; +} + +file_put_contents($filename, $data); \ No newline at end of file diff --git a/build/build_plugin.php b/build/build_plugin.php new file mode 100644 index 0000000..c5f86d7 --- /dev/null +++ b/build/build_plugin.php @@ -0,0 +1,57 @@ + diff --git a/plg_system_wp2joomla/sql/install.mysql.utf8.sql b/plg_system_wp2joomla/sql/install.mysql.utf8.sql new file mode 100644 index 0000000..e029cc8 --- /dev/null +++ b/plg_system_wp2joomla/sql/install.mysql.utf8.sql @@ -0,0 +1,19 @@ +CREATE TABLE `#__wp2joomla_categories` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `id_joomla` INT(11) NOT NULL, + `id_adapter` INT(11) NOT NULL, + `title` VARCHAR(255) NOT NULL, + `created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `modified` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + PRIMARY KEY (`id`) +) ENGINE=MyISAM AUTO_INCREMENT=0 DEFAULT CHARSET=utf8; + +CREATE TABLE `#__wp2joomla_articles` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `id_joomla` INT(11) NOT NULL, + `id_adapter` INT(11) NOT NULL, + `title` VARCHAR(255) NOT NULL, + `created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `modified` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + PRIMARY KEY (`id`) +) ENGINE=MyISAM AUTO_INCREMENT=0 DEFAULT CHARSET=utf8; diff --git a/plg_system_wp2joomla/sql/uninstall.mysql.utf8.sql b/plg_system_wp2joomla/sql/uninstall.mysql.utf8.sql new file mode 100644 index 0000000..db87ff9 --- /dev/null +++ b/plg_system_wp2joomla/sql/uninstall.mysql.utf8.sql @@ -0,0 +1,2 @@ +DROP TABLE IF EXISTS `#__wp2joomla_categories`; +DROP TABLE IF EXISTS `#__wp2joomla_articles`; \ No newline at end of file diff --git a/plg_system_wp2joomla/sql/updates/mysql/1.0.3.sql b/plg_system_wp2joomla/sql/updates/mysql/1.0.3.sql new file mode 100644 index 0000000..578c76a --- /dev/null +++ b/plg_system_wp2joomla/sql/updates/mysql/1.0.3.sql @@ -0,0 +1,23 @@ +DROP TABLE IF EXISTS `#__wp2joomla_categories`; + +CREATE TABLE `#__wp2joomla_categories` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `id_joomla` INT(11) NOT NULL, + `id_wordpress` INT(11) NOT NULL, + `name` VARCHAR(255) NOT NULL, + `created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `modified` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + PRIMARY KEY (`id`) +) ENGINE=MyISAM AUTO_INCREMENT=0 DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `#__wp2joomla_articles`; + +CREATE TABLE `#__wp2joomla_articles` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `id_joomla` INT(11) NOT NULL, + `id_wordpress` INT(11) NOT NULL, + `name` VARCHAR(255) NOT NULL, + `created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `modified` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + PRIMARY KEY (`id`) +) ENGINE=MyISAM AUTO_INCREMENT=0 DEFAULT CHARSET=utf8; diff --git a/plg_system_wp2joomla/sql/updates/mysql/1.0.4.sql b/plg_system_wp2joomla/sql/updates/mysql/1.0.4.sql new file mode 100644 index 0000000..8c3fed7 --- /dev/null +++ b/plg_system_wp2joomla/sql/updates/mysql/1.0.4.sql @@ -0,0 +1,2 @@ +ALTER TABLE `#__wp2joomla_categories` CHANGE `name` `title` VARCHAR(255) NOT NULL; +ALTER TABLE `#__wp2joomla_articles` CHANGE `name` `title` VARCHAR(255) NOT NULL; \ No newline at end of file diff --git a/plg_system_wp2joomla/sql/updates/mysql/1.0.5.sql b/plg_system_wp2joomla/sql/updates/mysql/1.0.5.sql new file mode 100644 index 0000000..90f475a --- /dev/null +++ b/plg_system_wp2joomla/sql/updates/mysql/1.0.5.sql @@ -0,0 +1,2 @@ +ALTER TABLE `#__wp2joomla_categories` CHANGE `id_wordpress` `id_adapter` INT(11) NOT NULL; +ALTER TABLE `#__wp2joomla_articles` CHANGE `id_wordpress` `id_adapter` INT(11) NOT NULL; \ No newline at end of file diff --git a/plg_system_wp2joomla/src/AlejoASotelo/Adapter/WordpressAdapter.php b/plg_system_wp2joomla/src/AlejoASotelo/Adapter/WordpressAdapter.php new file mode 100644 index 0000000..2568f8d --- /dev/null +++ b/plg_system_wp2joomla/src/AlejoASotelo/Adapter/WordpressAdapter.php @@ -0,0 +1,217 @@ +db = $db; + $this->user = $user; + } + + /** + * Undocumented function + * + * @return array + */ + public function listArticles() + { + $wpArticles = $this->getPosts(); + + $categoryDefault = 2; + $category = new MigratorCategoryTable($this->db); + + $articles = []; + + foreach ($wpArticles as $wpArticle) { + $wpCategories = $this->getCategoriesFromPost($wpArticle->id); + + if (count($wpCategories) > 0) { + $category->load(['id_adapter' => $wpCategories[0]]); + $categoryId = !$category->id ? $categoryDefault : $category->id_joomla; + } else { + $categoryId = $categoryDefault; + } + + $article = new ArticleFinalTable($this->db); + $article->id_adapter = $wpArticle->id; + $article->catid = $categoryId; + $article->title = $wpArticle->title; + $article->alias = \JFilterOutput::stringURLSafe($wpArticle->title); + $article->published = 1; + $article->state = 1; + $article->language = '*'; + $article->introtext = $wpArticle->content; + $article->fulltext = ''; + $article->created = date('Y-m-d H:i:s', strtotime($wpArticle->created)); + $article->created_by = $this->user->id; + $article->created_by_alias = $this->user->name; + $article->modified = date('Y-m-d H:i:s', strtotime($wpArticle->modified)); + $article->modified_by = $this->user->id; + $article->publish_up =null; + $article->hits = 0; + $article->metakey = ''; + $article->metadesc = ''; + $article->access = 1; + $article->hits = 0; + $article->featured = 0; + $article->images = '{}'; + $article->urls = '{}'; + $article->attribs = '{}'; + $article->metadata = '{}'; + + $articles[] = $article; + } + + return $articles; + } + + /** + * Undocumented function + * + * @return array + */ + public function listCategories() + { + $wpCategories = $this->getCategories(); + + $categories = []; + + foreach ($wpCategories as $wpCategory) { + $category = new CategoryFinalTable($this->db); + $category->id_adapter = $wpCategory->id; + $category->title = $wpCategory->name; + $category->alias = \JFilterOutput::stringURLSafe($wpCategory->name); + $category->extension = 'com_content'; + $category->published = 1; + $category->language = '*'; + $category->params = ['category_layout' => '', 'image' => '']; + $category->metadata = ['author' => '', 'robots' => '']; + $category->rules = [ + 'core.edit.state' => [], + 'core.edit.delete' => [], + 'core.edit.edit' => [], + 'core.edit.state' => [], + 'core.edit.own' => [1 => true] + ]; + + $categories[] = $category; + } + + return $categories; + } + + protected function getCategories() + { + $cacheId = 'wp_categories'; + + if (!isset($this->cache[$cacheId])) { + $db = $this->db; + $query = $this->getWPTermsQuery($db, 'category'); + $db->setQuery($query); + + $this->cache[$cacheId] = $db->loadObjectList(); + } + + return $this->cache[$cacheId]; + } + + public function getCategoriesFromPost($postId) + { + $db = $this->db; + $query = $db->getQuery(true) + ->select('terms.term_id') + ->from($db->qn('wp_terms', 'terms')) + ->innerJoin($db->qn('wp_term_taxonomy', 'taxonomy') . ' ON ' . $db->qn('terms.term_id') . ' = ' . $db->qn('taxonomy.term_id')) + ->innerJoin($db->qn('wp_term_relationships', 'relationships') . ' ON ' . $db->qn('taxonomy.term_taxonomy_id') . ' = ' . $db->qn('relationships.term_taxonomy_id')) + ->innerJoin($db->qn('wp_posts', 'posts') . ' ON ' . $db->qn('relationships.object_id') . ' = ' . $db->qn('posts.ID')) + ->where($db->qn('taxonomy.taxonomy') . ' = ' . $db->q('category')) + ->where($db->qn('posts.ID') . ' = ' . $db->q($postId)); + + $db->setQuery($query); + + $wpCategories = $db->loadObjectList(); + + $categories = []; + + foreach ($wpCategories as $wpCategory) { + $categories[] = $wpCategory->term_id; + } + + return $categories; + } + + protected function getPosts() + { + // SELECT * FROM wp_posts WHERE ID IN (SELECT object_id FROM wp_term_relationships WHERE wp_posts.post_type = 'post') + $db = $this->db; + $query = $db->getQuery(true) + ->select('ID as id, post_title as title, post_name alias, post_content as content, post_excerpt as introtext, post_date as created, post_modified as modified') + ->from($db->qn('wp_posts')) + ->where($db->qn('ID') . ' IN (SELECT object_id FROM wp_term_relationships WHERE wp_posts.post_type = "post")'); + + $db->setQuery($query); + + return $db->loadObjectList(); + } + + protected function getWPTags() + { + $cacheId = 'wp_categories'; + + if (!isset($this->cache[$cacheId])) { + $db = $this->db; + $query = $this->getWPTermsQuery($db, 'post_tag'); + $db->setQuery($query); + + $this->cache[$cacheId] = $db->loadObjectList(); + } + + return $this->cache[$cacheId]; + } + + protected function getWPTermsQuery($db, $taxonomy) + { + // SELECT * FROM wp_terms WHERE term_id IN (SELECT term_id FROM wp_term_taxonomy WHERE taxonomy = 'category') + // SELECT * FROM wp_terms WHERE term_id IN (SELECT term_id FROM wp_term_taxonomy WHERE taxonomy = 'post_tag') + $query = $db->getQuery(true) + ->select('term_id as id, name') + ->from($db->qn('wp_terms')) + ->where($db->qn('term_id') . ' IN (SELECT term_id FROM wp_term_taxonomy WHERE taxonomy = :taxonomy)') + ->bind(':taxonomy', $taxonomy); + + return $query; + } + + public function setDatabase($db) + { + $this->db = $db; + } + + public function getDatabase() + { + return $this->db; + } + + public function setUser($user) + { + $this->user = $user; + } + + public function getUser() + { + return $this->user; + } + +} diff --git a/plg_system_wp2joomla/src/AlejoASotelo/Adapter/Wp2JoomlaAdapterInterface.php b/plg_system_wp2joomla/src/AlejoASotelo/Adapter/Wp2JoomlaAdapterInterface.php new file mode 100644 index 0000000..87453f8 --- /dev/null +++ b/plg_system_wp2joomla/src/AlejoASotelo/Adapter/Wp2JoomlaAdapterInterface.php @@ -0,0 +1,31 @@ +setDatabase($db); + } + + /** + * Internal function to execute the command. + * + * @param InputInterface $input The input to inject into the command. + * @param OutputInterface $output The output to inject into the command. + * + * @return integer The command exit code + * + * @since 4.0.0 + */ + protected function doExecute(InputInterface $input, OutputInterface $output): int + { + $this->configureIO($input, $output); + $this->ioStyle->title('Ingrese un Id de usuario'); + $this->userId = (int)$this->getStringFromOption('userId', 'Por favor ingrese un ID de usuario'); + + if (!$this->userId) { + $this->ioStyle->error("El usuario #" . $this->userId . " no existe!"); + + return Command::FAILURE; + } + + $this->user = User::getInstance($this->userId); + + if (!$this->user->id) { + $this->ioStyle->error('El usuario con id = ' . $this->userId .' no existe'); + + return Command::FAILURE; + } + + $this->migrateArticles(); + + return Command::SUCCESS; + } + + protected function migrateArticles() + { + $db = $this->getDatabase(); + $adapter = new WordpressAdapter($db, $this->user); + $importer = new Importer($adapter, $this->ioStyle, $db); + $importer->importArticles(); + } + + /** + * Method to get a value from option + * + * @param string $option set the option name + * @param string $question set the question if user enters no value to option + * + * @return string + * + * @since 4.0.0 + */ + public function getStringFromOption($option, $question): string + { + $answer = (string) $this->cliInput->getOption($option); + + while (!$answer) { + if ($option === 'password') { + $answer = (string) $this->ioStyle->askHidden($question); + } else { + $answer = (string) $this->ioStyle->ask($question); + } + } + + return $answer; + } + + /** + * Configure the IO. + * + * @param InputInterface $input The input to inject into the command. + * @param OutputInterface $output The output to inject into the command. + * + * @return void + * + * @since 4.0.0 + */ + private function configureIO(InputInterface $input, OutputInterface $output) + { + $this->cliInput = $input; + $this->ioStyle = new SymfonyStyle($input, $output); + } + + /** + * Configure the command. + * + * @return void + * + * @since 4.0.0 + */ + protected function configure(): void + { + $help = "%command.name% will add a user + \nUsage: php %command.full_name%"; + + $this->addOption('userId', null, InputOption::VALUE_REQUIRED, 'userId'); + $this->setDescription('Ingrese un ID de usuario'); + $this->setHelp($help); + } +} \ No newline at end of file diff --git a/plg_system_wp2joomla/src/AlejoASotelo/Console/MigrateCategoriesCommand.php b/plg_system_wp2joomla/src/AlejoASotelo/Console/MigrateCategoriesCommand.php index 97f595d..b001443 100644 --- a/plg_system_wp2joomla/src/AlejoASotelo/Console/MigrateCategoriesCommand.php +++ b/plg_system_wp2joomla/src/AlejoASotelo/Console/MigrateCategoriesCommand.php @@ -2,18 +2,17 @@ namespace AlejoASotelo\Console; +use AlejoASotelo\Import\Importer; use Joomla\CMS\User\User; use Joomla\Console\Command\AbstractCommand; use Joomla\Database\DatabaseAwareTrait; use Joomla\Database\DatabaseInterface; -use Joomla\Filter\InputFilter; use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Exception\InvalidOptionException; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Question\ChoiceQuestion; use Symfony\Component\Console\Style\SymfonyStyle; +use AlejoASotelo\Adapter\WordpressAdapter; class MigrateCategoriesCommand extends AbstractCommand { @@ -51,6 +50,13 @@ class MigrateCategoriesCommand extends AbstractCommand */ private $userId; + /** + * Undocumented variable + * + * @var User + */ + protected $user; + protected $cache = []; /** @@ -82,7 +88,6 @@ protected function doExecute(InputInterface $input, OutputInterface $output): in $this->configureIO($input, $output); $this->ioStyle->title('Ingrese un Id de usuario'); $this->userId = (int)$this->getStringFromOption('userId', 'Por favor ingrese un ID de usuario'); - $action = $this->getStringFromOption('action', 'Por favor ingrese una acción: migrate-categories or migrate-articles'); if (!$this->userId) { $this->ioStyle->error("El usuario #" . $this->userId . " no existe!"); @@ -90,150 +95,25 @@ protected function doExecute(InputInterface $input, OutputInterface $output): in return Command::FAILURE; } - if (!in_array($action, ['migrate-categories', 'migrate-articles'])) { - $this->ioStyle->error("La acción " . $action . " no existe!"); - - return Command::FAILURE; - } - - $user = User::getInstance($this->userId); + $this->user = User::getInstance($this->userId); - if (!$user->id) { + if (!$this->user->id) { $this->ioStyle->error('El usuario con id = ' . $this->userId .' no existe'); return Command::FAILURE; } - switch ($action) { - case 'migrate-categories': - $this->migrateCategories(); - break; - case 'migrate-articles': - $this->migrateArticles(); - break; - } + $this->migrateCategories(); return Command::SUCCESS; } - protected function migrateCategories() { - $wpCategories = $this->getWPCategories(); - - foreach ($wpCategories as $wpCategory) { - $category = $this->createJoomlaCategory($wpCategory); - if (!$category) { - $this->ioStyle->error('Error al crear la categoría: ' . $wpCategory->name); - } else { - $this->ioStyle->writeln('Category created: ' . $category->title); - } - } - - $this->ioStyle->success("Categorías migradas!"); - } - - protected function createJoomlaCategory($wpCategory) - { - $joomlaCategory = new \JTableCategory($this->getDatabase()); - $joomlaCategory->title = $wpCategory->name; - $joomlaCategory->alias = \JFilterOutput::stringURLSafe($wpCategory->name); - $joomlaCategory->extension = 'com_content'; - $joomlaCategory->published = 1; - - if (!$joomlaCategory->store()) { - $this->ioStyle->error('Error al crear la categoría: ' . \JText::_($joomlaCategory->getError())); - return false; - } - - return $joomlaCategory; - } - - protected function migrateArticles() { - $wpArticles = $this->getWPArticles(); - - foreach ($wpArticles as $wpArticle) { - $this->ioStyle->writeln('Article: ' . $wpArticle->title); - } - - $this->ioStyle->success(count($wpArticles) . " . Artículos migrados!"); - } - - protected function getWPCategories() - { - $cacheId = 'wp_categories'; - - if (!isset($this->cache[$cacheId])) { - $db = $this->getDatabase(); - $query = $this->getWPTermsQuery($db, 'category'); - $db->setQuery($query); - - $this->cache[$cacheId] = $db->loadObjectList(); - } - - return $this->cache[$cacheId]; - } - - protected function getWPTags() - { - $cacheId = 'wp_categories'; - - if (!isset($this->cache[$cacheId])) { - $db = $this->getDatabase(); - $query = $this->getWPTermsQuery($db, 'post_tag'); - $db->setQuery($query); - - $this->cache[$cacheId] = $db->loadObjectList(); - } - - return $this->cache[$cacheId]; - } - - protected function getWPTermsQuery($db, $taxonomy) + protected function migrateCategories() { - // SELECT * FROM wp_terms WHERE term_id IN (SELECT term_id FROM wp_term_taxonomy WHERE taxonomy = 'category') - // SELECT * FROM wp_terms WHERE term_id IN (SELECT term_id FROM wp_term_taxonomy WHERE taxonomy = 'post_tag') - $query = $db->getQuery(true) - ->select('term_id as id, name') - ->from($db->qn('wp_terms')) - ->where($db->qn('term_id') . ' IN (SELECT term_id FROM wp_term_taxonomy WHERE taxonomy = :taxonomy)') - ->bind(':taxonomy', $taxonomy); - - return $query; - } - - protected function getWPArticles() - { - // SELECT * FROM wp_posts WHERE ID IN (SELECT object_id FROM wp_term_relationships WHERE wp_posts.post_type = 'post') - $db = $this->getDatabase(); - $query = $db->getQuery(true) - ->select('ID as id, post_title as title, post_name alias, post_content as content, post_excerpt as introtext, post_date as created, post_modified as modified') - ->from($db->qn('wp_posts')) - ->where($db->qn('ID') . ' IN (SELECT object_id FROM wp_term_relationships WHERE wp_posts.post_type = "post")'); - - $db->setQuery($query); - - return $db->loadObjectList(); - } - - /** - * Method to get groupId by groupName - * - * @param string $groupName name of group - * - * @return integer - * - * @since 4.0.0 - */ - protected function getGroupId($groupName) - { - $db = $this->getDatabase(); - $query = $db->getQuery(true) - ->select($db->quoteName('id')) - ->from($db->quoteName('#__usergroups')) - ->where($db->quoteName('title') . ' = :groupName') - ->bind(':groupName', $groupName); - $db->setQuery($query); - - return $db->loadResult(); + $db = $this->getDatabase(); + $adapter = new WordpressAdapter($db, $this->user); + $importer = new Importer($adapter, $this->ioStyle, $db); + $importer->importCategories(); } /** @@ -261,62 +141,6 @@ public function getStringFromOption($option, $question): string return $answer; } - /** - * Method to get a value from option - * - * @return array - * - * @since 4.0.0 - */ - protected function getUserGroups(): array - { - $groups = $this->getApplication()->getConsoleInput()->getOption('usergroup'); - $db = $this->getDatabase(); - - $groupList = []; - - // Group names have been supplied as input arguments - if (!\is_null($groups) && $groups[0]) { - $groups = explode(',', $groups); - - foreach ($groups as $group) { - $groupId = $this->getGroupId($group); - - if (empty($groupId)) { - $this->ioStyle->error("Invalid group name '" . $group . "'"); - throw new InvalidOptionException("Invalid group name " . $group); - } - - $groupList[] = $this->getGroupId($group); - } - - return $groupList; - } - - // Generate select list for user - $query = $db->getQuery(true) - ->select($db->quoteName('title')) - ->from($db->quoteName('#__usergroups')) - ->order($db->quoteName('id') . 'ASC'); - $db->setQuery($query); - - $list = $db->loadColumn(); - - $choice = new ChoiceQuestion( - 'Please select a usergroup (separate multiple groups with a comma)', - $list - ); - $choice->setMultiselect(true); - - $answer = (array) $this->ioStyle->askQuestion($choice); - - foreach ($answer as $group) { - $groupList[] = $this->getGroupId($group); - } - - return $groupList; - } - /** * Configure the IO. * @@ -347,8 +171,6 @@ protected function configure(): void $this->addOption('userId', null, InputOption::VALUE_REQUIRED, 'userId'); $this->setDescription('Ingrese un ID de usuario'); - $this->addOption('action', null, InputOption::VALUE_REQUIRED, 'action'); - $this->setDescription('Ingrese una acción'); $this->setHelp($help); } } \ No newline at end of file diff --git a/plg_system_wp2joomla/src/AlejoASotelo/Import/Importer.php b/plg_system_wp2joomla/src/AlejoASotelo/Import/Importer.php new file mode 100644 index 0000000..cd9ec7b --- /dev/null +++ b/plg_system_wp2joomla/src/AlejoASotelo/Import/Importer.php @@ -0,0 +1,116 @@ +adapter = $adapter; + $this->db = $db; + $this->io = $io; + } + + public function import() + { + $categories = $this->adapter->listCategories(); + $products = $this->adapter->listArticles(); + + // Procesar y almacenar los productos y categorías en tu componente Joomla + // ... + + + $this->io->writeln("Categorías importadas:"); + foreach ($categories as $category) { + $this->io->writeln($category->getName()); + } + + $this->io->writeln("Artículos importados:"); + foreach ($products as $product) { + $this->io->writeln($product->getName()); + } + } + + public function importCategories() + { + $categories = $this->adapter->listCategories(); + + foreach ($categories as $adapterCategory) { + /** @var CategoryFinalTable $category */ + $category = $this->saveCategory($adapterCategory); + + if (!$category) { + $this->io->error('Error al crear la categoría: ' . $adapterCategory->title); + } else { + $this->io->writeln('Categoría '.($category->isNew ? 'creada' : 'actualizada').': ' . $category->title); + } + } + + $this->io->success("Categorías migradas!"); + + return true; + } + + public function importArticles() + { + $articles = $this->adapter->listArticles(); + + foreach ($articles as $adapterArticle) { + /** @var ArticleFinalTable $article */ + $article = $this->saveArticle($adapterArticle); + + if (!$article) { + $this->io->error('Error al crear el artículo: ' . $adapterArticle->title); + } else { + $this->io->writeln('Artículo '.($article->isNew ? 'creado' : 'actualizado').': ' . $article->title); + } + } + + $this->io->success("Artículos migrados!"); + + return true; + } + + /** + * Undocumented function + * + * @param CategoryFinalTable $categoryFinal + * @return void + */ + protected function saveCategory($categoryFinal) + { + if (!$categoryFinal->store()) { + $this->io->error('Error al crear la categoría: ' . \JText::_($categoryFinal->getError())); + return false; + } + + return $categoryFinal; + } + + /** + * Undocumented function + * + * @param ArticleFinalTable $articleFinal + * @return void + */ + protected function saveArticle($articleFinal) + { + if (!$articleFinal->store()) { + $this->io->error('Error al crear el artículo: ' . \JText::_($articleFinal->getError())); + return false; + } + + return $articleFinal; + } + +} diff --git a/plg_system_wp2joomla/src/AlejoASotelo/Table/ArticleFinalTable.php b/plg_system_wp2joomla/src/AlejoASotelo/Table/ArticleFinalTable.php new file mode 100644 index 0000000..7824b81 --- /dev/null +++ b/plg_system_wp2joomla/src/AlejoASotelo/Table/ArticleFinalTable.php @@ -0,0 +1,55 @@ +_db); + $articleMigration->load(['id_adapter' => $this->id_adapter]); + + $this->id = $articleMigration->id_joomla; + $this->isNew = !$articleMigration->id_joomla; + + try { + + if (!parent::store($updateNulls)) { + return false; + } + + $articleMigration->title = $this->title; + $articleMigration->id_joomla = $this->id; + $articleMigration->id_adapter = $this->id_adapter; + + $date = Factory::getDate()->toSql(); + if ($articleMigration->id) { + $articleMigration->modified = $date; + } else { + $articleMigration->created = $date; + } + + $result = $articleMigration->store(); + } catch (\Exception $e) { + $this->setError($e->getMessage()); + $result = false; + } + + return $result; + } +} diff --git a/plg_system_wp2joomla/src/AlejoASotelo/Table/CategoryFinalTable.php b/plg_system_wp2joomla/src/AlejoASotelo/Table/CategoryFinalTable.php new file mode 100644 index 0000000..1a7d4d6 --- /dev/null +++ b/plg_system_wp2joomla/src/AlejoASotelo/Table/CategoryFinalTable.php @@ -0,0 +1,55 @@ +_db); + $migratorCategory->load(['id_adapter' => $this->id_adapter]); + + $this->id = $migratorCategory->id_joomla; + $this->isNew = !$migratorCategory->id_joomla; + + try { + + if (!parent::store($updateNulls)) { + return false; + } + + $migratorCategory->title = $this->title; + $migratorCategory->id_joomla = $this->id; + $migratorCategory->id_adapter = $this->id_adapter; + + $date = Factory::getDate()->toSql(); + if ($migratorCategory->id) { + $migratorCategory->modified = $date; + } else { + $migratorCategory->created = $date; + } + + $result = $migratorCategory->store(); + } catch (\Exception $e) { + $this->setError($e->getMessage()); + $result = false; + } + + return $result; + } +} diff --git a/plg_system_wp2joomla/src/AlejoASotelo/Table/MigratorArticleTable.php b/plg_system_wp2joomla/src/AlejoASotelo/Table/MigratorArticleTable.php new file mode 100644 index 0000000..8a79647 --- /dev/null +++ b/plg_system_wp2joomla/src/AlejoASotelo/Table/MigratorArticleTable.php @@ -0,0 +1,29 @@ +cache[$cacheId])) { + $this->cache[$cacheId] = parent::load($keys, $reset); + } + + return $this->cache[$cacheId]; + } + +} diff --git a/plg_system_wp2joomla/wp2joomla.php b/plg_system_wp2joomla/wp2joomla.php index 9091376..6494650 100644 --- a/plg_system_wp2joomla/wp2joomla.php +++ b/plg_system_wp2joomla/wp2joomla.php @@ -2,15 +2,16 @@ \defined('_JEXEC') or die; -JLoader::registerNamespace('\\AlejoASotelo\\Console', __DIR__ . '/src/AlejoASotelo/Console', false, true,'psr4'); +JLoader::registerNamespace('\\AlejoASotelo', __DIR__ . '/src/AlejoASotelo', false, true,'psr4'); use Joomla\Application\ApplicationEvents; +use Joomla\CMS\Console\Loader\WritableLoaderInterface; use Joomla\CMS\Factory; use Joomla\CMS\Plugin\CMSPlugin; use Joomla\Event\SubscriberInterface; -use AlejoASotelo\Console\MigrateCategoriesCommand; use Psr\Container\ContainerInterface; -use Joomla\CMS\Console\Loader\WritableLoaderInterface; +use AlejoASotelo\Console\MigrateCategoriesCommand; +use AlejoASotelo\Console\MigrateArticlesCommand; class PlgSystemWP2Joomla extends CMSPlugin implements SubscriberInterface { @@ -32,8 +33,17 @@ function (ContainerInterface $container) { }, true ); + + Factory::getContainer()->share( + 'migrate.articles', + function (ContainerInterface $container) { + return new MigrateArticlesCommand($this->db); + }, + true + ); Factory::getContainer()->get(WritableLoaderInterface::class)->add('migrate:categories', 'migrate.categories'); + Factory::getContainer()->get(WritableLoaderInterface::class)->add('migrate:articles', 'migrate.articles'); } } \ No newline at end of file diff --git a/plg_system_wp2joomla/wp2joomla.xml b/plg_system_wp2joomla/wp2joomla.xml index aeb5122..e66264a 100644 --- a/plg_system_wp2joomla/wp2joomla.xml +++ b/plg_system_wp2joomla/wp2joomla.xml @@ -7,11 +7,27 @@ GNU General Public License version 2 or later; see LICENSE.txt soporte@alejosotelo.com.ar https://alejosotelo.com.ar - 1.0.1 + 1.0.5 PLG_SYSTEM_WP2JOOMLA_XML_DESCRIPTION + + + sql/install.mysql.utf8.sql + + + + + sql/uninstall.mysql.utf8.sql + + + + + sql/updates/mysql + + wp2joomla.php language + sql src diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..4f16cbf --- /dev/null +++ b/readme.md @@ -0,0 +1,20 @@ +# Wp2Joomla - Migra artículos de Wordpress a Joomla 4 + +## Instalación + +Importar la base de datos de wordpress con el prefijo wp_ en la misma base de datos que Joomla 4. Una vez importada la base de datos, instalamos el plugin de la carpeta plg_system_wp2joomla en Joomla 4. + +Vamos a la consola de comandos del servidor y nos ubicamos en la carpeta raíz de Joomla 4. + +Importamos las categorías de Wordpress en Joomla 4 con: +```bash +php cli/joomla.php migrate:categories +``` + +y luego los Posts de wordpress con: + +```bash +php cli/joomla.php migrate:articles +``` + +Aclaración: si un post de wordpress tiene más de una categoría se elegirá la primera que se obtenga desde la base de datos. \ No newline at end of file