From 98ac5e06a1720821a2fd6a46b60658da43bd06a9 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 31 Jul 2023 22:50:13 +0200 Subject: [PATCH 001/121] add migration service --- .../layouts/joomgallery/migration/form.php | 57 ++++ .../src/Controller/MigrationController.php | 43 +++ .../src/Extension/JoomgalleryComponent.php | 3 + .../src/Model/MigrationModel.php | 152 +++++++++++ .../src/Service/Migration/Migration.php | 258 ++++++++++++++++++ .../Service/Migration/MigrationInterface.php | 73 +++++ .../Migration/MigrationServiceInterface.php | 50 ++++ .../Migration/MigrationServiceTrait.php | 101 +++++++ .../Service/Migration/Scripts/Jg3ToJg4.jpg | Bin 0 -> 3129 bytes .../Service/Migration/Scripts/Jg3ToJg4.php | 36 +++ .../Service/Migration/Scripts/Jg3ToJg4.xml | 142 ++++++++++ .../src/View/Migration/HtmlView.php | 73 +++++ .../tmpl/migration/default.php | 55 ++++ .../com_joomgallery/tmpl/migration/step1.php | 51 ++++ .../com_joomgallery/tmpl/migration/step2.php | 51 ++++ .../com_joomgallery/tmpl/migration/step3.php | 51 ++++ .../com_joomgallery/tmpl/migration/step4.php | 51 ++++ media/com_joomgallery/css/admin.css | 13 + 18 files changed, 1260 insertions(+) create mode 100644 administrator/com_joomgallery/layouts/joomgallery/migration/form.php create mode 100644 administrator/com_joomgallery/src/Controller/MigrationController.php create mode 100644 administrator/com_joomgallery/src/Model/MigrationModel.php create mode 100644 administrator/com_joomgallery/src/Service/Migration/Migration.php create mode 100644 administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php create mode 100644 administrator/com_joomgallery/src/Service/Migration/MigrationServiceInterface.php create mode 100644 administrator/com_joomgallery/src/Service/Migration/MigrationServiceTrait.php create mode 100644 administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.jpg create mode 100644 administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php create mode 100644 administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.xml create mode 100644 administrator/com_joomgallery/src/View/Migration/HtmlView.php create mode 100644 administrator/com_joomgallery/tmpl/migration/default.php create mode 100644 administrator/com_joomgallery/tmpl/migration/step1.php create mode 100644 administrator/com_joomgallery/tmpl/migration/step2.php create mode 100644 administrator/com_joomgallery/tmpl/migration/step3.php create mode 100644 administrator/com_joomgallery/tmpl/migration/step4.php diff --git a/administrator/com_joomgallery/layouts/joomgallery/migration/form.php b/administrator/com_joomgallery/layouts/joomgallery/migration/form.php new file mode 100644 index 00000000..7cf439e2 --- /dev/null +++ b/administrator/com_joomgallery/layouts/joomgallery/migration/form.php @@ -0,0 +1,57 @@ + ** +** @copyright 2008 - 2022 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 2 or later ** +*****************************************************************************************/ + +defined('_JEXEC') or die; + +use \Joomla\CMS\Language\Text; +use \Joomla\CMS\HTML\HTMLHelper; + +extract((array) $displayData); + +/** + * Layout variables + * ----------------- + * @var string $scriptName The name of the migration script. + * @var string $description The description of the migration script. + * @var string $url The url, where to process the form. + * @var string $task The task to be executed when submitting the form. + * @var array $fieldsets List of fieldsets forming the form. + * @var string $buttonTxt Text shown in the submit button. + */ + +?> + + + +
+ +
+ $fieldset) : ?> +
+
+
+ label); ?> +
+ output; ?> +
+
+
+
+ + + + + + +
+
\ No newline at end of file diff --git a/administrator/com_joomgallery/src/Controller/MigrationController.php b/administrator/com_joomgallery/src/Controller/MigrationController.php new file mode 100644 index 00000000..b7ff47be --- /dev/null +++ b/administrator/com_joomgallery/src/Controller/MigrationController.php @@ -0,0 +1,43 @@ + ** +** @copyright 2008 - 2022 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 2 or later ** +*****************************************************************************************/ + +namespace Joomgallery\Component\Joomgallery\Administrator\Controller; + +\defined('_JEXEC') or die; + +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; +use Joomla\CMS\Router\Route; +use Joomla\CMS\Uri\Uri; + +/** + * Migration controller class. + * + * @package JoomGallery + * @since 4.0.0 + */ +class MigrationController extends JoomAdminController +{ + /** + * Proxy for getModel. + * + * @param string $name Optional. Model name + * @param string $prefix Optional. Class prefix + * @param array $config Optional. Configuration array for model + * + * @return object The Model + * + * @since 4.0.0 + */ + public function getModel($name = 'Migration', $prefix = 'Administrator', $config = array()) + { + return parent::getModel($name, $prefix, array('ignore_request' => true)); + } +} diff --git a/administrator/com_joomgallery/src/Extension/JoomgalleryComponent.php b/administrator/com_joomgallery/src/Extension/JoomgalleryComponent.php index 1630c75c..fce2936b 100644 --- a/administrator/com_joomgallery/src/Extension/JoomgalleryComponent.php +++ b/administrator/com_joomgallery/src/Extension/JoomgalleryComponent.php @@ -42,6 +42,8 @@ use Joomgallery\Component\Joomgallery\Administrator\Service\TusServer\TusServiceTrait; use Joomgallery\Component\Joomgallery\Administrator\Service\Uploader\UploaderServiceInterface; use Joomgallery\Component\Joomgallery\Administrator\Service\Uploader\UploaderServiceTrait; +use Joomgallery\Component\Joomgallery\Administrator\Service\Migration\MigrationServiceInterface; +use Joomgallery\Component\Joomgallery\Administrator\Service\Migration\MigrationServiceTrait; /** * Component class for Joomgallery @@ -75,6 +77,7 @@ class JoomgalleryComponent extends MVCComponent implements BootableExtensionInte use RefresherServiceTrait; use TusServiceTrait; use UploaderServiceTrait; + use MigrationServiceTrait; /** * Storage for the component cache object diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php new file mode 100644 index 00000000..df84a671 --- /dev/null +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -0,0 +1,152 @@ + ** +** @copyright 2008 - 2022 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 2 or later ** +*****************************************************************************************/ + +namespace Joomgallery\Component\Joomgallery\Administrator\Model; + +// No direct access. +defined('_JEXEC') or die; + +use \Joomla\CMS\Factory; +use Joomla\CMS\Form\Form; +use \Joomla\CMS\Language\Text; +use \Joomla\CMS\Uri\Uri; +use \Joomla\CMS\Filesystem\Folder; +use \Joomla\CMS\MVC\Model\BaseDatabaseModel; +use \Joomla\CMS\MVC\Model\FormModelInterface; +use \Joomla\CMS\MVC\Model\FormBehaviorTrait; +use \Joomla\CMS\Form\FormFactoryAwareInterface; +use \Joomla\CMS\Form\FormFactoryAwareTrait; +use \Joomla\CMS\Form\FormFactoryInterface; +use \Joomla\CMS\MVC\Factory\MVCFactoryInterface; + +/** + * Migration model. + * + * @package JoomGallery + * @since 4.0.0 + */ +class MigrationModel extends BaseDatabaseModel implements FormFactoryAwareInterface, FormModelInterface +{ + use FormBehaviorTrait; + use FormFactoryAwareTrait; + + /** + * Maps events to plugin groups. + * + * @var array + * + * @since 3.6 + */ + protected $events_map = null; + + /** + * @var string The prefix to use with controller messages. + * + * @since 4.0.0 + */ + protected $text_prefix = _JOOM_OPTION_UC; + + /** + * @var string Alias to manage history control + * + * @since 4.0.0 + */ + public $typeAlias = _JOOM_OPTION.'.migration'; + + /** + * Constructor + * + * @param array $config An array of configuration options (name, state, dbo, table_path, ignore_request). + * @param MVCFactoryInterface $factory The factory. + * @param FormFactoryInterface $formFactory The form factory. + * + * @since 3.6 + * @throws \Exception + */ + public function __construct($config = [], MVCFactoryInterface $factory = null, FormFactoryInterface $formFactory = null) + { + $config['events_map'] = $config['events_map'] ?? []; + + $this->events_map = array_merge( + ['validate' => 'content'], + $config['events_map'] + ); + + parent::__construct($config, $factory); + + $this->app = Factory::getApplication('administrator'); + $this->component = $this->app->bootComponent(_JOOM_OPTION); + $this->user = Factory::getUser(); + + $this->setFormFactory($formFactory); + } + + /** + * Method to get all available migration scripts. + * + * @return array|boolean List of paths of all available scripts. + * + * @since 4.0.0 + */ + public function getScripts() + { + $files = Folder::files(JPATH_ADMINISTRATOR.'/components/'._JOOM_OPTION.'/src/Service/Migration/Scripts', '.php$', false, true); + + $scripts = array(); + foreach($files as $path) + { + $img = Uri::base().'components/'._JOOM_OPTION.'/src/Service/Migration/Scripts/'.basename($path, '.php').'.jpg'; + + $scripts[basename($path, '.php')] = array('path' => $path, 'img' => $img); + } + + return $scripts; + } + + /** + * Method to get the migration form. + * + * @param array $data An optional array of data for the form to interogate. + * @param boolean $loadData True if the form is to load its own data (default case), false if not. + * + * @return string html output of the migration form + * + * @since 4.0.0 + */ + public function getForm($data = array(), $loadData = true) + { + // Retreive script + $script = $this->app->input->get('script', '', 'cmd'); + + // Use migration service for that + if($script) + { + $this->component->createMigration($script); + + return $this->component->getMigration()->renderForm(); + } + else + { + return ''; + } + } + + /** + * Method to get the data that should be injected in the form. + * + * @return mixed The data for the form. + * + * @since 4.0.0 + */ + protected function loadFormData() + { + // Use migration service for that + } +} diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php new file mode 100644 index 00000000..3cbfec1c --- /dev/null +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -0,0 +1,258 @@ + ** +** @copyright 2008 - 2022 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 2 or later ** +*****************************************************************************************/ + +namespace Joomgallery\Component\Joomgallery\Administrator\Service\Migration; + +// No direct access +\defined('_JEXEC') or die; + +use \Joomla\CMS\Factory; +use \Joomla\CMS\Form\Form; +use \Joomla\CMS\Router\Route; +use \Joomla\CMS\Language\Text; +use \Joomla\CMS\Layout\FileLayout; +use \Joomla\CMS\Form\FormFactoryInterface; +use \Joomgallery\Component\Joomgallery\Administrator\Extension\ServiceTrait; +use \Joomgallery\Component\Joomgallery\Administrator\Service\Migration\MigrationInterface; + +/** + * Migration Base Class + * + * @package JoomGallery + * @since 4.0.0 + */ +abstract class Migration implements MigrationInterface +{ + use ServiceTrait; + + /** + * Storage for the migration parameters. + * + * @var \stdClass + * + * @since 4.0.0 + */ + protected $params = null; + + /** + * Name of the migration script to be used. + * + * @var string + * + * @since 4.0.0 + */ + protected $name = ''; + + /** + * Is the migration performed from the command line + * + * @var boolean + * + * @since 4.0.0 + */ + protected $isCli = false; + + /** + * Array of form objects. + * + * @var Form[] + * @since 4.0.0 + */ + protected $_forms = []; + + /** + * Constructor + * + * @return void + * + * @since 4.0.0 + */ + public function __construct() + { + // Load application + $this->getApp(); + + // Load component + $this->getComponent(); + + return; + } + + /** + * Step 1 + * Renders the form for configuring a migration using an XML file + * which has the same name than the migration script + * + * @return string HTML of the rendered form + * + * @since 4.0.0 + */ + public function renderForm(): string + { + // Prepare display data + $displayData = new \stdClass(); + $displayData->url = Route::_('index.php?option='._JOOM_OPTION.'&task=migration.precheck'); + $displayData->description = 'FILES_JOOMGALLERY_MIGRATION_'.strtoupper($this->name).'_DESC'; + $displayData->scriptName = $this->name; + $displayData->task = 'migration.precheck'; + $displayData->buttonTxt = 'COM_JOOMGALLERY_MIGRATION_STEP1_BTN_TXT'; + + // Get form + $form = $this->getForm(); + + // Add fieldsets + $displayData->fieldsets = $form->getFieldsets(); + foreach($displayData->fieldsets as $key => $fieldset) + { + $displayData->fieldsets[$key]->output = $form->renderFieldset($fieldset->name); + } + + // Render the form + $layout = new FileLayout('joomgallery.migration.form', JPATH_ADMINISTRATOR.'/components/'._JOOM_OPTION.'/layouts/joomgallery/migration'); + + return $layout->render($displayData); + } + + /** + * Step 2 + * Perform pre migration checks. + * + * @return void + * + * @since 4.0.0 + */ + public function checkPre() + { + return; + } + + /** + * Step 4 + * Perform post migration checks. + * + * @return void + * + * @since 4.0.0 + */ + public function checkPost() + { + return; + } + + /** + * Step 3 + * Perform one specific miration step and mark it as done at the end. + * + * @return void + * + * @since 4.0.0 + */ + public function migrate($type, $source, $dest) + { + return; + } + + /** + * Load and return the form object of the migration script + * + * @return Form Form object + * + * @since 4.0.0 + */ + protected function getForm(): Form + { + // Try to load language file of the migration script + $this->app->getLanguage()->load('com_joomgallery.migrate'.$this->name, JPATH_ADMINISTRATOR); + + // Form options + $name = _JOOM_OPTION.'.migration.'.$this->name; // The name of the form. + $source = $this->name; // The form source. Can be an XML string. + $options = array('control' => 'jform_'.$this->name, 'load_data' => true); // Optional array of options for the form creation. + $xpath = null; // An optional xpath to search for the fields. + + // Create a signature hash. But make sure, that loading the data does not create a new instance + $sigoptions = $options; + + if(isset($sigoptions['load_data'])) + { + unset($sigoptions['load_data']); + } + + $hash = md5($source . serialize($sigoptions)); + + // Check if we can use a previously loaded form. + if (isset($this->_forms[$hash])) + { + return $this->_forms[$hash]; + } + + // Add component form paths + Form::addFormPath(JPATH_ADMINISTRATOR.'/components/'._JOOM_OPTION.'/src/Service/Migration/Scripts'); + Form::addFormPath(JPATH_ADMINISTRATOR.'/components/'._JOOM_OPTION.'/forms'); + + // Create form + $formFactory = Factory::getContainer()->get(FormFactoryInterface::class); + $form = $formFactory->createForm($name, $options); + + // Load the data. + if(substr($source, 0, 1) === '<') + { + if($form->load($source, false, $xpath) == false) + { + throw new \RuntimeException('Form::loadForm could not load form'); + } + } + else + { + if($form->loadFile($source, false, $xpath) == false) + { + throw new \RuntimeException('Form::loadForm could not load file'); + } + } + + if(isset($options['load_data']) && $options['load_data']) + { + // Get the data for the form. + $data = $this->loadFormData(); + } + else + { + $data = []; + } + + // Load the data into the form. + $form->bind($data); + + // Store the form for later. + $this->_forms[$hash] = $form; + + return $form; + } + + /** + * Method to get the data that should be injected in the form. + * + * @return mixed The data for the form. + * + * @since 4.0.0 + */ + protected function loadFormData() + { + // Check the session for previously entered form data. + $data = Factory::getApplication()->getUserState(_JOOM_OPTION.'.migration.'.$this->name.'.data', array()); + + if(empty($data)) + { + $data = array(); + } + + return $data; + } +} diff --git a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php new file mode 100644 index 00000000..a47a2898 --- /dev/null +++ b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php @@ -0,0 +1,73 @@ + ** +** @copyright 2008 - 2022 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 2 or later ** +*****************************************************************************************/ + +namespace Joomgallery\Component\Joomgallery\Administrator\Service\Migration; + +// No direct access +\defined('_JEXEC') or die; + +/** + * Interface for the migration service class + * + * @package JoomGallery + * @since 4.0.0 + */ +interface MigrationInterface +{ + /** + * Constructor + * + * @return void + * + * @since 4.0.0 + */ + public function __construct(); + + /** + * Step 1 + * Renders the form for configuring a migration using an XML file + * which has the same name than the migration script + * + * @return string HTML of the rendered form + * + * @since 4.0.0 + */ + public function renderForm(): string; + + /** + * Step 2 + * Perform pre migration checks. + * + * @return void + * + * @since 4.0.0 + */ + public function checkPre(); + + /** + * Step 4 + * Perform post migration checks. + * + * @return void + * + * @since 4.0.0 + */ + public function checkPost(); + + /** + * Step 3 + * Perform one specific miration step and mark it as done at the end. + * + * @return void + * + * @since 4.0.0 + */ + public function migrate($type, $source, $dest); +} diff --git a/administrator/com_joomgallery/src/Service/Migration/MigrationServiceInterface.php b/administrator/com_joomgallery/src/Service/Migration/MigrationServiceInterface.php new file mode 100644 index 00000000..d92386b9 --- /dev/null +++ b/administrator/com_joomgallery/src/Service/Migration/MigrationServiceInterface.php @@ -0,0 +1,50 @@ + ** +** @copyright 2008 - 2022 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 2 or later ** +*****************************************************************************************/ + +namespace Joomgallery\Component\Joomgallery\Administrator\Service\Migration; + +\defined('_JEXEC') or die; + +/** +* The Migration service +* +* @since 4.0.0 +*/ +interface MigrationServiceInterface +{ + /** + * Storage for the migration service class. + * + * @var MigrationInterface + * + * @since 4.0.0 + */ + private $migration; + + /** + * Creates the migration service class + * + * @param string $script Name of the migration script to be used + * + * @return void + * + * @since 4.0.0 + */ + public function createMigration($script): void; + + /** + * Returns the migration service class. + * + * @return MigrationInterface + * + * @since 4.0.0 + */ + public function getMigration(): MigrationInterface; +} diff --git a/administrator/com_joomgallery/src/Service/Migration/MigrationServiceTrait.php b/administrator/com_joomgallery/src/Service/Migration/MigrationServiceTrait.php new file mode 100644 index 00000000..b55292a9 --- /dev/null +++ b/administrator/com_joomgallery/src/Service/Migration/MigrationServiceTrait.php @@ -0,0 +1,101 @@ + ** +** @copyright 2008 - 2022 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 2 or later ** +*****************************************************************************************/ + +namespace Joomgallery\Component\Joomgallery\Administrator\Service\Migration; + +\defined('_JEXEC') or die; + +use \Joomla\CMS\Uri\Uri; +use \Joomla\CMS\Language\Text; +use \Joomla\CMS\Filesystem\Folder; +use \Joomla\CMS\Filesystem\Path; + +/** +* Trait to implement MigrationServiceInterface +* +* @since 4.0.0 +*/ +trait MigrationServiceTrait +{ + /** + * Storage for the migration service class. + * + * @var MigrationInterface + * + * @since 4.0.0 + */ + private $migration = null; + + /** + * Creates the migration service class + * + * @param string $script Name of the migration script to be used + * + * @return void + * + * @since 4.0.0 + * @throws Exception + */ + public function createMigration($script) + { + // Get list of scripts + $scripts = $this->getScripts(); + + // Check if selected script exists + if(!\in_array($script, \array_keys($scripts))) + { + // Requested script does not exists + throw new \Exception(Text::_('COM_JOOMGALLERY_MIGRATION_SCRIPT_NOT_EXIST'), 1); + } + + // Create migration service based on provided migration script name + require_once $scripts[$script]['path']; + + $namespace = '\\Joomgallery\\Component\\Joomgallery\\Administrator\\Service\\Migration\\Scripts'; + $fully_qualified_class_name = $namespace.'\\'.$script; + $this->migration = new $fully_qualified_class_name; + + return; + } + + /** + * Returns the migration service class. + * + * @return MigrationInterface + * + * @since 4.0.0 + */ + public function getMigration() + { + return $this->migration; + } + + /** + * Method to get all available migration scripts. + * + * @return array|boolean List of paths of all available scripts. + * + * @since 4.0.0 + */ + protected function getScripts() + { + $files = Folder::files(JPATH_ADMINISTRATOR.'/components/'._JOOM_OPTION.'/src/Service/Migration/Scripts', '.php$', false, true); + + $scripts = array(); + foreach($files as $path) + { + $img = Uri::base().'components/'._JOOM_OPTION.'/src/Service/Migration/Scripts/'.basename($path, '.php').'.jpg'; + + $scripts[basename($path, '.php')] = array('path' => Path::clean($path), 'img' => $img); + } + + return $scripts; + } +} diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.jpg b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.jpg new file mode 100644 index 0000000000000000000000000000000000000000..86cd91786ae87b45778787587a48796e575cb200 GIT binary patch literal 3129 zcma)82|QF?8^5!Uu}+Ah&%UKXD#|v5-bq=<7BWr98ed6S$0r-vW|fdPVW7!tz`0Ps!#K)M3}hbRDW zsjUqFQ2-paE?^Bu!oeGbfWr|eG!ltIBGDK$2PkL^hHG8Gnwy)83%f1?KLr9uAkZi@ zF9yRaz{A7ylmEZLS`&ap0~tUD41xuqSO^RYS)&63pgRx*1hU~MfQCT9rNNLO32J^( z00;;a21g~Hq!9=Nob!hbC>SB^9zF?` z6gatnw#io+r)zeCxZ7MxJiM_}QjcUIjS$=ffTN&D*v4a3AbeN}6tGt70vGbPJOceB4M%seuk6Q>i<#1 z!1$rrDFFRYvm5WAr%bRKB;9BQ<5m7J|7A!43Lo32E=qWU_2 z81X3T4@VR9j#x;wNUCB_E(aP@^hY?}6S@fdD!9uJx_7+1xdE6_`Kb|Ucscv8g7N&i z|GJZ?nT8q@$t$kFi(E#IH{S6%ZT}m1=qsu{_T5`3QV04m{p*nc-tTK%@4s!A6>B55 z%aC5u1hOjL-s^E2uD|Q`jrwevO4Q?vl0X|O1SvIohuY_<)1-tSt6V%Oy!72g2n3w| z*~`-B2rK5Zf)pP|iQ|yy3y1f;#Z68(A;IzWe zzA(DmGBlrMR28bev-+edG4Bs+1Box! z0$x_px3E!X#g}rswp=W0U(geU!dO}P+JM}<0X-J<-Vqb3+@~p{A`;6c5UqL^m9s;hAdukJ{_I1b;k8$1 z)fnSK>JT--cn3ok!^JLmtFqFlnAtR!)N zIPCBZ`um#TU|=EAK>(5JAse$}Q;Xa+(|N^s$`Z`?s-w{bktD5*_u0L-Dm={UZlBBJ z*m|MCA(8TFamUg=`l28+>a=S3ZewQUYmxD%?wWZWrB<_X+!;kzV&_)tFQtW=@9G-} zZsRTU3AKov$`vCFFO{Nt)U~fSE*SHcIU5~|VL=i*Of{1x*MRS-%8m$Gz`2%uHfL~X zE+iwdXAR&w)@*pc0ug?VZgv%NSS!2BZgW$OgHAVw!OE!#IL++8r{?mW73UphSzK7U zI1zBQT5PGHzCTCR#gmy;v(JUmpQHSI^~VNfKY^km_$Js;KL3`sM#9A%ANMKO)V*k8 z;IFsZx{gq^ChM_KJ)V`TWf~Q^fPjLeDWcS*_bltM<54H9)r?@+{tF8`S7yq0%%q$aB zWhG42q_rrz6Zr9YxbS|(mk6ADr(W!Rk9e!Q4-0Q2a2RT1PRy-SdKZmOk57huvd~mI z^CIWCenEDgydOrwOq6c?bkA04XsuyJd=ZiFd>I45ndk2(6NIJLNaFg{p6(TcVI0B3ciLeI*Z> zhh37=PuRw7V8#>~dvSg?okKL4|K@e!u$1}%B}rTg$b3pFkJ4b7C#;i9XrYdwT!_vleu2MWKr3`oqdP(nDezM7+n7 zWG4DVCXM&XZB0$UKQ4Z4PQI?*o$GFJr(~>dKW7yeDLu~B4ZBsVFzA5pUjH?~YML4K z@@Vl!(Y3*qvJ(;wfq~lweDQ(>n9kBnYpv~7XXm2zTcQ<>-H+eyp1$^l*DvJp>_iOt z%OQsMuD~;5?CyDOmqJv!_qcq5;IphLyY^e(wI}YOgPV$Q`Cc6E7yRZa8U)TvN2E6` z+@rNE>+G<6cgCPxUrE&ioaDPaN29<~{jnq)3@u~GYinTr3in7dg)%&Gs3;b3bam)t zyQgFM%fg1*91~kFE$5pH8P9v|HlPtH0E3^jFETz@{TFSxFdUphRY zpk-y-hl>4SxP3?3k9SeAo!Qgh_Nb-V(YH@6`%XB{GDGx}6P|376ZOj_cafjqe}fX; RMg67ClG`gB*FRnT{{~Zk ** +** @copyright 2008 - 2022 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 2 or later ** +*****************************************************************************************/ + +namespace Joomgallery\Component\Joomgallery\Administrator\Service\Migration\Scripts; + +// No direct access +\defined('_JEXEC') or die; + +use \Joomgallery\Component\Joomgallery\Administrator\Service\Migration\Migration; +use \Joomgallery\Component\Joomgallery\Administrator\Service\Migration\MigrationInterface; + +/** + * Migration script class + * JoomGallery 3.x to JoomGallery 4.x + * + * @package JoomGallery + * @since 4.0.0 + */ +class Jg3ToJg4 extends Migration implements MigrationInterface +{ + /** + * Name of the migration script to be used. + * + * @var string + * + * @since 4.0.0 + */ + protected $name = 'Jg3ToJg4'; +} \ No newline at end of file diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.xml b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.xml new file mode 100644 index 00000000..74027d56 --- /dev/null +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.xml @@ -0,0 +1,142 @@ + +
+
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + +
+
diff --git a/administrator/com_joomgallery/src/View/Migration/HtmlView.php b/administrator/com_joomgallery/src/View/Migration/HtmlView.php new file mode 100644 index 00000000..9caa4a88 --- /dev/null +++ b/administrator/com_joomgallery/src/View/Migration/HtmlView.php @@ -0,0 +1,73 @@ + ** +** @copyright 2008 - 2022 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 2 or later ** +*****************************************************************************************/ + +namespace Joomgallery\Component\Joomgallery\Administrator\View\Migration; + +// No direct access +defined('_JEXEC') or die; + +use \Joomla\CMS\Factory; +use \Joomla\CMS\Language\Text; +use \Joomla\CMS\Toolbar\Toolbar; +use \Joomla\CMS\Toolbar\ToolbarHelper; +use \Joomgallery\Component\Joomgallery\Administrator\Helper\JoomHelper; +use \Joomgallery\Component\Joomgallery\Administrator\View\JoomGalleryView; + +/** + * View class for a single Tag. + * + * @package JoomGallery + * @since 4.0.0 + */ +class HtmlView extends JoomGalleryView +{ + protected $scripts; + + /** + * Display the view + * + * @param string $tpl Template name + * + * @return void + * + * @throws Exception + */ + public function display($tpl = null) + { + $this->scripts = $this->get('Scripts'); + $this->script = $this->app->input->get('script', '', 'cmd'); + $this->layout = $this->app->input->get('layout', 'default', 'cmd'); + $this->error = array(); + + // Add page title + ToolbarHelper::title(Text::_('COM_JOOMGALLERY_MIGRATION'), 'migration'); + + if($this->layout != 'default') + { + $this->app->input->set('hidemainmenu', true); + ToolbarHelper::cancel('migration.cancel', 'JTOOLBAR_CLOSE'); + + // Check if requested script exists + if(!\in_array($this->script, \array_keys($this->scripts))) + { + // Requested script does not exists + \array_push($this->error, 'COM_JOOMGALLERY_MIGRATION_SCRIPT_NOT_EXIST'); + } + } + + // Check for errors. + if(count($errors = $this->get('Errors'))) + { + throw new \Exception(implode("\n", $errors)); + } + + parent::display($tpl); + } +} diff --git a/administrator/com_joomgallery/tmpl/migration/default.php b/administrator/com_joomgallery/tmpl/migration/default.php new file mode 100644 index 00000000..979ad255 --- /dev/null +++ b/administrator/com_joomgallery/tmpl/migration/default.php @@ -0,0 +1,55 @@ + ** +** @copyright 2008 - 2022 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 2 or later ** +*****************************************************************************************/ + +// No direct access +defined('_JEXEC') or die; + +use \Joomla\CMS\HTML\HTMLHelper; +use \Joomla\CMS\Factory; +use \Joomla\CMS\Router\Route; +use \Joomla\CMS\Language\Text; + +HTMLHelper::addIncludePath(JPATH_COMPONENT . '/src/Helper/'); + +// Import CSS +$wa = Factory::getApplication()->getDocument()->getWebAssetManager(); +$wa->useStyle('com_joomgallery.admin') + ->useScript('com_joomgallery.admin'); +?> + +
+

Available migration scripts:

+
+ + scripts as $name => $script) : ?> +
+
+
+
+

+
+
+ <?php echo $name; ?> logo +
+
+

+ + + + + +
+
+
+
+
+
+ +
\ No newline at end of file diff --git a/administrator/com_joomgallery/tmpl/migration/step1.php b/administrator/com_joomgallery/tmpl/migration/step1.php new file mode 100644 index 00000000..e80e8c14 --- /dev/null +++ b/administrator/com_joomgallery/tmpl/migration/step1.php @@ -0,0 +1,51 @@ + ** +** @copyright 2008 - 2022 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 2 or later ** +*****************************************************************************************/ + +// No direct access +defined('_JEXEC') or die; + +use \Joomla\CMS\HTML\HTMLHelper; +use \Joomla\CMS\Factory; +use \Joomla\CMS\Router\Route; +use \Joomla\CMS\Language\Text; + +HTMLHelper::addIncludePath(JPATH_COMPONENT . '/src/Helper/'); + +// Import CSS +$wa = Factory::getApplication()->getDocument()->getWebAssetManager(); +$wa->useStyle('com_joomgallery.admin') + ->useScript('com_joomgallery.admin'); +?> + +
+ +
+ +
+ +

Step 1: Migration configuration

+
+ + error)): ?> + + + + + get('Form'); ?> +
\ No newline at end of file diff --git a/administrator/com_joomgallery/tmpl/migration/step2.php b/administrator/com_joomgallery/tmpl/migration/step2.php new file mode 100644 index 00000000..7996f063 --- /dev/null +++ b/administrator/com_joomgallery/tmpl/migration/step2.php @@ -0,0 +1,51 @@ + ** +** @copyright 2008 - 2022 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 2 or later ** +*****************************************************************************************/ + +// No direct access +defined('_JEXEC') or die; + +use \Joomla\CMS\HTML\HTMLHelper; +use \Joomla\CMS\Factory; +use \Joomla\CMS\Router\Route; +use \Joomla\CMS\Language\Text; + +HTMLHelper::addIncludePath(JPATH_COMPONENT . '/src/Helper/'); + +// Import CSS +$wa = Factory::getApplication()->getDocument()->getWebAssetManager(); +$wa->useStyle('com_joomgallery.admin') + ->useScript('com_joomgallery.admin'); +?> + +
+ +
+ +
+ +

Step 2: Migration pre-check

+
+ + error)): ?> + + + + + +
\ No newline at end of file diff --git a/administrator/com_joomgallery/tmpl/migration/step3.php b/administrator/com_joomgallery/tmpl/migration/step3.php new file mode 100644 index 00000000..0097b892 --- /dev/null +++ b/administrator/com_joomgallery/tmpl/migration/step3.php @@ -0,0 +1,51 @@ + ** +** @copyright 2008 - 2022 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 2 or later ** +*****************************************************************************************/ + +// No direct access +defined('_JEXEC') or die; + +use \Joomla\CMS\HTML\HTMLHelper; +use \Joomla\CMS\Factory; +use \Joomla\CMS\Router\Route; +use \Joomla\CMS\Language\Text; + +HTMLHelper::addIncludePath(JPATH_COMPONENT . '/src/Helper/'); + +// Import CSS +$wa = Factory::getApplication()->getDocument()->getWebAssetManager(); +$wa->useStyle('com_joomgallery.admin') + ->useScript('com_joomgallery.admin'); +?> + +
+ +
+ +
+ +

Step 3: Perform migration

+
+ + error)): ?> + + + + + +
\ No newline at end of file diff --git a/administrator/com_joomgallery/tmpl/migration/step4.php b/administrator/com_joomgallery/tmpl/migration/step4.php new file mode 100644 index 00000000..2f745d15 --- /dev/null +++ b/administrator/com_joomgallery/tmpl/migration/step4.php @@ -0,0 +1,51 @@ + ** +** @copyright 2008 - 2022 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 2 or later ** +*****************************************************************************************/ + +// No direct access +defined('_JEXEC') or die; + +use \Joomla\CMS\HTML\HTMLHelper; +use \Joomla\CMS\Factory; +use \Joomla\CMS\Router\Route; +use \Joomla\CMS\Language\Text; + +HTMLHelper::addIncludePath(JPATH_COMPONENT . '/src/Helper/'); + +// Import CSS +$wa = Factory::getApplication()->getDocument()->getWebAssetManager(); +$wa->useStyle('com_joomgallery.admin') + ->useScript('com_joomgallery.admin'); +?> + +
+ +
+ +
+ +

Step 4: Migration post-check

+
+ + error)): ?> + + + + + +
\ No newline at end of file diff --git a/media/com_joomgallery/css/admin.css b/media/com_joomgallery/css/admin.css index 135f69f0..306df0c6 100644 --- a/media/com_joomgallery/css/admin.css +++ b/media/com_joomgallery/css/admin.css @@ -60,6 +60,10 @@ img.jg-controlpanel-logo { .mr { margin-right: 1rem !important; } +.flex-center { + display: flex; + justify-content: center; +} .modal .modal-body { margin: 1rem 2rem; } @@ -199,3 +203,12 @@ joomla-field-image .jg_minithumb { .uppy-Dashboard-Item-name { white-space: nowrap; } +.jg-migration .card img { + max-width: 120px; +} +.jg-migration .card img { + max-width: 120px; +} +.jg-migration .navigation { + margin: 0 0 2rem; +} From e5ff9c6a7255aef88a6751a2a46c94f227127b12 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Tue, 1 Aug 2023 15:56:18 +0200 Subject: [PATCH 002/121] add precheck method to controller --- .../src/Controller/MigrationController.php | 209 +++++++++++++++++- .../src/Model/MigrationModel.php | 42 +--- .../src/Service/Migration/Migration.php | 2 +- 3 files changed, 211 insertions(+), 42 deletions(-) diff --git a/administrator/com_joomgallery/src/Controller/MigrationController.php b/administrator/com_joomgallery/src/Controller/MigrationController.php index b7ff47be..4d87fa0b 100644 --- a/administrator/com_joomgallery/src/Controller/MigrationController.php +++ b/administrator/com_joomgallery/src/Controller/MigrationController.php @@ -12,10 +12,18 @@ \defined('_JEXEC') or die; -use Joomla\CMS\Factory; -use Joomla\CMS\Language\Text; -use Joomla\CMS\Router\Route; -use Joomla\CMS\Uri\Uri; +use \Joomla\CMS\Factory; +use \Joomla\CMS\Uri\Uri; +use \Joomla\Input\Input; +use \Joomla\CMS\Language\Text; +use \Joomla\CMS\Router\Route; +use \Joomla\CMS\MVC\Controller\BaseController; +use \Joomla\CMS\Application\CMSApplication; +use \Joomla\CMS\Form\FormFactoryAwareInterface; +use \Joomla\CMS\Form\FormFactoryAwareTrait; +use \Joomla\CMS\Form\FormFactoryInterface; +use \Joomla\CMS\MVC\Factory\MVCFactoryInterface; +use \Joomgallery\Component\Joomgallery\Administrator\Extension\JoomgalleryComponent; /** * Migration controller class. @@ -23,8 +31,66 @@ * @package JoomGallery * @since 4.0.0 */ -class MigrationController extends JoomAdminController +class MigrationController extends BaseController implements FormFactoryAwareInterface { + use FormFactoryAwareTrait; + + /** + * Joomgallery\Component\Joomgallery\Administrator\Extension\JoomgalleryComponent + * + * @var JoomgalleryComponent + * @since 4.0.0 + */ + protected $component; + + /** + * The context for storing internal data, e.g. record. + * + * @var string + * @since 1.6 + */ + protected $context = _JOOM_OPTION.'.migration'; + + /** + * The URL option for the component. + * + * @var string + * @since 1.6 + */ + protected $option = _JOOM_OPTION; + + /** + * The prefix to use with controller messages. + * + * @var string + * @since 1.6 + */ + protected $text_prefix = _JOOM_OPTION_UC; + + /** + * Constructor. + * + * @param array $config An optional associative array of configuration settings. + * Recognized key values include 'name', 'default_task', 'model_path', and + * 'view_path' (this list is not meant to be comprehensive). + * @param MVCFactoryInterface $factory The factory. + * @param CMSApplication $app The Application for the dispatcher + * @param Input $input Input + * @param FormFactoryInterface $formFactory The form factory. + * + * @since 3.0 + */ + public function __construct($config = [], MVCFactoryInterface $factory = null, ?CMSApplication $app = null, ?Input $input = null, FormFactoryInterface $formFactory = null) + { + parent::__construct($config, $factory, $app, $input); + + $this->setFormFactory($formFactory); + $this->component = $this->app->bootComponent(_JOOM_OPTION); + + // As copy should be standard on forms. + $this->registerTask('check', 'precheck'); + } + /** * Proxy for getModel. * @@ -40,4 +106,137 @@ public function getModel($name = 'Migration', $prefix = 'Administrator', $config { return parent::getModel($name, $prefix, array('ignore_request' => true)); } + + /** + * Method to cancel a migration. + * + * @return boolean True if access level checks pass, false otherwise. + * + * @since 4.0.0 + */ + public function cancel() + { + $this->checkToken(); + + $model = $this->getModel(); + $script = $this->input->get('script', '', 'cmd'); + $scripts = $model->getScripts(); + + // Check if requested script exists + if(!\in_array($script, \array_keys($scripts))) + { + // Requested script does not exists + throw new Exception('Requested migration script does not exist.', 1); + } + + // Clean the session data and redirect. + $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.step2.data', null); + $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.step2.result', null); + + // Redirect to the list screen. + $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration', false)); + + return true; + } + + /** + * Step 2 + * Method to perform the pre migration checks. + * + * @return void + * + * @throws Exception + */ + public function precheck() + { + // Check for request forgeries + $this->checkToken(); + + $model = $this->getModel(); + $script = $this->input->get('script', '', 'cmd'); + $scripts = $model->getScripts(); + + // Check if requested script exists + if(!\in_array($script, \array_keys($scripts))) + { + // Requested script does not exists + throw new Exception('Requested migration script does not exist.', 1); + } + + $data = $this->input->post->get('jform_'.$script, [], 'array'); + $context = _JOOM_OPTION.'.migration.'.$script.'.step2'; + $task = $this->getTask(); + + // Access check. + if(false) + { + $this->setMessage(Text::_('COM_JOOMGALLERY_ERROR_MIGRATION_NOT_PERMITTED'), 'error'); + $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration', false)); + + return false; + } + + // Validate the posted data. + $form = $model->getForm($data, false); + + // Send an object which can be modified through the plugin event + $objData = (object) $data; + $this->app->triggerEvent('onContentNormaliseRequestData', [$context, $objData, $form]); + $data = (array) $objData; + + // Test whether the data is valid. + $validData = $model->validate($form, $data); + + // Check for validation errors. + if($validData === false) + { + // Get the validation messages. + $errors = $model->getErrors(); + + // Push up to three validation messages out to the user. + for($i = 0, $n = \count($errors); $i < $n && $i < 3; $i++) + { + if($errors[$i] instanceof \Exception) + { + $this->app->enqueueMessage($errors[$i]->getMessage(), 'warning'); + } + else + { + $this->app->enqueueMessage($errors[$i], 'warning'); + } + } + + // Save the data in the session. + $this->app->setUserState($context . '.data', $data); + + // Redirect back to the edit screen. + $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration', false)); + + return false; + } + + // Save the data in the session. + $this->app->setUserState($context . '.data', $validData); + + // Perform the pre migration checks + $res = $model->precheck($validData); + if($res === false) + { + // Pre-checks failed. Go back to step 1 and show a notice. + $this->setMessage(Text::sprintf('COM_JOOMGALLERY_ERROR_MIGRATION_STEP2_FAILED', $model->getError()), 'error'); + $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration&layout=step1&script=' . $script, false)); + + return false; + } + + // Pre-checks successful. + // Save the data in the session. + $this->app->setUserState($context . '.result', $res); + + // Output message and redirect to the next step + $this->setMessage(Text::_('COM_JOOMGALLERY_ERROR_MIGRATION_STEP2_SUCCESSFUL')); + $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration&layout=step2&script=' . $script, false)); + + return true; + } } diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index df84a671..31054fac 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -14,17 +14,10 @@ defined('_JEXEC') or die; use \Joomla\CMS\Factory; -use Joomla\CMS\Form\Form; -use \Joomla\CMS\Language\Text; use \Joomla\CMS\Uri\Uri; +use \Joomla\CMS\Language\Text; use \Joomla\CMS\Filesystem\Folder; -use \Joomla\CMS\MVC\Model\BaseDatabaseModel; -use \Joomla\CMS\MVC\Model\FormModelInterface; -use \Joomla\CMS\MVC\Model\FormBehaviorTrait; -use \Joomla\CMS\Form\FormFactoryAwareInterface; -use \Joomla\CMS\Form\FormFactoryAwareTrait; -use \Joomla\CMS\Form\FormFactoryInterface; -use \Joomla\CMS\MVC\Factory\MVCFactoryInterface; +use \Joomla\CMS\MVC\Model\FormModel; /** * Migration model. @@ -32,20 +25,8 @@ * @package JoomGallery * @since 4.0.0 */ -class MigrationModel extends BaseDatabaseModel implements FormFactoryAwareInterface, FormModelInterface +class MigrationModel extends FormModel { - use FormBehaviorTrait; - use FormFactoryAwareTrait; - - /** - * Maps events to plugin groups. - * - * @var array - * - * @since 3.6 - */ - protected $events_map = null; - /** * @var string The prefix to use with controller messages. * @@ -64,28 +45,17 @@ class MigrationModel extends BaseDatabaseModel implements FormFactoryAwareInterf * Constructor * * @param array $config An array of configuration options (name, state, dbo, table_path, ignore_request). - * @param MVCFactoryInterface $factory The factory. - * @param FormFactoryInterface $formFactory The form factory. * - * @since 3.6 + * @since 4.0.0 * @throws \Exception */ - public function __construct($config = [], MVCFactoryInterface $factory = null, FormFactoryInterface $formFactory = null) + public function __construct($config = array()) { - $config['events_map'] = $config['events_map'] ?? []; - - $this->events_map = array_merge( - ['validate' => 'content'], - $config['events_map'] - ); - - parent::__construct($config, $factory); + parent::__construct($config); $this->app = Factory::getApplication('administrator'); $this->component = $this->app->bootComponent(_JOOM_OPTION); $this->user = Factory::getUser(); - - $this->setFormFactory($formFactory); } /** diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index 3cbfec1c..1e5fe09e 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -169,7 +169,7 @@ public function migrate($type, $source, $dest) protected function getForm(): Form { // Try to load language file of the migration script - $this->app->getLanguage()->load('com_joomgallery.migrate'.$this->name, JPATH_ADMINISTRATOR); + $this->app->getLanguage()->load('com_joomgallery.migration'.$this->name, JPATH_ADMINISTRATOR); // Form options $name = _JOOM_OPTION.'.migration.'.$this->name; // The name of the form. From 5572730d86ce94d63e15a9e64281098114465cf4 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Sun, 6 Aug 2023 16:21:36 +0200 Subject: [PATCH 003/121] add MVC routine for precheck --- .../layouts/joomgallery/migration/form.php | 57 ------ .../src/Controller/MigrationController.php | 8 +- .../src/Model/MigrationModel.php | 97 ++++++++-- .../src/Service/Migration/Migration.php | 170 +++--------------- .../Service/Migration/MigrationInterface.php | 15 +- .../Service/Migration/Scripts/Jg3ToJg4.php | 13 ++ .../Service/Migration/Scripts/Jg3ToJg4.xml | 1 - .../src/View/Migration/HtmlView.php | 20 ++- .../com_joomgallery/tmpl/migration/step1.php | 29 ++- 9 files changed, 168 insertions(+), 242 deletions(-) delete mode 100644 administrator/com_joomgallery/layouts/joomgallery/migration/form.php diff --git a/administrator/com_joomgallery/layouts/joomgallery/migration/form.php b/administrator/com_joomgallery/layouts/joomgallery/migration/form.php deleted file mode 100644 index 7cf439e2..00000000 --- a/administrator/com_joomgallery/layouts/joomgallery/migration/form.php +++ /dev/null @@ -1,57 +0,0 @@ - ** -** @copyright 2008 - 2022 JoomGallery::ProjectTeam ** -** @license GNU General Public License version 2 or later ** -*****************************************************************************************/ - -defined('_JEXEC') or die; - -use \Joomla\CMS\Language\Text; -use \Joomla\CMS\HTML\HTMLHelper; - -extract((array) $displayData); - -/** - * Layout variables - * ----------------- - * @var string $scriptName The name of the migration script. - * @var string $description The description of the migration script. - * @var string $url The url, where to process the form. - * @var string $task The task to be executed when submitting the form. - * @var array $fieldsets List of fieldsets forming the form. - * @var string $buttonTxt Text shown in the submit button. - */ - -?> - - - -
- -
- $fieldset) : ?> -
-
-
- label); ?> -
- output; ?> -
-
-
-
- - - - - - -
-
\ No newline at end of file diff --git a/administrator/com_joomgallery/src/Controller/MigrationController.php b/administrator/com_joomgallery/src/Controller/MigrationController.php index 4d87fa0b..0394e251 100644 --- a/administrator/com_joomgallery/src/Controller/MigrationController.php +++ b/administrator/com_joomgallery/src/Controller/MigrationController.php @@ -131,7 +131,7 @@ public function cancel() // Clean the session data and redirect. $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.step2.data', null); - $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.step2.result', null); + $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.result', null); // Redirect to the list screen. $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration', false)); @@ -206,7 +206,7 @@ public function precheck() } } - // Save the data in the session. + // Save the form data in the session. $this->app->setUserState($context . '.data', $data); // Redirect back to the edit screen. @@ -215,8 +215,8 @@ public function precheck() return false; } - // Save the data in the session. - $this->app->setUserState($context . '.data', $validData); + // Save the migration parameters in the session. + $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.params', $validData); // Perform the pre migration checks $res = $model->precheck($validData); diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index 31054fac..b1133f3e 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -15,7 +15,7 @@ use \Joomla\CMS\Factory; use \Joomla\CMS\Uri\Uri; -use \Joomla\CMS\Language\Text; +use \Joomla\CMS\Form\Form; use \Joomla\CMS\Filesystem\Folder; use \Joomla\CMS\MVC\Model\FormModel; @@ -58,6 +58,34 @@ public function __construct($config = array()) $this->user = Factory::getUser(); } + /** + * Method to get info array of current migration script. + * + * @return object|boolean Migration info object. + * + * @since 4.0.0 + */ + public function getScript() + { + // Retreive script variable + $name = $this->app->input->get('script', '', 'cmd'); + + if(!$name) + { + $tmp = new \stdClass; + $tmp->name = ''; + + return $tmp; + } + + if(!$this->component->getMigration()) + { + $this->component->createMigration($name); + } + + return $this->component->getMigration()->get('info'); + } + /** * Method to get all available migration scripts. * @@ -80,32 +108,41 @@ public function getScripts() return $scripts; } - /** + /** * Method to get the migration form. * * @param array $data An optional array of data for the form to interogate. * @param boolean $loadData True if the form is to load its own data (default case), false if not. * - * @return string html output of the migration form + * @return Form|boolean A \JForm object on success, false on failure * * @since 4.0.0 */ public function getForm($data = array(), $loadData = true) { // Retreive script - $script = $this->app->input->get('script', '', 'cmd'); + $script = $this->getScript(); - // Use migration service for that - if($script) + if(!$script) { - $this->component->createMigration($script); - - return $this->component->getMigration()->renderForm(); + return false; } - else - { - return ''; - } + + // Add migration form paths + Form::addFormPath(JPATH_ADMINISTRATOR.'/components/'._JOOM_OPTION.'/src/Service/Migration/Scripts'); + Form::addFormPath(JPATH_ADMINISTRATOR.'/components/'._JOOM_OPTION.'/forms'); + + // Get the form. + $name = _JOOM_OPTION.'.migration.'.$this->component->getMigration()->get('name'); + $source = $this->component->getMigration()->get('name'); + $form = $this->loadForm($name, $source, array('control' => 'jform_'.$source, 'load_data' => true)); + + if(empty($form)) + { + return false; + } + + return $form; } /** @@ -117,6 +154,38 @@ public function getForm($data = array(), $loadData = true) */ protected function loadFormData() { - // Use migration service for that + if(!$this->component->getMigration()) + { + $this->getScript(); + } + + // Check the session for previously entered form data. + $name = _JOOM_OPTION.'.migration.'.$this->component->getMigration()->get('name'); + $data = $this->app->getUserState($name.'.step2.data', array()); + + // Check the session for validated migration parameters + $params = $this->app->getUserState($name.'.params', array()); + + return (empty($params)) ? $data : $params; } + + /** + * Method to perform the pre migration checks. + * + * @param array $params The migration parameters entered in the migration form + * + * @return array|boolean An array containing the precheck results on success. + * + * @since 4.0.0 + */ + public function precheck($params) + { + $info = $this->getScript(); + + // Set the migration parameters + $this->component->getMigration()->set('params', (object) $params); + + // Perform the prechecks + return $this->component->getMigration()->checkPre(); + } } diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index 1e5fe09e..35162eeb 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -14,11 +14,7 @@ \defined('_JEXEC') or die; use \Joomla\CMS\Factory; -use \Joomla\CMS\Form\Form; -use \Joomla\CMS\Router\Route; use \Joomla\CMS\Language\Text; -use \Joomla\CMS\Layout\FileLayout; -use \Joomla\CMS\Form\FormFactoryInterface; use \Joomgallery\Component\Joomgallery\Administrator\Extension\ServiceTrait; use \Joomgallery\Component\Joomgallery\Administrator\Service\Migration\MigrationInterface; @@ -33,7 +29,7 @@ abstract class Migration implements MigrationInterface use ServiceTrait; /** - * Storage for the migration parameters. + * Storage for the migration form object. * * @var \stdClass * @@ -42,7 +38,16 @@ abstract class Migration implements MigrationInterface protected $params = null; /** - * Name of the migration script to be used. + * Storage for the migration info object. + * + * @var \stdClass + * + * @since 4.0.0 + */ + protected $info = null; + + /** + * Name of the migration script. * * @var string * @@ -59,13 +64,6 @@ abstract class Migration implements MigrationInterface */ protected $isCli = false; - /** - * Array of form objects. - * - * @var Form[] - * @since 4.0.0 - */ - protected $_forms = []; /** * Constructor @@ -82,55 +80,28 @@ public function __construct() // Load component $this->getComponent(); - return; - } - - /** - * Step 1 - * Renders the form for configuring a migration using an XML file - * which has the same name than the migration script - * - * @return string HTML of the rendered form - * - * @since 4.0.0 - */ - public function renderForm(): string - { - // Prepare display data - $displayData = new \stdClass(); - $displayData->url = Route::_('index.php?option='._JOOM_OPTION.'&task=migration.precheck'); - $displayData->description = 'FILES_JOOMGALLERY_MIGRATION_'.strtoupper($this->name).'_DESC'; - $displayData->scriptName = $this->name; - $displayData->task = 'migration.precheck'; - $displayData->buttonTxt = 'COM_JOOMGALLERY_MIGRATION_STEP1_BTN_TXT'; - - // Get form - $form = $this->getForm(); - - // Add fieldsets - $displayData->fieldsets = $form->getFieldsets(); - foreach($displayData->fieldsets as $key => $fieldset) - { - $displayData->fieldsets[$key]->output = $form->renderFieldset($fieldset->name); - } + // Try to load language file of the migration script + $this->app->getLanguage()->load('com_joomgallery.migration'.$this->name, JPATH_ADMINISTRATOR); - // Render the form - $layout = new FileLayout('joomgallery.migration.form', JPATH_ADMINISTRATOR.'/components/'._JOOM_OPTION.'/layouts/joomgallery/migration'); - - return $layout->render($displayData); + // Fill info object + $this->info = new \stdClass; + $this->info->name = $this->name; + $this->info->title = Text::_('FILES_JOOMGALLERY_MIGRATION_'.strtoupper($this->name).'_TITLE'); + $this->info->description = Text::_('FILES_JOOMGALLERY_MIGRATION_'.strtoupper($this->name).'_DESC'); + $this->info->startBtnText = Text::_('COM_JOOMGALLERY_MIGRATION_STEP1_BTN_TXT'); } /** * Step 2 * Perform pre migration checks. * - * @return void + * @return array|boolean An array containing the precheck results on success. * * @since 4.0.0 */ - public function checkPre() + public function checkPre(): array { - return; + return array('php' => true); } /** @@ -158,101 +129,4 @@ public function migrate($type, $source, $dest) { return; } - - /** - * Load and return the form object of the migration script - * - * @return Form Form object - * - * @since 4.0.0 - */ - protected function getForm(): Form - { - // Try to load language file of the migration script - $this->app->getLanguage()->load('com_joomgallery.migration'.$this->name, JPATH_ADMINISTRATOR); - - // Form options - $name = _JOOM_OPTION.'.migration.'.$this->name; // The name of the form. - $source = $this->name; // The form source. Can be an XML string. - $options = array('control' => 'jform_'.$this->name, 'load_data' => true); // Optional array of options for the form creation. - $xpath = null; // An optional xpath to search for the fields. - - // Create a signature hash. But make sure, that loading the data does not create a new instance - $sigoptions = $options; - - if(isset($sigoptions['load_data'])) - { - unset($sigoptions['load_data']); - } - - $hash = md5($source . serialize($sigoptions)); - - // Check if we can use a previously loaded form. - if (isset($this->_forms[$hash])) - { - return $this->_forms[$hash]; - } - - // Add component form paths - Form::addFormPath(JPATH_ADMINISTRATOR.'/components/'._JOOM_OPTION.'/src/Service/Migration/Scripts'); - Form::addFormPath(JPATH_ADMINISTRATOR.'/components/'._JOOM_OPTION.'/forms'); - - // Create form - $formFactory = Factory::getContainer()->get(FormFactoryInterface::class); - $form = $formFactory->createForm($name, $options); - - // Load the data. - if(substr($source, 0, 1) === '<') - { - if($form->load($source, false, $xpath) == false) - { - throw new \RuntimeException('Form::loadForm could not load form'); - } - } - else - { - if($form->loadFile($source, false, $xpath) == false) - { - throw new \RuntimeException('Form::loadForm could not load file'); - } - } - - if(isset($options['load_data']) && $options['load_data']) - { - // Get the data for the form. - $data = $this->loadFormData(); - } - else - { - $data = []; - } - - // Load the data into the form. - $form->bind($data); - - // Store the form for later. - $this->_forms[$hash] = $form; - - return $form; - } - - /** - * Method to get the data that should be injected in the form. - * - * @return mixed The data for the form. - * - * @since 4.0.0 - */ - protected function loadFormData() - { - // Check the session for previously entered form data. - $data = Factory::getApplication()->getUserState(_JOOM_OPTION.'.migration.'.$this->name.'.data', array()); - - if(empty($data)) - { - $data = array(); - } - - return $data; - } } diff --git a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php index a47a2898..1e59d2af 100644 --- a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php +++ b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php @@ -30,26 +30,15 @@ interface MigrationInterface */ public function __construct(); - /** - * Step 1 - * Renders the form for configuring a migration using an XML file - * which has the same name than the migration script - * - * @return string HTML of the rendered form - * - * @since 4.0.0 - */ - public function renderForm(): string; - /** * Step 2 * Perform pre migration checks. * - * @return void + * @return array|boolean An array containing the precheck results on success. * * @since 4.0.0 */ - public function checkPre(); + public function checkPre(): array; /** * Step 4 diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index 0ce2d87e..c9e49db0 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -13,6 +13,7 @@ // No direct access \defined('_JEXEC') or die; +use \Joomla\CMS\Language\Text; use \Joomgallery\Component\Joomgallery\Administrator\Service\Migration\Migration; use \Joomgallery\Component\Joomgallery\Administrator\Service\Migration\MigrationInterface; @@ -33,4 +34,16 @@ class Jg3ToJg4 extends Migration implements MigrationInterface * @since 4.0.0 */ protected $name = 'Jg3ToJg4'; + + /** + * Constructor + * + * @return void + * + * @since 4.0.0 + */ + public function __construct() + { + parent::__construct(); + } } \ No newline at end of file diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.xml b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.xml index 74027d56..cf8c3d27 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.xml +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.xml @@ -92,7 +92,6 @@ filter="string" size="10" default="jos_" - validate="prefix" showon="same_joomla:0" label="COM_JOOMGALLERY_FIELDS_DATABASE_PREFIX_LABEL" description="COM_JOOMGALLERY_FIELDS_DATABASE_PREFIX_DESC" diff --git a/administrator/com_joomgallery/src/View/Migration/HtmlView.php b/administrator/com_joomgallery/src/View/Migration/HtmlView.php index 9caa4a88..523f8498 100644 --- a/administrator/com_joomgallery/src/View/Migration/HtmlView.php +++ b/administrator/com_joomgallery/src/View/Migration/HtmlView.php @@ -41,9 +41,9 @@ class HtmlView extends JoomGalleryView */ public function display($tpl = null) { - $this->scripts = $this->get('Scripts'); - $this->script = $this->app->input->get('script', '', 'cmd'); - $this->layout = $this->app->input->get('layout', 'default', 'cmd'); + $this->script = $this->get('Script'); + $this->scripts = $this->get('Scripts'); + $this->layout = $this->app->input->get('layout', 'default', 'cmd'); $this->error = array(); // Add page title @@ -55,11 +55,23 @@ public function display($tpl = null) ToolbarHelper::cancel('migration.cancel', 'JTOOLBAR_CLOSE'); // Check if requested script exists - if(!\in_array($this->script, \array_keys($this->scripts))) + if(!\in_array($this->script->name, \array_keys($this->scripts))) { // Requested script does not exists \array_push($this->error, 'COM_JOOMGALLERY_MIGRATION_SCRIPT_NOT_EXIST'); } + else + { + // Load migration form data + $this->form = $this->get('Form'); + } + + // Check if form parameters exists + if(false) + { + // Requested script does not exists + \array_push($this->error, 'COM_JOOMGALLERY_MIGRATION_PARAMS_NOT_EXIST'); + } } // Check for errors. diff --git a/administrator/com_joomgallery/tmpl/migration/step1.php b/administrator/com_joomgallery/tmpl/migration/step1.php index e80e8c14..731d655b 100644 --- a/administrator/com_joomgallery/tmpl/migration/step1.php +++ b/administrator/com_joomgallery/tmpl/migration/step1.php @@ -47,5 +47,32 @@ - get('Form'); ?> + + +
+ +
+ form->getFieldsets() as $key => $fieldset) : ?> +
+
+
+ label); ?> +
+ form->renderFieldset($fieldset->name);; ?> +
+
+
+
+ + + + + + +
+
\ No newline at end of file From 03310237a8cb980894f7262080a7f876c33c27a3 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 7 Aug 2023 22:42:29 +0200 Subject: [PATCH 004/121] add step2 to migration --- .../src/Controller/MigrationController.php | 33 +- .../src/Model/MigrationModel.php | 44 ++- .../src/Service/Migration/Checks.php | 314 ++++++++++++++++++ .../src/Service/Migration/Migration.php | 30 +- .../Service/Migration/MigrationInterface.php | 4 +- .../src/View/Migration/HtmlView.php | 42 ++- .../com_joomgallery/tmpl/migration/step1.php | 29 +- .../com_joomgallery/tmpl/migration/step2.php | 64 ++++ 8 files changed, 518 insertions(+), 42 deletions(-) create mode 100644 administrator/com_joomgallery/src/Service/Migration/Checks.php diff --git a/administrator/com_joomgallery/src/Controller/MigrationController.php b/administrator/com_joomgallery/src/Controller/MigrationController.php index 0394e251..55f0f811 100644 --- a/administrator/com_joomgallery/src/Controller/MigrationController.php +++ b/administrator/com_joomgallery/src/Controller/MigrationController.php @@ -130,8 +130,12 @@ public function cancel() } // Clean the session data and redirect. - $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.step2.data', null); - $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.result', null); + $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.params', null); + $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.step2.data', null); + $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.step2.results', null); + $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.step2.success', null); + $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.step3.results', null); + $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.step4.results', null); // Redirect to the list screen. $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration', false)); @@ -219,24 +223,25 @@ public function precheck() $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.params', $validData); // Perform the pre migration checks - $res = $model->precheck($validData); - if($res === false) + list($success, $res) = $model->precheck($validData); + if(!$success) { - // Pre-checks failed. Go back to step 1 and show a notice. + // Pre-checks not successful. Show error message. $this->setMessage(Text::sprintf('COM_JOOMGALLERY_ERROR_MIGRATION_STEP2_FAILED', $model->getError()), 'error'); - $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration&layout=step1&script=' . $script, false)); - - return false; + } + else + { + // Pre-checks successful. Show success message. + $this->setMessage(Text::_('COM_JOOMGALLERY_ERROR_MIGRATION_STEP2_SUCCESSFUL')); } - // Pre-checks successful. - // Save the data in the session. - $this->app->setUserState($context . '.result', $res); + // Save the results of the pre migration checks in the session. + $this->app->setUserState($context . '.results', $res); + $this->app->setUserState($context . '.success', $success); - // Output message and redirect to the next step - $this->setMessage(Text::_('COM_JOOMGALLERY_ERROR_MIGRATION_STEP2_SUCCESSFUL')); + // Redirect to the screen to show the results (View of Step 2) $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration&layout=step2&script=' . $script, false)); - return true; + return; } } diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index b1133f3e..c72d38ce 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -174,7 +174,7 @@ protected function loadFormData() * * @param array $params The migration parameters entered in the migration form * - * @return array|boolean An array containing the precheck results on success. + * @return array An array containing the precheck results. * * @since 4.0.0 */ @@ -186,6 +186,46 @@ public function precheck($params) $this->component->getMigration()->set('params', (object) $params); // Perform the prechecks - return $this->component->getMigration()->checkPre(); + return $this->component->getMigration()->precheck(); + } + + /** + * Method to perform the pre migration checks. + * + * @param array $params The migration parameters entered in the migration form + * + * @return array|boolean An array containing the precheck results on success. + * + * @since 4.0.0 + */ + public function postcheck($params) + { + $info = $this->getScript(); + + // Set the migration parameters + $this->component->getMigration()->set('params', (object) $params); + + // Perform the prechecks + return $this->component->getMigration()->postcheck(); + } + + /** + * Method to perform the pre migration checks. + * + * @param array $params The migration parameters entered in the migration form + * + * @return array|boolean An array containing the precheck results on success. + * + * @since 4.0.0 + */ + public function migrate($params) + { + $info = $this->getScript(); + + // Set the migration parameters + $this->component->getMigration()->set('params', (object) $params); + + // Perform the prechecks + return $this->component->getMigration()->migrate(); } } diff --git a/administrator/com_joomgallery/src/Service/Migration/Checks.php b/administrator/com_joomgallery/src/Service/Migration/Checks.php new file mode 100644 index 00000000..a25d2518 --- /dev/null +++ b/administrator/com_joomgallery/src/Service/Migration/Checks.php @@ -0,0 +1,314 @@ + ** +** @copyright 2008 - 2022 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 2 or later ** +*****************************************************************************************/ + +namespace Joomgallery\Component\Joomgallery\Administrator\Service\Migration\Checks; + +// No direct access +\defined('_JEXEC') or die; + +/** + * Migration Checks Class + * Providing a structure for the results of migration checks + * + * @package JoomGallery + * @since 4.0.0 + */ +class Checks +{ + /** + * List of assets available in the check-objects + * array(categoryName.checkName => categoryKey.checkKey) + * + * @var array + * + * @since 4.0.0 + */ + private $assets = []; + + /** + * The array of check-objects + * + * @var \stdClass[] + * + * @since 4.0.0 + */ + private $objects = []; + + /** + * The overall success of all checks + * True if all checks were successful, false otherwise + * + * @var bool + * + * @since 4.0.0 + */ + private $success = true; + + /** + * Register a new category or modify an existing one + * + * @param string $name The name of the category + * @param string $title Optional: Title of the category + * @param string $desc Optional: Description of the category + * + * @return void + * + * @since 4.0.0 + * @throws \Exception + */ + public function addCategory(string $name, string $title = '', string $desc = '') + { + // Make category name lowercase + $name = \strtolower(\trim($name)); + + if(!\in_array($name, \array_keys($this->assets))) + { + // Category not yet existing, create a new one + $cat = new \stdClass(); + $cat->name = $name; + $cat->title = $title; + $cat->desc = $desc; + $cat->checks = []; + + // Add category to check-objects array + $key = $this->array_push($this->objects, $cat); + + // Add category to assets array + $this->assets[$name] = $key; + } + else + { + // You try to add a category already existing + throw new \Exception('You try to add a category that already exists. If you want to modify it, use "modCategory()" instead.', 1); + } + } + + /** + * Modify an existing category + * + * @param string $name The name of the category + * @param string $title Optional: Title of the category + * @param string $desc Optional: Description of the category + * + * @return void + * + * @since 4.0.0 + * @throws \Exception + */ + public function modCategory(string $name, $title = null, $desc = null) + { + // Make category name lowercase + $name = \strtolower(\trim($name)); + + if(!\in_array($name, \array_keys($this->assets))) + { + // You try to modify a category which does not exist + throw new \Exception('You try to modify a category which does not exists. Please add the category first.', 1); + } + else + { + $key = $this->assets[$name]; + + // Modify title and/or description + if(!\is_null($title)) + { + $this->objects[$key]->title = (string) $title; + } + + if(!\is_null($desc)) + { + $this->objects[$key]->desc = (string) $desc; + } + } + } + + /** + * Add a new check beeing performed + * + * @param string $category The category of the check + * @param string $name The name of the check + * @param bool $result True if the check was successful, false otherwise + * @param string $title Optional: Title of the check + * @param string $desc Optional: Description of the check + * @param string $help Optional: URL to a help-site or help-text + * + * @return void + * + * @since 4.0.0 + * @throws \Exception + */ + public function addCheck(string $category, string $name, bool $result, string $title = '', string $desc = '', string $help = '') + { + // Make category and check name lowercase + $category = \strtolower(\trim($category)); + $name = \strtolower(\trim($name)); + $asset = $category.'.'.$name; + + // Check if category exists + if(!\in_array($category, \array_keys($this->assets))) + { + throw new \Exception('You try to add a check to a category which is not existing. Please add the category first.', 1); + } + + // Check if asset exists + if(!\in_array($asset, \array_keys($this->assets))) + { + // Get category key + $catKey = $this->assets[$asset]; + + // Asset not yet existing, create a new one + $check = new \stdClass(); + $check->name = $name; + $check->result = $result; + $check->title = $title; + $check->desc = $desc; + $check->help = $help; + + // Add check to check-objects array + $key = $this->array_push($this->objects[$catKey]->checks, $check); + + // Add check to assets array + $this->assets[$name] = $catKey.'.'.$key; + + // Modify the oversall success if needed + if($result === false) + { + $this->success = false; + } + } + else + { + // You try to add a check already existing + throw new \Exception('You try to add a check that already exists. If you want to modify it, use "modCheck()" instead.', 2); + } + } + + /** + * Modify an existing check + * + * @param string $category The category of the check + * @param string $name The name of the check + * @param bool $result True if the check was successful, false otherwise + * @param string $title Optional: Title of the check + * @param string $desc Optional: Description of the check + * @param string $help Optional: URL to a help-site or help-text + * + * @return void + * + * @since 4.0.0 + * @throws \Exception + */ + public function modCheck(string $category, string $name, $result = null, $title = null, $desc = null, $help = null) + { + // Make category and check name lowercase + $category = \strtolower(\trim($category)); + $name = \strtolower(\trim($name)); + $asset = $category.'.'.$name; + + // Check if category exists + if(!\in_array($category, \array_keys($this->assets))) + { + throw new \Exception('You try to modify a check in a category which is not existing. Please add the category first.', 1); + } + + // Check if asset exists + if(!\in_array($asset, \array_keys($this->assets))) + { + // You try to modify a check which does not exist + throw new \Exception('You try to modify a check which does not exists. Please add the check first.', 2); + } + else + { + $key = $this->assets[$asset]; + list($catKey, $checkKey) = \explode('.', $key, 2); + + // Modify the result + if(!\is_null($result)) + { + $this->objects[$catKey]->checks[$checkKey]->result = \boolval($result); + + // Modify the oversall success if needed + if(\boolval($result) === false) + { + $this->success = false; + } + } + + // Modify the title + if(!\is_null($title)) + { + $this->objects[$catKey]->checks[$checkKey]->title = (string) $title; + } + + // Modify the description + if(!\is_null($desc)) + { + $this->objects[$catKey]->checks[$checkKey]->desc = (string) $desc; + } + + // Modify the description + if(!\is_null($help)) + { + $this->objects[$catKey]->checks[$checkKey]->help = (string) $help; + } + } + } + + /** + * Returns all registered checks + * + * @return array A list of checks + * + * @since 4.0.0 + */ + public function getChecks(): array + { + return $this->objects; + } + + /** + * Returns the overall success of the checks + * + * @return bool True if all checks were successful, false otherwise + * + * @since 4.0.0 + */ + public function getSuccess(): bool + { + return $this->success; + } + + /** + * Returns the registered checks and the overall success + * + * @return array array($this->success, $this->objects) + * + * @since 4.0.0 + */ + public function getAll(): array + { + return array($this->success, $this->objects); + } + + /** + * Wrapper for the php function 'array_push' with new created key as return value + * + * @return int Key of the new created array entry + * + * @since 4.0.0 + */ + protected function array_push(array &$array, $item): int + { + $next = \count($array); + $array[$next] = $item; + + return $next; + } +} \ No newline at end of file diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index 35162eeb..cc2444a1 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -95,13 +95,35 @@ public function __construct() * Step 2 * Perform pre migration checks. * - * @return array|boolean An array containing the precheck results on success. + * @return \stdClass[] An array containing the precheck results. * * @since 4.0.0 */ - public function checkPre(): array + public function precheck(): array { - return array('php' => true); + $checks = array((object) array('name' => 'directories', + 'title' => 'Existence of directories', + 'colTitle' => 'Folder', + 'desc' => 'Are the nessecary directories available and writeable?', + 'checks' => array( (object) array( + 'name' => 'originals', + 'result' => true, + 'title' => 'Original images', + 'description' => '/joomla3/images/joomgallery/originals/', + 'help' => 'Folder (/joomla3/images/joomgallery/originals/) exist and is writeable.' + ), + (object) array( + 'name' => 'thumbs', + 'result' => false, + 'title' => 'Thumbnail images', + 'description' => '/joomla3/images/joomgallery/thumbnails/', + 'help' => 'Folder (/joomla3/images/joomgallery/thumbnails/) is not writeable. make sure the permissions are set correctly for this folder.' + ), + ) + ) + ); + + return array(true, $checks); } /** @@ -112,7 +134,7 @@ public function checkPre(): array * * @since 4.0.0 */ - public function checkPost() + public function postcheck() { return; } diff --git a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php index 1e59d2af..395b8981 100644 --- a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php +++ b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php @@ -38,7 +38,7 @@ public function __construct(); * * @since 4.0.0 */ - public function checkPre(): array; + public function precheck(): array; /** * Step 4 @@ -48,7 +48,7 @@ public function checkPre(): array; * * @since 4.0.0 */ - public function checkPost(); + public function postcheck(); /** * Step 3 diff --git a/administrator/com_joomgallery/src/View/Migration/HtmlView.php b/administrator/com_joomgallery/src/View/Migration/HtmlView.php index 523f8498..b4904338 100644 --- a/administrator/com_joomgallery/src/View/Migration/HtmlView.php +++ b/administrator/com_joomgallery/src/View/Migration/HtmlView.php @@ -43,7 +43,7 @@ public function display($tpl = null) { $this->script = $this->get('Script'); $this->scripts = $this->get('Scripts'); - $this->layout = $this->app->input->get('layout', 'default', 'cmd'); + $this->layout = $this->app->input->get('layout', 'default', 'cmd'); $this->error = array(); // Add page title @@ -62,15 +62,43 @@ public function display($tpl = null) } else { - // Load migration form data - $this->form = $this->get('Form'); + // Try to load the migration params + $this->params = $this->app->getUserState(_JOOM_OPTION.'.migration.'.$this->script->name.'.params', null); + + // Check if migration params exist + if(\is_null($this->params) && $this->layout != 'step1') + { + // Requested script does not exists + \array_push($this->error, 'COM_JOOMGALLERY_MIGRATION_PARAMS_NOT_EXIST'); + } } - // Check if form parameters exists - if(false) + switch($this->layout) { - // Requested script does not exists - \array_push($this->error, 'COM_JOOMGALLERY_MIGRATION_PARAMS_NOT_EXIST'); + case 'step1': + // Load migration form + $this->form = $this->get('Form'); + break; + + case 'step2': + // Load precheck results + $this->precheck = $this->app->getUserState(_JOOM_OPTION.'.migration.'.$this->script->name.'.step2.results', array()); + $this->success = $this->app->getUserState(_JOOM_OPTION.'.migration.'.$this->script->name.'.step2.success', false); + break; + + case 'step3': + // Load migration results + $this->migration = $this->app->getUserState(_JOOM_OPTION.'.migration.'.$this->script->name.'.step3.results', array()); + break; + + case 'step4': + // Load postcheck results + $this->postcheck = $this->app->getUserState(_JOOM_OPTION.'.migration.'.$this->script->name.'.step4.results', array()); + break; + + default: + # code... + break; } } diff --git a/administrator/com_joomgallery/tmpl/migration/step1.php b/administrator/com_joomgallery/tmpl/migration/step1.php index 731d655b..38623800 100644 --- a/administrator/com_joomgallery/tmpl/migration/step1.php +++ b/administrator/com_joomgallery/tmpl/migration/step1.php @@ -56,23 +56,26 @@ name="adminForm" id="migration-form" class="form-validate card" aria-label="COM_JOOMGALLERY_MIGRATION_STEP1_TITLE">
- form->getFieldsets() as $key => $fieldset) : ?> -
-
-
- label); ?> -
- form->renderFieldset($fieldset->name);; ?> -
-
+ error)) : ?> + form->getFieldsets() as $key => $fieldset) : ?> +
+
+
+ label); ?> +
+ form->renderFieldset($fieldset->name);; ?> +
+
+
-
- + + + + - - +
\ No newline at end of file diff --git a/administrator/com_joomgallery/tmpl/migration/step2.php b/administrator/com_joomgallery/tmpl/migration/step2.php index 7996f063..75f3bb8a 100644 --- a/administrator/com_joomgallery/tmpl/migration/step2.php +++ b/administrator/com_joomgallery/tmpl/migration/step2.php @@ -47,5 +47,69 @@ + + +
+ +
+ + error)) : ?> +

Results of the migration check

+ + + precheck as $cat) : ?> +
+ title): ?> +
+

title; ?>

+ desc): ?> + desc; ?> + +
+ +
+ + + + + + + + + + + + + checks as $check) : ?> + + + + + + + + +
title; ?>
colTitle; ?>
+ title; ?>
+ description; ?> +
result ? 'success' : 'failed'; ?>
+
+
+ + +
+ + + + + + + +
+ \ No newline at end of file From b2f5a70f8b24774cc358e7bd4ea656cd08cb08ee Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 28 Aug 2023 17:12:39 +0200 Subject: [PATCH 005/121] add view of step 3 --- .../src/Controller/MigrationController.php | 68 +++++++++++++++++-- .../src/Model/MigrationModel.php | 23 ++++++- .../src/Service/Migration/Migration.php | 23 ++++++- .../Service/Migration/MigrationInterface.php | 9 +++ .../Service/Migration/Scripts/Jg3ToJg4.php | 10 +++ .../src/View/Migration/HtmlView.php | 6 +- .../tmpl/migration/default.php | 2 - .../com_joomgallery/tmpl/migration/step1.php | 2 - .../com_joomgallery/tmpl/migration/step2.php | 5 +- .../com_joomgallery/tmpl/migration/step3.php | 48 ++++++++++++- 10 files changed, 176 insertions(+), 20 deletions(-) diff --git a/administrator/com_joomgallery/src/Controller/MigrationController.php b/administrator/com_joomgallery/src/Controller/MigrationController.php index 55f0f811..553c68b0 100644 --- a/administrator/com_joomgallery/src/Controller/MigrationController.php +++ b/administrator/com_joomgallery/src/Controller/MigrationController.php @@ -119,7 +119,7 @@ public function cancel() $this->checkToken(); $model = $this->getModel(); - $script = $this->input->get('script', '', 'cmd'); + $script = $this->app->getUserStateFromRequest(_JOOM_OPTION.'.migration.script', 'script', '', 'cmd'); $scripts = $model->getScripts(); // Check if requested script exists @@ -130,12 +130,15 @@ public function cancel() } // Clean the session data and redirect. + $this->app->setUserState(_JOOM_OPTION.'.migration.script', null); $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.params', null); $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.step2.data', null); $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.step2.results', null); $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.step2.success', null); $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.step3.results', null); + $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.step3.success', null); $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.step4.results', null); + $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.step4.success', null); // Redirect to the list screen. $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration', false)); @@ -145,11 +148,12 @@ public function cancel() /** * Step 2 - * Method to perform the pre migration checks. + * Validate the form input data and perform the pre migration checks. * * @return void * - * @throws Exception + * @since 4.0.0 + * @throws \Exception */ public function precheck() { @@ -157,14 +161,14 @@ public function precheck() $this->checkToken(); $model = $this->getModel(); - $script = $this->input->get('script', '', 'cmd'); + $script = $this->app->getUserStateFromRequest(_JOOM_OPTION.'.migration.script', 'script', '', 'cmd'); $scripts = $model->getScripts(); // Check if requested script exists if(!\in_array($script, \array_keys($scripts))) { // Requested script does not exists - throw new Exception('Requested migration script does not exist.', 1); + throw new \Exception('Requested migration script does not exist.', 1); } $data = $this->input->post->get('jform_'.$script, [], 'array'); @@ -219,6 +223,9 @@ public function precheck() return false; } + // Save the script name in the session. + $this->app->setUserState(_JOOM_OPTION.'.migration.script', $script); + // Save the migration parameters in the session. $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.params', $validData); @@ -240,8 +247,57 @@ public function precheck() $this->app->setUserState($context . '.success', $success); // Redirect to the screen to show the results (View of Step 2) - $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration&layout=step2&script=' . $script, false)); + $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration&layout=step2', false)); return; } + + /** + * Step 3 + * Enter the migration view. + * + * @return void + * + * @since 4.0.0 + * @throws \Exception + */ + public function migrate() + { + // Check for request forgeries + $this->checkToken(); + + $model = $this->getModel(); + $script = $this->app->getUserStateFromRequest(_JOOM_OPTION.'.migration.script', 'script', '', 'cmd'); + $scripts = $model->getScripts(); + + // Check if requested script exists + if(!\in_array($script, \array_keys($scripts))) + { + // Requested script does not exists + throw new \Exception('Requested migration script does not exist.', 1); + } + + // Access check. + if(false) + { + $this->setMessage(Text::_('COM_JOOMGALLERY_ERROR_MIGRATION_NOT_PERMITTED'), 'error'); + $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration', false)); + + return false; + } + + $precheck = $this->app->getUserState(_JOOM_OPTION.'.migration.'.$script.'.step2.success', false); + + // Check if no errors detected in precheck (step 2) + if(!$precheck) + { + // Pre-checks not successful. Show error message. + $this->setMessage(Text::sprintf('COM_JOOMGALLERY_ERROR_MIGRATION_STEP2_FAILED', 'Previous step not completed.'), 'error'); + // Redirect to the step 2 screen + $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration&layout=step2', false)); + } + + // Redirect to the step 3 screen + $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration&layout=step3', false)); + } } diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index c72d38ce..03dce142 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -64,11 +64,12 @@ public function __construct($config = array()) * @return object|boolean Migration info object. * * @since 4.0.0 + * @throws \Exception */ public function getScript() { // Retreive script variable - $name = $this->app->input->get('script', '', 'cmd'); + $name = $this->app->getUserStateFromRequest(_JOOM_OPTION.'.migration.script', 'script', '', 'cmd'); if(!$name) { @@ -108,6 +109,26 @@ public function getScripts() return $scripts; } + /** + * Method to a list of content types which can be migrated using the selected script. + * + * @return array|boolean List of content types on success, false otherwise + * + * @since 4.0.0 + */ + public function getMigrateables() + { + // Retreive script + $script = $this->getScript(); + + if(!$script) + { + return false; + } + + return $this->component->getMigration()->getMigrateables(); + } + /** * Method to get the migration form. * diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index cc2444a1..e0d537f3 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -64,6 +64,15 @@ abstract class Migration implements MigrationInterface */ protected $isCli = false; + /** + * List of content types which can be migrated with this script + * + * @var array + * + * @since 4.0.0 + */ + protected $contentTypes = array(); + /** * Constructor @@ -91,6 +100,18 @@ public function __construct() $this->info->startBtnText = Text::_('COM_JOOMGALLERY_MIGRATION_STEP1_BTN_TXT'); } + /** + * Returns a list of content types which can be migrated. + * + * @return array List of content types + * + * @since 4.0.0 + */ + public function getMigrateables(): array + { + return $this->contentTypes; + } + /** * Step 2 * Perform pre migration checks. @@ -114,7 +135,7 @@ public function precheck(): array ), (object) array( 'name' => 'thumbs', - 'result' => false, + 'result' => true, 'title' => 'Thumbnail images', 'description' => '/joomla3/images/joomgallery/thumbnails/', 'help' => 'Folder (/joomla3/images/joomgallery/thumbnails/) is not writeable. make sure the permissions are set correctly for this folder.' diff --git a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php index 395b8981..cbc5a821 100644 --- a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php +++ b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php @@ -59,4 +59,13 @@ public function postcheck(); * @since 4.0.0 */ public function migrate($type, $source, $dest); + + /** + * Returns a list of content types which can be migrated. + * + * @return array List of content types + * + * @since 4.0.0 + */ + public function getMigrateables(): array; } diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index c9e49db0..a60320dc 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -35,6 +35,16 @@ class Jg3ToJg4 extends Migration implements MigrationInterface */ protected $name = 'Jg3ToJg4'; + /** + * List of content types which can be migrated with this script + * Use the singular form of the content type (e.g image, not images) + * + * @var array + * + * @since 4.0.0 + */ + protected $contentTypes = array('image', 'category'); + /** * Constructor * diff --git a/administrator/com_joomgallery/src/View/Migration/HtmlView.php b/administrator/com_joomgallery/src/View/Migration/HtmlView.php index b4904338..bb49b42b 100644 --- a/administrator/com_joomgallery/src/View/Migration/HtmlView.php +++ b/administrator/com_joomgallery/src/View/Migration/HtmlView.php @@ -87,8 +87,10 @@ public function display($tpl = null) break; case 'step3': - // Load migration results - $this->migration = $this->app->getUserState(_JOOM_OPTION.'.migration.'.$this->script->name.'.step3.results', array()); + // Data for the migration view + $this->precheck = $this->app->getUserState(_JOOM_OPTION.'.migration.'.$this->script->name.'.step2.success', false); + $this->migrateables = $this->get('Migrateables'); + $this->migration = $this->app->getUserState(_JOOM_OPTION.'.migration.'.$this->script->name.'.step3.results', array()); break; case 'step4': diff --git a/administrator/com_joomgallery/tmpl/migration/default.php b/administrator/com_joomgallery/tmpl/migration/default.php index 979ad255..102bb02e 100644 --- a/administrator/com_joomgallery/tmpl/migration/default.php +++ b/administrator/com_joomgallery/tmpl/migration/default.php @@ -16,8 +16,6 @@ use \Joomla\CMS\Router\Route; use \Joomla\CMS\Language\Text; -HTMLHelper::addIncludePath(JPATH_COMPONENT . '/src/Helper/'); - // Import CSS $wa = Factory::getApplication()->getDocument()->getWebAssetManager(); $wa->useStyle('com_joomgallery.admin') diff --git a/administrator/com_joomgallery/tmpl/migration/step1.php b/administrator/com_joomgallery/tmpl/migration/step1.php index 38623800..29e934e7 100644 --- a/administrator/com_joomgallery/tmpl/migration/step1.php +++ b/administrator/com_joomgallery/tmpl/migration/step1.php @@ -16,8 +16,6 @@ use \Joomla\CMS\Router\Route; use \Joomla\CMS\Language\Text; -HTMLHelper::addIncludePath(JPATH_COMPONENT . '/src/Helper/'); - // Import CSS $wa = Factory::getApplication()->getDocument()->getWebAssetManager(); $wa->useStyle('com_joomgallery.admin') diff --git a/administrator/com_joomgallery/tmpl/migration/step2.php b/administrator/com_joomgallery/tmpl/migration/step2.php index 75f3bb8a..63dac319 100644 --- a/administrator/com_joomgallery/tmpl/migration/step2.php +++ b/administrator/com_joomgallery/tmpl/migration/step2.php @@ -16,8 +16,6 @@ use \Joomla\CMS\Router\Route; use \Joomla\CMS\Language\Text; -HTMLHelper::addIncludePath(JPATH_COMPONENT . '/src/Helper/'); - // Import CSS $wa = Factory::getApplication()->getDocument()->getWebAssetManager(); $wa->useStyle('com_joomgallery.admin') @@ -102,7 +100,8 @@
- + + diff --git a/administrator/com_joomgallery/tmpl/migration/step3.php b/administrator/com_joomgallery/tmpl/migration/step3.php index 0097b892..72e7acf6 100644 --- a/administrator/com_joomgallery/tmpl/migration/step3.php +++ b/administrator/com_joomgallery/tmpl/migration/step3.php @@ -16,8 +16,6 @@ use \Joomla\CMS\Router\Route; use \Joomla\CMS\Language\Text; -HTMLHelper::addIncludePath(JPATH_COMPONENT . '/src/Helper/'); - // Import CSS $wa = Factory::getApplication()->getDocument()->getWebAssetManager(); $wa->useStyle('com_joomgallery.admin') @@ -47,5 +45,49 @@ - + + +
+ + error) && !empty($this->migrateables)) : ?> + migrateables as $key => $migrateable) : ?> +
+
+
+
+

+
+
+ Pendent: ? + Successful: 0 + Failed: 0 +
+ + + + +
+
+
+ +
+
+
+
+
+
+ + + +
+ + + + + +
\ No newline at end of file From 7de57a91bb9ed62b3de3335df914f973d57a8f65 Mon Sep 17 00:00:00 2001 From: Elfangor Date: Sat, 9 Sep 2023 08:38:26 +0200 Subject: [PATCH 006/121] update file headers --- .../com_joomgallery/src/Controller/MigrationController.php | 6 +++--- administrator/com_joomgallery/src/Model/MigrationModel.php | 6 +++--- .../com_joomgallery/src/Service/Migration/Checks.php | 6 +++--- .../com_joomgallery/src/Service/Migration/Migration.php | 6 +++--- .../src/Service/Migration/MigrationInterface.php | 6 +++--- .../src/Service/Migration/MigrationServiceInterface.php | 6 +++--- .../src/Service/Migration/MigrationServiceTrait.php | 6 +++--- .../src/Service/Migration/Scripts/Jg3ToJg4.php | 6 +++--- .../com_joomgallery/src/View/Migration/HtmlView.php | 6 +++--- administrator/com_joomgallery/tmpl/migration/default.php | 6 +++--- administrator/com_joomgallery/tmpl/migration/step1.php | 6 +++--- administrator/com_joomgallery/tmpl/migration/step2.php | 6 +++--- administrator/com_joomgallery/tmpl/migration/step3.php | 6 +++--- administrator/com_joomgallery/tmpl/migration/step4.php | 6 +++--- 14 files changed, 42 insertions(+), 42 deletions(-) diff --git a/administrator/com_joomgallery/src/Controller/MigrationController.php b/administrator/com_joomgallery/src/Controller/MigrationController.php index 553c68b0..337a36b6 100644 --- a/administrator/com_joomgallery/src/Controller/MigrationController.php +++ b/administrator/com_joomgallery/src/Controller/MigrationController.php @@ -1,11 +1,11 @@ ** -** @copyright 2008 - 2022 JoomGallery::ProjectTeam ** -** @license GNU General Public License version 2 or later ** +** @copyright 2008 - 2023 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 3 or later ** *****************************************************************************************/ namespace Joomgallery\Component\Joomgallery\Administrator\Controller; diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index 03dce142..f974d83d 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -1,11 +1,11 @@ ** -** @copyright 2008 - 2022 JoomGallery::ProjectTeam ** -** @license GNU General Public License version 2 or later ** +** @copyright 2008 - 2023 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 3 or later ** *****************************************************************************************/ namespace Joomgallery\Component\Joomgallery\Administrator\Model; diff --git a/administrator/com_joomgallery/src/Service/Migration/Checks.php b/administrator/com_joomgallery/src/Service/Migration/Checks.php index a25d2518..904d290d 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Checks.php +++ b/administrator/com_joomgallery/src/Service/Migration/Checks.php @@ -1,11 +1,11 @@ ** -** @copyright 2008 - 2022 JoomGallery::ProjectTeam ** -** @license GNU General Public License version 2 or later ** +** @copyright 2008 - 2023 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 3 or later ** *****************************************************************************************/ namespace Joomgallery\Component\Joomgallery\Administrator\Service\Migration\Checks; diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index e0d537f3..cad87d59 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -1,11 +1,11 @@ ** -** @copyright 2008 - 2022 JoomGallery::ProjectTeam ** -** @license GNU General Public License version 2 or later ** +** @copyright 2008 - 2023 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 3 or later ** *****************************************************************************************/ namespace Joomgallery\Component\Joomgallery\Administrator\Service\Migration; diff --git a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php index cbc5a821..79b6df58 100644 --- a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php +++ b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php @@ -1,11 +1,11 @@ ** -** @copyright 2008 - 2022 JoomGallery::ProjectTeam ** -** @license GNU General Public License version 2 or later ** +** @copyright 2008 - 2023 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 3 or later ** *****************************************************************************************/ namespace Joomgallery\Component\Joomgallery\Administrator\Service\Migration; diff --git a/administrator/com_joomgallery/src/Service/Migration/MigrationServiceInterface.php b/administrator/com_joomgallery/src/Service/Migration/MigrationServiceInterface.php index d92386b9..887f7da0 100644 --- a/administrator/com_joomgallery/src/Service/Migration/MigrationServiceInterface.php +++ b/administrator/com_joomgallery/src/Service/Migration/MigrationServiceInterface.php @@ -1,11 +1,11 @@ ** -** @copyright 2008 - 2022 JoomGallery::ProjectTeam ** -** @license GNU General Public License version 2 or later ** +** @copyright 2008 - 2023 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 3 or later ** *****************************************************************************************/ namespace Joomgallery\Component\Joomgallery\Administrator\Service\Migration; diff --git a/administrator/com_joomgallery/src/Service/Migration/MigrationServiceTrait.php b/administrator/com_joomgallery/src/Service/Migration/MigrationServiceTrait.php index b55292a9..a9d75fc4 100644 --- a/administrator/com_joomgallery/src/Service/Migration/MigrationServiceTrait.php +++ b/administrator/com_joomgallery/src/Service/Migration/MigrationServiceTrait.php @@ -1,11 +1,11 @@ ** -** @copyright 2008 - 2022 JoomGallery::ProjectTeam ** -** @license GNU General Public License version 2 or later ** +** @copyright 2008 - 2023 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 3 or later ** *****************************************************************************************/ namespace Joomgallery\Component\Joomgallery\Administrator\Service\Migration; diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index a60320dc..f0d05619 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -1,11 +1,11 @@ ** -** @copyright 2008 - 2022 JoomGallery::ProjectTeam ** -** @license GNU General Public License version 2 or later ** +** @copyright 2008 - 2023 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 3 or later ** *****************************************************************************************/ namespace Joomgallery\Component\Joomgallery\Administrator\Service\Migration\Scripts; diff --git a/administrator/com_joomgallery/src/View/Migration/HtmlView.php b/administrator/com_joomgallery/src/View/Migration/HtmlView.php index bb49b42b..c1873e70 100644 --- a/administrator/com_joomgallery/src/View/Migration/HtmlView.php +++ b/administrator/com_joomgallery/src/View/Migration/HtmlView.php @@ -1,11 +1,11 @@ ** -** @copyright 2008 - 2022 JoomGallery::ProjectTeam ** -** @license GNU General Public License version 2 or later ** +** @copyright 2008 - 2023 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 3 or later ** *****************************************************************************************/ namespace Joomgallery\Component\Joomgallery\Administrator\View\Migration; diff --git a/administrator/com_joomgallery/tmpl/migration/default.php b/administrator/com_joomgallery/tmpl/migration/default.php index 102bb02e..e722be5c 100644 --- a/administrator/com_joomgallery/tmpl/migration/default.php +++ b/administrator/com_joomgallery/tmpl/migration/default.php @@ -1,11 +1,11 @@ ** -** @copyright 2008 - 2022 JoomGallery::ProjectTeam ** -** @license GNU General Public License version 2 or later ** +** @copyright 2008 - 2023 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 3 or later ** *****************************************************************************************/ // No direct access diff --git a/administrator/com_joomgallery/tmpl/migration/step1.php b/administrator/com_joomgallery/tmpl/migration/step1.php index 29e934e7..5a4c5a08 100644 --- a/administrator/com_joomgallery/tmpl/migration/step1.php +++ b/administrator/com_joomgallery/tmpl/migration/step1.php @@ -1,11 +1,11 @@ ** -** @copyright 2008 - 2022 JoomGallery::ProjectTeam ** -** @license GNU General Public License version 2 or later ** +** @copyright 2008 - 2023 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 3 or later ** *****************************************************************************************/ // No direct access diff --git a/administrator/com_joomgallery/tmpl/migration/step2.php b/administrator/com_joomgallery/tmpl/migration/step2.php index 63dac319..0fb49244 100644 --- a/administrator/com_joomgallery/tmpl/migration/step2.php +++ b/administrator/com_joomgallery/tmpl/migration/step2.php @@ -1,11 +1,11 @@ ** -** @copyright 2008 - 2022 JoomGallery::ProjectTeam ** -** @license GNU General Public License version 2 or later ** +** @copyright 2008 - 2023 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 3 or later ** *****************************************************************************************/ // No direct access diff --git a/administrator/com_joomgallery/tmpl/migration/step3.php b/administrator/com_joomgallery/tmpl/migration/step3.php index 72e7acf6..fa3740b8 100644 --- a/administrator/com_joomgallery/tmpl/migration/step3.php +++ b/administrator/com_joomgallery/tmpl/migration/step3.php @@ -1,11 +1,11 @@ ** -** @copyright 2008 - 2022 JoomGallery::ProjectTeam ** -** @license GNU General Public License version 2 or later ** +** @copyright 2008 - 2023 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 3 or later ** *****************************************************************************************/ // No direct access diff --git a/administrator/com_joomgallery/tmpl/migration/step4.php b/administrator/com_joomgallery/tmpl/migration/step4.php index 2f745d15..0936a74a 100644 --- a/administrator/com_joomgallery/tmpl/migration/step4.php +++ b/administrator/com_joomgallery/tmpl/migration/step4.php @@ -1,11 +1,11 @@ ** -** @copyright 2008 - 2022 JoomGallery::ProjectTeam ** -** @license GNU General Public License version 2 or later ** +** @copyright 2008 - 2023 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 3 or later ** *****************************************************************************************/ // No direct access From 9a88d5c1723e448009262be767668d53dfa56d7d Mon Sep 17 00:00:00 2001 From: Elfangor Date: Sat, 9 Sep 2023 17:56:22 +0200 Subject: [PATCH 007/121] add migration functionality --- .../language/en-GB/com_joomgallery.ini | 1 + .../com_joomgallery/src/Helper/JoomHelper.php | 4 +- .../src/Model/MigrationModel.php | 218 +++++++++++++++++- .../Service/Migration/MigrationInterface.php | 42 ++++ .../src/Table/CategoryTable.php | 1 + .../com_joomgallery/src/Table/ImageTable.php | 1 + .../src/Table/MigrationTableTrait.php | 60 +++++ 7 files changed, 318 insertions(+), 9 deletions(-) create mode 100644 administrator/com_joomgallery/src/Table/MigrationTableTrait.php diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index a0679323..5195bcc9 100755 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -268,6 +268,7 @@ COM_JOOMGALLERY_ERROR_REPLACE_IMAGETYPE="Image-Type (%s) successfully replaced." COM_JOOMGALLERY_SUCCESS_IMAGETYPE="Image-Type (%s) could not be replaced. Error: %s." COM_JOOMGALLERY_SUCCESS_REPLACE_IMAGETYPE="Image-Type (%s) successfully replaced." COM_JOOMGALLERY_ERROR_REPLACE_IMAGETYPE="Image-Type (%s) could not be replaced. Error: %s." +COM_JOOMGALLERY_ERROR_TABLE_NOT_EXISTING="Table for content type '%s' does not exist." ;Mail Templates COM_JOOMGALLERY_MAIL_NEWIMAGE_TITLE="JoomGallery: New Image" diff --git a/administrator/com_joomgallery/src/Helper/JoomHelper.php b/administrator/com_joomgallery/src/Helper/JoomHelper.php index e8bf53d1..dcd905dd 100644 --- a/administrator/com_joomgallery/src/Helper/JoomHelper.php +++ b/administrator/com_joomgallery/src/Helper/JoomHelper.php @@ -681,7 +681,7 @@ public static function getRecordIDbyAliasOrFilename($record, $name) * * @since 4.0.0 */ - protected static function isAvailable($name) + public static function isAvailable($name) { if(!\in_array($name, \array_keys(self::$content_types))) { @@ -700,7 +700,7 @@ protected static function isAvailable($name) * * @since 4.0.0 */ - protected static function getImgZero($type, $url=true, $root=true) + public static function getImgZero($type, $url=true, $root=true) { if($url) { diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index f974d83d..ca4d624c 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -18,6 +18,9 @@ use \Joomla\CMS\Form\Form; use \Joomla\CMS\Filesystem\Folder; use \Joomla\CMS\MVC\Model\FormModel; +use \Joomla\CMS\Language\Multilanguage; +use \Joomgallery\Component\Joomgallery\Administrator\Table\ImageTable; +use \Joomgallery\Component\Joomgallery\Administrator\Helper\JoomHelper; /** * Migration model. @@ -56,6 +59,9 @@ public function __construct($config = array()) $this->app = Factory::getApplication('administrator'); $this->component = $this->app->bootComponent(_JOOM_OPTION); $this->user = Factory::getUser(); + + // Create config service + $this->component->createConfig(); } /** @@ -211,11 +217,11 @@ public function precheck($params) } /** - * Method to perform the pre migration checks. + * Method to perform the post migration checks. * * @param array $params The migration parameters entered in the migration form * - * @return array|boolean An array containing the precheck results on success. + * @return array|boolean An array containing the postcheck results on success. * * @since 4.0.0 */ @@ -231,22 +237,220 @@ public function postcheck($params) } /** - * Method to perform the pre migration checks. + * Method to perform one migration of one record. * - * @param array $params The migration parameters entered in the migration form + * @param string $type Name of the content type to migrate. + * @param integer $pk The primary key of the source record. + * @param array $params The migration parameters entered in the migration form * - * @return array|boolean An array containing the precheck results on success. + * @return object The object containing the migration results. * * @since 4.0.0 */ - public function migrate($params) + public function migrate($type, $pk, $params) { $info = $this->getScript(); // Set the migration parameters $this->component->getMigration()->set('params', (object) $params); - // Perform the prechecks + // Get record data from source + $data = $this->component->getMigration()->getData($type, $pk); + + // Apply data mapping based on migration parameters + $data = $this->component->getMigration()->applyDataMapping($data); + + // Create new record based on data + $record = $this->insertRecord($type, $data, $params['same_ids']); + + // Create imagetypes + if($type === 'image') + { + $img_source = $this->component->getMigration()->getImageSource($data); + if(\array_key_first($img_source) === 0) + { + // Create imagetypes based on one given image + $this->createImages($record, $img_source[0]); + } + else + { + // Reuse images from source as imagetypes + $this->reuseImages($record, $img_source); + } + } + return $this->component->getMigration()->migrate(); } + + /** + * Method to insert a content type record from migration data. + * + * @param string $type Name of the content type to insert. + * @param array $data The record data gathered from the migration source. + * @param bool $newID True to auto-increment the id in the database + * + * @return object Inserted record object on success, False on error. + * + * @since 4.0.0 + */ + protected function insertRecord(string $type, array $data, bool $newID=true): bool + { + // Check content type + JoomHelper::isAvailable($type); + + // Create table + if(!$table = $this->getMVCFactory()->createTable($type, 'administrator')) + { + $this->setError(Text::sprintf('COM_JOOMGALLERY_ERROR_TABLE_NOT_EXISTING', $type)); + + return false; + } + + // Get table primary key name + $key = $table->getKeyName(); + + // Disable auto-incrementing record ID + if($newID && \in_array($key, \array_keys($data)) && \method_exists($table, 'insertID')) + { + $table->insertID(); + } + + // Change language to 'All' if multilangugae is not enabled + if(!Multilanguage::isEnabled()) + { + $data['language'] = '*'; + } + + // Bind migrated data to table object + if(!$table->bind($data)) + { + $this->setError($table->getError()); + + return false; + } + + // Prepare the row for saving + $this->prepareTable($table); + + // Check the data. + if(!$table->check()) + { + $this->setError($table->getError()); + + return false; + } + + // Store the data. + if(!$table->store()) + { + $this->setError($table->getError()); + + return false; + } + + return $table; + } + + /** + * Creation of imagetypes based on one source file. + * Source file has to be given with a full system path. + * + * @param ImageTable $img ImageTable object, already stored + * @param string $source Source file with which the imagetypes shall be created + * + * @return bool True on success, false otherwise + * + * @since 4.0.0 + */ + protected function createImages(ImageTable $img, string $source): bool + { + // Create file manager service + $this->component->createFileManager(); + + return $this->component->getFileManager()->createImages($source, $img->filename, $img->catid); + } + + /** + * Creation of imagetypes based on images already available on the server. + * Source files has to be given for each imagetype with a full system path. + * + * @param ImageTable $img ImageTable object, already stored + * @param array $sources List of source images for each imagetype + * + * @return bool True on success, false otherwise + * + * @since 4.0.0 + * @throws \Exception + */ + protected function reuseImages(ImageTable $img, array $sources): bool + { + // Create services + $this->component->createFileManager(); + $this->component->createFilesystem($this->component->getConfig()->get('jg_filesystem','local-images')); + + // Fetch available imagetypes + $imagetypes = $this->component->getFileManager()->get('imagetypes'); + + // Check the sources + if($imagetypes != \array_keys($sources)) + { + throw new \Exception('Imagetype mapping from migration script does not match component configuration!', 1); + } + + // Loop through all imagetypes + $error = false; + foreach($sources as $type => $path) + { + // Get image source path + $img_src = $path; + + // Get category destination path + $cat_dst = $this->component->getFileManager()->getCatPath($img->catid, $type); + + // Create image destination path + $img_dst = $cat_dst . '/' . $img->filename; + + // Create folders if not existent + $folder_dst = \dirname($img_dst); + try + { + $this->component->getFilesystem()->createFolder(\basename($folder_dst), \dirname($folder_dst)); + } + catch(\FileExistsException $e) + { + // Do nothing + } + catch(\Exception $e) + { + // Debug info + $this->component->addDebug(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_CREATE_CATEGORY', \ucfirst($folder_dst))); + $error = true; + + continue; + } + + // Move images + try + { + $this->component->getFilesystem()->move($img_src, $img_dst); + } + catch(\Exception $e) + { + // Operation failed + $this->component->addDebug(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_MOVE_IMAGETYPE', \basename($img_src), $type)); + $error = true; + + continue; + } + } + + if($error) + { + return false; + } + else + { + return true; + } + } } diff --git a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php index 79b6df58..ca6b66cd 100644 --- a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php +++ b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php @@ -68,4 +68,46 @@ public function migrate($type, $source, $dest); * @since 4.0.0 */ public function getMigrateables(): array; + + /** + * Returns a associative array containing the record data written from source. + * + * @param string $type Name of the content type + * @param int $pk The primary key of the content type + * + * @return array Record data + * + * @since 4.0.0 + */ + public function getData(string $type, int $pk): array; + + /** + * Translates the record data from source structure to destination structure + * based on migration parameters + * + * @param array $data Record data received from getData() + * + * @return array Restructured record data to save into JG4 + * + * @since 4.0.0 + */ + public function applyDataMapping(array $data): array; + + /** + * Fetches an array of source images for the current migrated image + * based on migration parameters. + * + * There are two possibilities how the new imagetypes are created: + * 1. Imagetypes get recreated using one source image from the migration source + * 2. Imagetypes get copied from existing images available from the migration source + * + * @param array $data Record data received from getData() + * + * @return array List of image sources to be used to create the new imagetypes + * If imagetypes get recreated: array('image/source/path') + * If imagetypes get copied: array('original' => 'image/source/path1', 'detail' => 'image/source/path2', ...) + * + * @since 4.0.0 + */ + public function getImageSource(array $data): array; } diff --git a/administrator/com_joomgallery/src/Table/CategoryTable.php b/administrator/com_joomgallery/src/Table/CategoryTable.php index 912ecce1..d02ce1ba 100644 --- a/administrator/com_joomgallery/src/Table/CategoryTable.php +++ b/administrator/com_joomgallery/src/Table/CategoryTable.php @@ -34,6 +34,7 @@ class CategoryTable extends Table implements VersionableTableInterface { use JoomTableTrait; + use MigrationTableTrait; /** * Object property to hold the path of the new location reference node. diff --git a/administrator/com_joomgallery/src/Table/ImageTable.php b/administrator/com_joomgallery/src/Table/ImageTable.php index 02cb316c..c55ac1c5 100644 --- a/administrator/com_joomgallery/src/Table/ImageTable.php +++ b/administrator/com_joomgallery/src/Table/ImageTable.php @@ -33,6 +33,7 @@ class ImageTable extends Table implements VersionableTableInterface { use JoomTableTrait; + use MigrationTableTrait; /** * Constructor diff --git a/administrator/com_joomgallery/src/Table/MigrationTableTrait.php b/administrator/com_joomgallery/src/Table/MigrationTableTrait.php new file mode 100644 index 00000000..49fb1b9b --- /dev/null +++ b/administrator/com_joomgallery/src/Table/MigrationTableTrait.php @@ -0,0 +1,60 @@ + ** +** @copyright 2008 - 2023 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 3 or later ** +*****************************************************************************************/ + +namespace Joomgallery\Component\Joomgallery\Administrator\Table; + +\defined('_JEXEC') or die; + +/** +* Add functionality for tables of migrateable records +* +* @since 4.0.0 +*/ +trait MigrationTableTrait +{ + /** + * True to insert the provided value of the primary key + * Needed if you want to create a new record with a given ID + * + * @var bool + */ + protected $_insertID = false; + + /** + * Validate that the primary key has been set. + * + * @return boolean True if the primary key(s) have been set. + * + * @since 3.1.4 + */ + public function hasPrimaryKey() + { + if($this->_insertID) + { + return false; + } + else + { + return parent::hasPrimaryKey(); + } + } + + /** + * Method to set force using the provided ID when storing a new record. + * + * @return void + * + * @since 4.0.0 + */ + public function insertID() + { + $this->_insertID = true; + } +} From 212f88db1d187789bcd1294b10be901fcd8313b4 Mon Sep 17 00:00:00 2001 From: Elfangor Date: Sun, 17 Sep 2023 20:42:33 +0200 Subject: [PATCH 008/121] add translations --- .../language/en-GB/com_joomgallery.ini | 27 ++++++- .../com_joomgallery.migration.Jg3ToJg4.ini | 14 ++++ .../src/Service/Migration/Migration.php | 2 +- .../Service/Migration/MigrationInterface.php | 25 +++--- .../Service/Migration/Scripts/Jg3ToJg4.php | 69 +++++++++++++++++ .../Service/Migration/Scripts/Jg3ToJg4.xml | 77 +++++++++---------- .../tmpl/migration/default.php | 2 +- 7 files changed, 159 insertions(+), 57 deletions(-) create mode 100644 administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index 5195bcc9..b9de1db3 100755 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -78,6 +78,7 @@ COM_JOOMGALLERY_DEBUG_MODE="Debug Mode" COM_JOOMGALLERY_IMAGE_SELECTION="Image selection" COM_JOOMGALLERY_IMAGE_SOURCE="Source" COM_JOOMGALLERY_REPLACE="Replace" +COM_JOOMGALLERY_MIGRATION="Migration" ;Control panel COM_JOOMGALLERY_CONTROL_PANEL="Control Panel" @@ -177,7 +178,9 @@ COM_JOOMGALLERY_GENERIC_UPLOAD_DATA="Data entered in this form is applied on all COM_JOOMGALLERY_FILE_TITLE_HINT="Title of this file" COM_JOOMGALLERY_FILE_DESCRIPTION_HINT="Description of this file" COM_JOOMGALLERY_FILE_AUTHOR_HINT="Author of this file" - +COM_JOOMGALLERY_USE_SCRIPT="Use this script" +COM_JOOMGALLERY_AVAILABLE_SCRIPT="Available migration scripts" +COM_JOOMGALLERY_MIGRATION_STEP1_BTN_TXT="Submit & Start Pre-Check" ;Messages COM_JOOMGALLERY_NOTE_DEVELOPMENT_VERSION="Attention!
----------------
This version of JoomGallery is still under development. Do not use this version on a live website. It is intended for testing purposes only..." @@ -305,6 +308,28 @@ COM_JOOMGALLERY_FIELDS_NUMBERING_START="Numbering start" COM_JOOMGALLERY_FIELDS_NUMBERING_START_DESC="Start numbering images with the following value." COM_JOOMGALLERY_FIELDS_REPLACE_IMAGETYPE_DESC="Select the image type to replace." COM_JOOMGALLERY_FIELDS_REPLACE_IMAGETYPE_PROCESS_DESC="Yes to process the image before replacement (resize, watermarking, ...)." +COM_JOOMGALLERY_FIELDS_SOURCE_LBL="Configuration for source data fetching" +COM_JOOMGALLERY_FIELDS_DEST_LBL="Configuration for destination data writing" +COM_JOOMGALLERY_FIELDS_SAMEJOOMLA_LABEL="Same Joomla! installation?" +COM_JOOMGALLERY_FIELDS_SAMEJOOMLA_DESC="Is the source located inside the same Joomla! installation folder?" +COM_JOOMGALLERY_FIELDS_JOOMLAPATH_LABEL="Joomla! path" +COM_JOOMGALLERY_FIELDS_JOOMLAPATH_DESC="Path to the Joomla! installation of the source." +COM_JOOMGALLERY_FIELDS_SAMEDB_LABEL="Same database?" +COM_JOOMGALLERY_FIELDS_SAMEDB_DESC="Is the source located inside the same Joomla! database?" +COM_JOOMGALLERY_FIELDS_DATABASE_TYPE_LABEL="Database Type" +COM_JOOMGALLERY_FIELDS_DATABASE_HOST_LABEL="Host" +COM_JOOMGALLERY_FIELDS_DATABASE_USER_LABEL="Database Username" +COM_JOOMGALLERY_FIELDS_DATABASE_PASS_LABEL="Database Password" +COM_JOOMGALLERY_FIELDS_DATABASE_NAME_LABEL="Database Name" +COM_JOOMGALLERY_FIELDS_DATABASE_PREFIX_LABEL="Database Tables Prefix" +COM_JOOMGALLERY_FIELDS_CHECKOWNER_LABEL="Check owner" +COM_JOOMGALLERY_FIELDS_CHECKOWNER_DESC="Do you wan to check if the Joomla! users (owners) exist on the source system?" +COM_JOOMGALLERY_FIELDS_IMAGEUSAGE_LABEL="Image usage" +COM_JOOMGALLERY_FIELDS_IMAGEUSAGE_DESC="How do you want to use the images provided by the source?{tip} Direct usage: Do nothing. Directly usage of the image in source folder. Copy: Copy the chosen images and use them as imagetype. Recreate: Use the chosen images as source for recreating the imagetype." +COM_JOOMGALLERY_FIELDS_IMAGEMAPPING_LABEL="Image mapping" +COM_JOOMGALLERY_FIELDS_IMAGEMAPPING_DESC="Image mapping. Which source image should be used for which destination imagetype. You need to add one entry for each available imagetype!" +COM_JOOMGALLERY_FIELDS_SOURCE_IMAGE_LABEL="Source image" +COM_JOOMGALLERY_FIELDS_DEST_IMAGE_LABEL="Destination imagetype" ;Configuration COM_JOOMGALLERY_CONFIG_INHERITANCE_METHOD_LABEL="Configuration inheritance" diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini new file mode 100644 index 00000000..91438d23 --- /dev/null +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini @@ -0,0 +1,14 @@ +;****************************************************************************************** +;** @version 4.0.0-dev ** +;** @package com_joomgallery ** +;** @author JoomGallery::ProjectTeam ** +;** @copyright 2008 - 2023 JoomGallery::ProjectTeam ** +;** @license GNU General Public License version 3 or later ** +;*****************************************************************************************/ +; +FILES_JOOMGALLERY_MIGRATION_JG3TOJG4="JoomGallery 3.x to JoomGallery 4.x migration script" +FILES_JOOMGALLERY_MIGRATION_JG3TOJG4_TITLE="JoomGallery 3.x to JoomGallery 4.x" +FILES_JOOMGALLERY_MIGRATION_JG3TOJG4_DESC="Migration of content types (images, categories, tags) from JoomGallery version 3.x to JoomGallery version 4.x." +FILES_JOOMGALLERY_MIGRATION_JG3TOJG4_ORIGPATH_DESC="Path to original image" +FILES_JOOMGALLERY_MIGRATION_JG3TOJG4_DETAILPATH_DESC="Path to detail image" +FILES_JOOMGALLERY_MIGRATION_JG3TOJG4_THUMBPATH_DESC="Path to thumbnail image" \ No newline at end of file diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index cad87d59..09e2d107 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -90,7 +90,7 @@ public function __construct() $this->getComponent(); // Try to load language file of the migration script - $this->app->getLanguage()->load('com_joomgallery.migration'.$this->name, JPATH_ADMINISTRATOR); + $this->app->getLanguage()->load('com_joomgallery.migration.'.$this->name, _JOOM_PATH_ADMIN); // Fill info object $this->info = new \stdClass; diff --git a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php index ca6b66cd..5f7fe522 100644 --- a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php +++ b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php @@ -70,7 +70,7 @@ public function migrate($type, $source, $dest); public function getMigrateables(): array; /** - * Returns a associative array containing the record data written from source. + * Returns an associative array containing the record data from source. * * @param string $type Name of the content type * @param int $pk The primary key of the content type @@ -82,30 +82,25 @@ public function getMigrateables(): array; public function getData(string $type, int $pk): array; /** - * Translates the record data from source structure to destination structure - * based on migration parameters + * Converts data from source into the structure needed for JoomGallery. * - * @param array $data Record data received from getData() + * @param array $data Data received from getData() method. * - * @return array Restructured record data to save into JG4 + * @return array Converted data to save into JoomGallery * * @since 4.0.0 */ - public function applyDataMapping(array $data): array; + public function convertData(array $data): array; /** - * Fetches an array of source images for the current migrated image - * based on migration parameters. - * - * There are two possibilities how the new imagetypes are created: - * 1. Imagetypes get recreated using one source image from the migration source - * 2. Imagetypes get copied from existing images available from the migration source + * Fetches an array of images from source to be used for creating the imagetypes + * for the current image. * * @param array $data Record data received from getData() * - * @return array List of image sources to be used to create the new imagetypes - * If imagetypes get recreated: array('image/source/path') - * If imagetypes get copied: array('original' => 'image/source/path1', 'detail' => 'image/source/path2', ...) + * @return array List of images from sources used to create the new imagetypes + * 1. If imagetypes get recreated: array('image/source/path') + * 2. If imagetypes get copied: array('original' => 'image/source/path1', 'detail' => 'image/source/path2', ...) * * @since 4.0.0 */ diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index f0d05619..ce08f69d 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -56,4 +56,73 @@ public function __construct() { parent::__construct(); } + + /** + * Returns an associative array containing the record data from source. + * + * @param string $type Name of the content type + * @param int $pk The primary key of the content type + * + * @return array Record data + * + * @since 4.0.0 + */ + public function getData(string $type, int $pk): array + { + switch ($type) + { + case 'category': + return $this->getCategoryData($pk); + break; + + default: + return array(); + break; + } + } + + /** + * Converts data from source into the structure needed for JoomGallery. + * + * @param array $data Data received from getData() method. + * + * @return array Converted data to save into JoomGallery + * + * @since 4.0.0 + */ + public function convertData(array $data): array + { + return array(); + } + + /** + * Fetches an array of images from source to be used for creating the imagetypes + * for the current image. + * + * @param array $data Record data received from getData() + * + * @return array List of images from sources used to create the new imagetypes + * 1. If imagetypes get recreated: array('image/source/path') + * 2. If imagetypes get copied: array('original' => 'image/source/path1', 'detail' => 'image/source/path2', ...) + * + * @since 4.0.0 + */ + public function getImageSource(array $data): array + { + return array(); + } + + /** + * Returns an associative array containing the category data from source. + * + * @param int $pk The primary key of the category + * + * @return array Category data array + * + * @since 4.0.0 + */ + public function getCategoryData(int $pk): array + { + return array(); + } } \ No newline at end of file diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.xml b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.xml index cf8c3d27..c15fb5d7 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.xml +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.xml @@ -23,24 +23,24 @@ filter="string" required="true" default="images/joomgallery/originals/" - label="COM_JOOMGALLERY_FIELDS_ORIGPATH_LABEL" - description="COM_JOOMGALLERY_FIELDS_ORIGPATH_DESC" + label="COM_JOOMGALLERY_ORIGINAL" + description="FILES_JOOMGALLERY_MIGRATION_JG3TOJG4_ORIGPATH_DESC" /> @@ -109,33 +103,38 @@ - - - + label="COM_JOOMGALLERY_FIELDS_IMAGEUSAGE_LABEL" + description="COM_JOOMGALLERY_FIELDS_IMAGEUSAGE_DESC"> + + + + + +
+ + + + + + +
- - - diff --git a/administrator/com_joomgallery/tmpl/migration/default.php b/administrator/com_joomgallery/tmpl/migration/default.php index e722be5c..aba84e09 100644 --- a/administrator/com_joomgallery/tmpl/migration/default.php +++ b/administrator/com_joomgallery/tmpl/migration/default.php @@ -23,7 +23,7 @@ ?>
-

Available migration scripts:

+

:


scripts as $name => $script) : ?> From 4f00e2c242df1adee166aa64659b8df028106661 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 18 Sep 2023 16:55:04 +0200 Subject: [PATCH 009/121] add some prechecks --- .../language/en-GB/com_joomgallery.ini | 32 ++- .../src/Extension/JoomgalleryComponent.php | 15 +- .../src/Model/MigrationModel.php | 2 +- .../src/Service/Migration/Migration.php | 195 +++++++++++++++++- .../Service/Migration/Scripts/Jg3ToJg4.xml | 20 +- .../src/View/Migration/HtmlView.php | 8 + 6 files changed, 250 insertions(+), 22 deletions(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index b9de1db3..819624a7 100755 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -79,6 +79,12 @@ COM_JOOMGALLERY_IMAGE_SELECTION="Image selection" COM_JOOMGALLERY_IMAGE_SOURCE="Source" COM_JOOMGALLERY_REPLACE="Replace" COM_JOOMGALLERY_MIGRATION="Migration" +COM_JOOMGALLERY_LOGFILE="Log file" +COM_JOOMGALLERY_LOGDIRECTORY="Log directory" +COM_JOOMGALLERY_DIRECTORY="Directory" +COM_JOOMGALLERY_DIRECTORIES="Directories" +COM_JOOMGALLERY_GENERAL="General" +COM_JOOMGALLERY_SITE_OFFLINE="Site offline" ;Control panel COM_JOOMGALLERY_CONTROL_PANEL="Control Panel" @@ -180,7 +186,7 @@ COM_JOOMGALLERY_FILE_DESCRIPTION_HINT="Description of this file" COM_JOOMGALLERY_FILE_AUTHOR_HINT="Author of this file" COM_JOOMGALLERY_USE_SCRIPT="Use this script" COM_JOOMGALLERY_AVAILABLE_SCRIPT="Available migration scripts" -COM_JOOMGALLERY_MIGRATION_STEP1_BTN_TXT="Submit & Start Pre-Check" +COM_JOOMGALLERY_MIGRATION_STEP1_BTN_TXT="Check migration capability" ;Messages COM_JOOMGALLERY_NOTE_DEVELOPMENT_VERSION="Attention!
----------------
This version of JoomGallery is still under development. Do not use this version on a live website. It is intended for testing purposes only..." @@ -322,12 +328,12 @@ COM_JOOMGALLERY_FIELDS_DATABASE_USER_LABEL="Database Username" COM_JOOMGALLERY_FIELDS_DATABASE_PASS_LABEL="Database Password" COM_JOOMGALLERY_FIELDS_DATABASE_NAME_LABEL="Database Name" COM_JOOMGALLERY_FIELDS_DATABASE_PREFIX_LABEL="Database Tables Prefix" -COM_JOOMGALLERY_FIELDS_CHECKOWNER_LABEL="Check owner" -COM_JOOMGALLERY_FIELDS_CHECKOWNER_DESC="Do you wan to check if the Joomla! users (owners) exist on the source system?" +COM_JOOMGALLERY_FIELDS_CHECKOWNER_LABEL="Check owners" +COM_JOOMGALLERY_FIELDS_CHECKOWNER_DESC="If set to yes, the owners of the migrated categories and images will be checked. If a user ID cannot be found in the current Joomla! installation the owner will be set to 0 (no one)." COM_JOOMGALLERY_FIELDS_IMAGEUSAGE_LABEL="Image usage" -COM_JOOMGALLERY_FIELDS_IMAGEUSAGE_DESC="How do you want to use the images provided by the source?{tip} Direct usage: Do nothing. Directly usage of the image in source folder. Copy: Copy the chosen images and use them as imagetype. Recreate: Use the chosen images as source for recreating the imagetype." +COM_JOOMGALLERY_FIELDS_IMAGEUSAGE_DESC="How do you want to use the images provided by the source?{tip}Direct usage: Do nothing. Directly use the images in the source folder.
Copy: Copy the chosen images and use them as imagetypes.
Recreate: Use the chosen images as source for recreating the imagetypes." COM_JOOMGALLERY_FIELDS_IMAGEMAPPING_LABEL="Image mapping" -COM_JOOMGALLERY_FIELDS_IMAGEMAPPING_DESC="Image mapping. Which source image should be used for which destination imagetype. You need to add one entry for each available imagetype!" +COM_JOOMGALLERY_FIELDS_IMAGEMAPPING_DESC="Apply the image mapping. Which source image should be used to create the destination imagetype. You need to add one row for each available destination imagetype!" COM_JOOMGALLERY_FIELDS_SOURCE_IMAGE_LABEL="Source image" COM_JOOMGALLERY_FIELDS_DEST_IMAGE_LABEL="Destination imagetype" @@ -632,6 +638,22 @@ COM_JOOMGALLERY_SERVICE_ERROR_FILENOTFOUND="File not found." COM_JOOMGALLERY_SERVICE_ERROR_CREATE_FILE="File could not be created." COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_NOT_FOUND="No filesystem plugin available for the specified adapter (%s)." COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_ERROR="Filesystem plugin is reporting the following error: %s" +COM_JOOMGALLERY_SERVICE_MIGRATION_LOGFILE_CHECK_DESC="Log file existant and writeable." +COM_JOOMGALLERY_SERVICE_MIGRATION_DIRECTORY_CHECK_DESC="Existance and writeability of directories." +COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_CHECK_DESC="Version and compatibility checks of the extension." +COM_JOOMGALLERY_SERVICE_MIGRATION_STATE_CHECK_LABEL="State of the site" +COM_JOOMGALLERY_SERVICE_MIGRATION_SOURCE_DIRS_LABEL="Source directories" +COM_JOOMGALLERY_SERVICE_MIGRATION_DEST_DIRS_LABEL="Destination directories" +COM_JOOMGALLERY_SERVICE_MIGRATION_TABLES_CHECK_DESC="Existance and integrity of databasetables." +COM_JOOMGALLERY_SERVICE_MIGRATION_SOURCE_TABLES_LABEL="Source databasetables" +COM_JOOMGALLERY_SERVICE_MIGRATION_DEST_TABLES_LABEL="Destination databasetables" +COM_JOOMGALLERY_SERVICE_MIGRATION_LOG_DIR_LABEL="Logging directory" +COM_JOOMGALLERY_SERVICE_MIGRATION_LOGFILE_ERROR="Log file '%s' is not writable. Please check permissions." +COM_JOOMGALLERY_SERVICE_MIGRATION_LOGFILE_SUCCESS="Log file '%s' is writable." +COM_JOOMGALLERY_SERVICE_MIGRATION_LOGDIR_SUCCESS="Log file will be created in folder '%s'." +COM_JOOMGALLERY_SERVICE_MIGRATION_LOGDIR_ERROR="Log directory '%s' is not writable. Please check permissions." +COM_JOOMGALLERY_SERVICE_MIGRATION_OFFLINE_SUCCESS="Website is currently offline." +COM_JOOMGALLERY_SERVICE_MIGRATION_OFFLINE_ERROR="Website is currently online." ;Plugin events COM_JOOMGALLERY_PLUGIN_ERROR_RETURN_VALUE="Return value of the plugin event '%s' must be of type %s and contain : %s" diff --git a/administrator/com_joomgallery/src/Extension/JoomgalleryComponent.php b/administrator/com_joomgallery/src/Extension/JoomgalleryComponent.php index fce2936b..b6811e1f 100644 --- a/administrator/com_joomgallery/src/Extension/JoomgalleryComponent.php +++ b/administrator/com_joomgallery/src/Extension/JoomgalleryComponent.php @@ -86,6 +86,13 @@ class JoomgalleryComponent extends MVCComponent implements BootableExtensionInte */ public $cache = false; + /** + * Storage for the xml of the current component + * + * @var \SimpleXMLElement + */ + public $xml = null; + /** * Storage for the current component version * @@ -118,10 +125,14 @@ public function boot(ContainerInterface $container) $this->cache = new JoomCache(); } + if(!$this->xml) + { + $this->xml = \simplexml_load_file(Path::clean(JPATH_ADMINISTRATOR . '/components/com_joomgallery/joomgallery.xml')); + } + if(!$this->version) { - $xml = \simplexml_load_file(Path::clean(JPATH_ADMINISTRATOR . '/components/com_joomgallery/joomgallery.xml')); - $this->version = (string) $xml->version; + $this->version = (string) $this->xml->version; } } } diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index ca4d624c..f9b90f19 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -109,7 +109,7 @@ public function getScripts() { $img = Uri::base().'components/'._JOOM_OPTION.'/src/Service/Migration/Scripts/'.basename($path, '.php').'.jpg'; - $scripts[basename($path, '.php')] = array('path' => $path, 'img' => $img); + $scripts[basename($path, '.php')] = array('name' => basename($path, '.php'), 'path' => $path, 'img' => $img); } return $scripts; diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index 09e2d107..fa8ebf70 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -14,8 +14,11 @@ \defined('_JEXEC') or die; use \Joomla\CMS\Factory; +use \Joomla\CMS\Log\Log; use \Joomla\CMS\Language\Text; +use \Joomla\CMS\Filesystem\Path; use \Joomgallery\Component\Joomgallery\Administrator\Extension\ServiceTrait; +use Joomgallery\Component\Joomgallery\Administrator\Service\Migration\Checks\Checks; use \Joomgallery\Component\Joomgallery\Administrator\Service\Migration\MigrationInterface; /** @@ -73,6 +76,14 @@ abstract class Migration implements MigrationInterface */ protected $contentTypes = array(); + /** + * State if logger is created + * + * @var bool + * + * @since 4.0.0 + */ + protected $log = false; /** * Constructor @@ -92,12 +103,14 @@ public function __construct() // Try to load language file of the migration script $this->app->getLanguage()->load('com_joomgallery.migration.'.$this->name, _JOOM_PATH_ADMIN); + // Create logger + $this->addLogger(); + // Fill info object $this->info = new \stdClass; $this->info->name = $this->name; $this->info->title = Text::_('FILES_JOOMGALLERY_MIGRATION_'.strtoupper($this->name).'_TITLE'); $this->info->description = Text::_('FILES_JOOMGALLERY_MIGRATION_'.strtoupper($this->name).'_DESC'); - $this->info->startBtnText = Text::_('COM_JOOMGALLERY_MIGRATION_STEP1_BTN_TXT'); } /** @@ -122,7 +135,43 @@ public function getMigrateables(): array */ public function precheck(): array { - $checks = array((object) array('name' => 'directories', + // Instantiate a new checks class + $checks = new Checks(); + + // Check log file + $checks->addCategory('logfile', Text::_('COM_JOOMGALLERY_LOGFILE'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_LOGFILE_CHECK_DESC')); + $this->checkLogFile($checks, 'logfile'); + + // Check source extension (version, compatibility) + $checks->addCategory('source', Text::_('COM_JOOMGALLERY_GENERAL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_CHECK_DESC')); + $this->checkSourceExtension($checks, 'source'); + + // Check destination extension (version, compatibility) + $checks->addCategory('destination', Text::_('COM_JOOMGALLERY_GENERAL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_CHECK_DESC')); + $this->checkDestExtension($checks, 'destination'); + + // Check the state of the site and the environment (php, joomla, db, ...) + $checks->addCategory('state', Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_STATE_CHECK_LABEL')); + $this->checkEnvironment($checks, 'state'); + $this->checkSiteState($checks, 'state'); + + // Check existance and writeability of source directories + $checks->addCategory('source_directories', Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_SOURCE_DIRS_LABEL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_DIRECTORY_CHECK_DESC')); + + // Check existence and integrity of source databasetables + $checks->addCategory('source_tables', Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_SOURCE_TABLES_LABEL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_TABLES_CHECK_DESC')); + + // Check existance and writeability of destination directories + $checks->addCategory('dest_directories', Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_DEST_DIRS_LABEL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_DIRECTORY_CHECK_DESC')); + + // Check existence and integrity of destination databasetables + $checks->addCategory('dest_tables', Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_DEST_TABLES_LABEL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_TABLES_CHECK_DESC')); + + + + + + /* $checks = array((object) array( 'name' => 'directories', 'title' => 'Existence of directories', 'colTitle' => 'Folder', 'desc' => 'Are the nessecary directories available and writeable?', @@ -142,9 +191,9 @@ public function precheck(): array ), ) ) - ); + ); */ - return array(true, $checks); + return $checks->getAll(); } /** @@ -172,4 +221,142 @@ public function migrate($type, $source, $dest) { return; } + + /** + * Add a JoomGallery migration logger to the JLog class + * + * @return void + * + * @since 4.0.0 + */ + protected function addLogger() + { + if(!$this->log) + { + Log::addLogger(['text_file' => 'com_joomgallery.migration.log.php'], Log::ALL, ['com_joomgallery.migration']); + } + + $this->log = true; + } + + /** + * Log a message + * + * @param string $txt The message for a new log entry. + * @param integer $priority Message priority. + * + * @return void + * + * @since 4.0.0 + */ + protected function addLog($txt, $priority) + { + Log::add($txt, $priority, 'com_joomgallery.migration'); + } + + /** + * Precheck: Check logfile and add check to checks array. + * + * @param Checks $checks The checks object + * @param int $category The checks-category into which to add the new check + * + * @return void + * + * @since 4.0.0 + */ + protected function checkLogFile(Checks &$checks, int $category) + { + $log_dir = Path::clean($this->app->get('log_path')); + + if(\is_dir($log_dir)) + { + $log_file = Path::clean($log_dir . '/' . 'com_joomgallery.log.php'); + + if(\is_file($log_file)) + { + if(\is_writable($log_dir)) + { + $checks->addCheck($category, 'log_file', true, Text::_('COM_JOOMGALLERY_LOGFILE'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_LOGFILE_SUCCESS', $log_file)); + } + else + { + $checks->addCheck($category, 'log_file', false, Text::_('COM_JOOMGALLERY_LOGFILE'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_LOGFILE_ERROR', $log_file)); + } + } + else + { + if(\is_writable($log_dir)) + { + $checks->addCheck($category, 'log_dir', true, Text::_('COM_JOOMGALLERY_LOGDIRECTORY'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_LOGDIR_SUCCESS', $log_dir)); + } + else + { + $checks->addCheck($category, 'log_dir', false, Text::_('COM_JOOMGALLERY_LOGDIRECTORY'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_LOGDIR_ERROR', $log_dir)); + } + } + } + else + { + $checks->addCheck($category, 'log_dir', false, Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_LOG_DIR_LABEL'), Text::_('Logging directory not existent.')); + } + + } + + /** + * Precheck: Check the environment (joomla, php, db, ...) to be compatible with this migration script + * + * @param Checks $checks The checks object + * @param int $category The checks-category into which to add the new check + * + * @return void + * + * @since 4.0.0 + */ + abstract protected function checkEnvironment(Checks &$checks, int $category); + + /** + * Precheck: Check the source extension to be the correct one for this migration script + * + * @param Checks $checks The checks object + * @param int $category The checks-category into which to add the new check + * + * @return void + * + * @since 4.0.0 + */ + abstract protected function checkSourceExtension(Checks &$checks, int $category); + + /** + * Precheck: Check the destination extension to be the correct one for this migration script + * + * @param Checks $checks The checks object + * @param int $category The checks-category into which to add the new check + * + * @return void + * + * @since 4.0.0 + */ + abstract protected function checkDestExtension(Checks &$checks, int $category); + + /** + * Precheck: Check site state and add check to checks array. + * + * @param Checks $checks The checks object + * @param int $category The checks-category into which to add the new check + * + * @return void + * + * @since 4.0.0 + */ + protected function checkSiteState(Checks &$checks, int $category) + { + if($this->app->get('offline')) + { + $checks->addCheck($category, 'offline', true, Text::_('COM_JOOMGALLERY_SITE_OFFLINE'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_OFFLINE_SUCCESS')); + } + else + { + $checks->addCheck($category, 'offline', false, Text::_('COM_JOOMGALLERY_SITE_OFFLINE'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_OFFLINE_ERROR')); + } + } } diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.xml b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.xml index c15fb5d7..f0dc1445 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.xml +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.xml @@ -113,27 +113,27 @@ + min="1" >
- + label="COM_JOOMGALLERY_FIELDS_SOURCE_IMAGE_LABEL" > + diff --git a/administrator/com_joomgallery/src/View/Migration/HtmlView.php b/administrator/com_joomgallery/src/View/Migration/HtmlView.php index c1873e70..9257b58c 100644 --- a/administrator/com_joomgallery/src/View/Migration/HtmlView.php +++ b/administrator/com_joomgallery/src/View/Migration/HtmlView.php @@ -103,6 +103,14 @@ public function display($tpl = null) break; } } + else + { + // default view + foreach($this->scripts as $script) + { + $this->app->getLanguage()->load('com_joomgallery.migration.'.$script['name'], _JOOM_PATH_ADMIN); + } + } // Check for errors. if(count($errors = $this->get('Errors'))) From 109b659389d35cb607207c9a66128454c766e43e Mon Sep 17 00:00:00 2001 From: Elfangor Date: Sat, 23 Sep 2023 11:41:42 +0200 Subject: [PATCH 010/121] add some checks --- .../language/en-GB/com_joomgallery.ini | 9 +- .../src/Field/ExternalconfigField.php | 2 +- .../src/Service/Migration/Checks.php | 2 +- .../src/Service/Migration/Migration.php | 118 +++++++++--------- .../Service/Migration/MigrationInterface.php | 11 ++ .../src/Service/Migration/Targetinfo.php | 87 +++++++++++++ 6 files changed, 170 insertions(+), 59 deletions(-) create mode 100644 administrator/com_joomgallery/src/Service/Migration/Targetinfo.php diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index 819624a7..eb7f5918 100755 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -76,7 +76,8 @@ COM_JOOMGALLERY_IMPORT="Import" COM_JOOMGALLERY_MULTIPLE_NEW="Multiple New" COM_JOOMGALLERY_DEBUG_MODE="Debug Mode" COM_JOOMGALLERY_IMAGE_SELECTION="Image selection" -COM_JOOMGALLERY_IMAGE_SOURCE="Source" +COM_JOOMGALLERY_SOURCE="Source" +COM_JOOMGALLERY_DESTINATION="Destination" COM_JOOMGALLERY_REPLACE="Replace" COM_JOOMGALLERY_MIGRATION="Migration" COM_JOOMGALLERY_LOGFILE="Log file" @@ -336,6 +337,8 @@ COM_JOOMGALLERY_FIELDS_IMAGEMAPPING_LABEL="Image mapping" COM_JOOMGALLERY_FIELDS_IMAGEMAPPING_DESC="Apply the image mapping. Which source image should be used to create the destination imagetype. You need to add one row for each available destination imagetype!" COM_JOOMGALLERY_FIELDS_SOURCE_IMAGE_LABEL="Source image" COM_JOOMGALLERY_FIELDS_DEST_IMAGE_LABEL="Destination imagetype" +COM_JOOMGALLERY_FIELDS_DEST_EXTENSION_LABEL="Destination extension compatibility" +COM_JOOMGALLERY_FIELDS_SRC_EXTENSION_LABEL="Source extension compatibility" ;Configuration COM_JOOMGALLERY_CONFIG_INHERITANCE_METHOD_LABEL="Configuration inheritance" @@ -654,6 +657,10 @@ COM_JOOMGALLERY_SERVICE_MIGRATION_LOGDIR_SUCCESS="Log file will be created in fo COM_JOOMGALLERY_SERVICE_MIGRATION_LOGDIR_ERROR="Log directory '%s' is not writable. Please check permissions." COM_JOOMGALLERY_SERVICE_MIGRATION_OFFLINE_SUCCESS="Website is currently offline." COM_JOOMGALLERY_SERVICE_MIGRATION_OFFLINE_ERROR="Website is currently online." +COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_NOT_SUPPORTED="Extension not supported (Extension: %s)" +COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_WRONG_VERSION="The current version of the extension is not supported. Current version: %s. Supported version: %s" +COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_SUCCESS="Extension is compatible (Extension: %s, Version: %s)" +COM_JOOMGALLERY_SERVICE_MIGRATION_PHP_WRONG_VERSION="The current PHP version is not supported. Current version: %s. Minimum requirement: %s" ;Plugin events COM_JOOMGALLERY_PLUGIN_ERROR_RETURN_VALUE="Return value of the plugin event '%s' must be of type %s and contain : %s" diff --git a/administrator/com_joomgallery/src/Field/ExternalconfigField.php b/administrator/com_joomgallery/src/Field/ExternalconfigField.php index a90e9c82..92c75356 100644 --- a/administrator/com_joomgallery/src/Field/ExternalconfigField.php +++ b/administrator/com_joomgallery/src/Field/ExternalconfigField.php @@ -86,7 +86,7 @@ protected function getInput() $this->value = ComponentHelper::getParams($array[0])->get($array[1]); $this->readonly = true; - $this->description = Text::_(\strval($this->external->element->attributes()->description)) . ' ('.Text::_('COM_JOOMGALLERY_IMAGE_SOURCE').': '.$array[0].')'; + $this->description = Text::_(\strval($this->external->element->attributes()->description)) . ' ('.Text::_('COM_JOOMGALLERY_SOURCE').': '.$array[0].')'; $html = ''.Text::_('JACTION_EDIT').''; $html .= ''; diff --git a/administrator/com_joomgallery/src/Service/Migration/Checks.php b/administrator/com_joomgallery/src/Service/Migration/Checks.php index 904d290d..bf1d8fc3 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Checks.php +++ b/administrator/com_joomgallery/src/Service/Migration/Checks.php @@ -8,7 +8,7 @@ ** @license GNU General Public License version 3 or later ** *****************************************************************************************/ -namespace Joomgallery\Component\Joomgallery\Administrator\Service\Migration\Checks; +namespace Joomgallery\Component\Joomgallery\Administrator\Service\Migration; // No direct access \defined('_JEXEC') or die; diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index fa8ebf70..7199dd0f 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -139,59 +139,29 @@ public function precheck(): array $checks = new Checks(); // Check log file - $checks->addCategory('logfile', Text::_('COM_JOOMGALLERY_LOGFILE'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_LOGFILE_CHECK_DESC')); - $this->checkLogFile($checks, 'logfile'); + $checks->addCategory('general', Text::_('COM_JOOMGALLERY_GENERAL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_LOGFILE_CHECK_DESC')); + $this->checkLogFile($checks, 'general'); + $this->checkSiteState($checks, 'general'); // Check source extension (version, compatibility) - $checks->addCategory('source', Text::_('COM_JOOMGALLERY_GENERAL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_CHECK_DESC')); + $checks->addCategory('source', Text::_('COM_JOOMGALLERY_SOURCE'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_CHECK_DESC')); $this->checkSourceExtension($checks, 'source'); - // Check destination extension (version, compatibility) - $checks->addCategory('destination', Text::_('COM_JOOMGALLERY_GENERAL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_CHECK_DESC')); - $this->checkDestExtension($checks, 'destination'); - - // Check the state of the site and the environment (php, joomla, db, ...) - $checks->addCategory('state', Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_STATE_CHECK_LABEL')); - $this->checkEnvironment($checks, 'state'); - $this->checkSiteState($checks, 'state'); - // Check existance and writeability of source directories - $checks->addCategory('source_directories', Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_SOURCE_DIRS_LABEL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_DIRECTORY_CHECK_DESC')); + $this->checkSourceDir($checks, 'source_directories'); // Check existence and integrity of source databasetables - $checks->addCategory('source_tables', Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_SOURCE_TABLES_LABEL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_TABLES_CHECK_DESC')); + $this->checkSourceTable($checks, 'source_tables'); + + // Check destination extension (version, compatibility) + $checks->addCategory('destination', Text::_('COM_JOOMGALLERY_DESTINATION'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_CHECK_DESC')); + $this->checkDestExtension($checks, 'destination'); // Check existance and writeability of destination directories - $checks->addCategory('dest_directories', Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_DEST_DIRS_LABEL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_DIRECTORY_CHECK_DESC')); + $this->checkDestDir($checks, 'dest_directories'); // Check existence and integrity of destination databasetables - $checks->addCategory('dest_tables', Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_DEST_TABLES_LABEL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_TABLES_CHECK_DESC')); - - - - - - /* $checks = array((object) array( 'name' => 'directories', - 'title' => 'Existence of directories', - 'colTitle' => 'Folder', - 'desc' => 'Are the nessecary directories available and writeable?', - 'checks' => array( (object) array( - 'name' => 'originals', - 'result' => true, - 'title' => 'Original images', - 'description' => '/joomla3/images/joomgallery/originals/', - 'help' => 'Folder (/joomla3/images/joomgallery/originals/) exist and is writeable.' - ), - (object) array( - 'name' => 'thumbs', - 'result' => true, - 'title' => 'Thumbnail images', - 'description' => '/joomla3/images/joomgallery/thumbnails/', - 'help' => 'Folder (/joomla3/images/joomgallery/thumbnails/) is not writeable. make sure the permissions are set correctly for this folder.' - ), - ) - ) - ); */ + $this->checkDestTable($checks, 'dest_tables'); return $checks->getAll(); } @@ -302,18 +272,6 @@ protected function checkLogFile(Checks &$checks, int $category) } - /** - * Precheck: Check the environment (joomla, php, db, ...) to be compatible with this migration script - * - * @param Checks $checks The checks object - * @param int $category The checks-category into which to add the new check - * - * @return void - * - * @since 4.0.0 - */ - abstract protected function checkEnvironment(Checks &$checks, int $category); - /** * Precheck: Check the source extension to be the correct one for this migration script * @@ -324,7 +282,31 @@ abstract protected function checkEnvironment(Checks &$checks, int $category); * * @since 4.0.0 */ - abstract protected function checkSourceExtension(Checks &$checks, int $category); + protected function checkSourceExtension(Checks &$checks, int $category) + { + $src_info = $this->getTargetinfo('source'); + + if(\version_compare(PHP_VERSION, $src_info->php_min, '<')) + { + // PHP version not supported + $checks->addCheck($category, 'src_extension', false, Text::sprintf('COM_JOOMGALLERY_FIELDS_SRC_EXTENSION_LABEL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_PHP_WRONG_VERSION', PHP_VERSION, $src_info->php_min)); + } + elseif($this->component->xml->name !== $src_info->extension) + { + // Wrong destination extension + $checks->addCheck($category, 'src_extension', false, Text::sprintf('COM_JOOMGALLERY_FIELDS_SRC_EXTENSION_LABEL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_NOT_SUPPORTED', $this->component->xml->name)); + } + elseif(\version_compare($this->component->version, $src_info->min, '<') || \version_compare($this->component->version, $src_info->max, '>')) + { + // Version not correct + $checks->addCheck($category, 'src_extension', false, Text::sprintf('COM_JOOMGALLERY_FIELDS_SRC_EXTENSION_LABEL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_WRONG_VERSION', $this->component->version, $src_info->min . ' - ' . $src_info->max)); + } + else + { + // Check successful + $checks->addCheck($category, 'src_extension', true, Text::sprintf('COM_JOOMGALLERY_FIELDS_SRC_EXTENSION_LABEL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_SUCCESS', $this->component->xml->name, $this->component->version)); + } + } /** * Precheck: Check the destination extension to be the correct one for this migration script @@ -336,7 +318,31 @@ abstract protected function checkSourceExtension(Checks &$checks, int $category) * * @since 4.0.0 */ - abstract protected function checkDestExtension(Checks &$checks, int $category); + protected function checkDestExtension(Checks &$checks, int $category) + { + $dest_info = $this->getTargetinfo('destination'); + + if(\version_compare(PHP_VERSION, $dest_info->php_min, '<')) + { + // PHP version not supported + $checks->addCheck($category, 'dest_extension', false, Text::sprintf('COM_JOOMGALLERY_FIELDS_SRC_EXTENSION_LABEL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_PHP_WRONG_VERSION', PHP_VERSION, $dest_info->php_min)); + } + elseif($this->component->xml->name !== $dest_info->extension) + { + // Wrong destination extension + $checks->addCheck($category, 'dest_extension', false, Text::sprintf('COM_JOOMGALLERY_FIELDS_DEST_EXTENSION_LABEL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_NOT_SUPPORTED', $this->component->xml->name)); + } + elseif(\version_compare($this->component->version, $dest_info->min, '<') || \version_compare($this->component->version, $dest_info->max, '>')) + { + // Version not correct + $checks->addCheck($category, 'dest_extension', false, Text::sprintf('COM_JOOMGALLERY_FIELDS_DEST_EXTENSION_LABEL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_WRONG_VERSION', $this->component->version, $dest_info->min . ' - ' . $dest_info->max)); + } + else + { + // Check successful + $checks->addCheck($category, 'dest_extension', true, Text::sprintf('COM_JOOMGALLERY_FIELDS_DEST_EXTENSION_LABEL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_SUCCESS', $this->component->xml->name, $this->component->version)); + } + } /** * Precheck: Check site state and add check to checks array. diff --git a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php index 5f7fe522..aea1f6a9 100644 --- a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php +++ b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php @@ -69,6 +69,17 @@ public function migrate($type, $source, $dest); */ public function getMigrateables(): array; + /** + * Returns an object with compatibility info for this migration script. + * + * @param string $type Select if you get source or destination info + * + * @return Targetinfo Compatibility info object + * + * @since 4.0.0 + */ + public function getTargetinfo(string $type = 'source'): Targetinfo; + /** * Returns an associative array containing the record data from source. * diff --git a/administrator/com_joomgallery/src/Service/Migration/Targetinfo.php b/administrator/com_joomgallery/src/Service/Migration/Targetinfo.php new file mode 100644 index 00000000..852b2957 --- /dev/null +++ b/administrator/com_joomgallery/src/Service/Migration/Targetinfo.php @@ -0,0 +1,87 @@ + ** +** @copyright 2008 - 2023 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 3 or later ** +*****************************************************************************************/ + +namespace Joomgallery\Component\Joomgallery\Administrator\Service\Migration; + +// No direct access +\defined('_JEXEC') or die; + +use \Joomgallery\Component\Joomgallery\Administrator\Extension\ServiceTrait; + +/** + * Targetinfo Class + * Providing compatibility information about source or destination extension + * + * @package JoomGallery + * @since 4.0.0 + */ +class Targetinfo +{ + use ServiceTrait; + + /** + * Does this object provide info about source or destination extension? + * + * @var string + * + * @since 4.0.0 + */ + private $target = 'source'; + + /** + * Extension name + * + * @var string + * + * @since 4.0.0 + */ + private $extension = 'com_joomgallery'; + + /** + * Type of the extension + * + * @var string + * + * @since 4.0.0 + */ + private $type = 'component'; + + /** + * Minimum compatible version + * - Version string must be compatible with \version_compare() + * - If there is no limit, add '-' as version string + * + * @var string + * + * @since 4.0.0 + */ + private $min = '1.0.0'; + + /** + * Maximum compatible version + * - Version string must be compatible with \version_compare() + * - If there is no limit, add '-' as version string + * + * @var string + * + * @since 4.0.0 + */ + private $max = '-'; + + /** + * Minimum compatible PHP version + * - Version string must be compatible with \version_compare() + * + * @var string + * + * @since 4.0.0 + */ + private $php_min = '7.4.0'; +} \ No newline at end of file From 728fc46a1843e979c3e9a2744d699ba18830ed5e Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Sun, 24 Sep 2023 15:54:32 +0200 Subject: [PATCH 011/121] add some pre-migration checks --- .../language/en-GB/com_joomgallery.ini | 20 ++- .../src/Controller/MigrationController.php | 5 +- .../src/Service/Migration/Checks.php | 19 ++- .../src/Service/Migration/Migration.php | 157 ++++++++++++++---- .../Service/Migration/MigrationInterface.php | 18 ++ .../Service/Migration/Scripts/Jg3ToJg4.php | 64 +++++++ .../com_joomgallery/tmpl/migration/step1.php | 2 +- .../com_joomgallery/tmpl/migration/step2.php | 75 ++++----- script.php | 2 +- 9 files changed, 270 insertions(+), 92 deletions(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index eb7f5918..559d6cb3 100755 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -82,6 +82,7 @@ COM_JOOMGALLERY_REPLACE="Replace" COM_JOOMGALLERY_MIGRATION="Migration" COM_JOOMGALLERY_LOGFILE="Log file" COM_JOOMGALLERY_LOGDIRECTORY="Log directory" +COM_JOOMGALLERY_CHECK="Check" COM_JOOMGALLERY_DIRECTORY="Directory" COM_JOOMGALLERY_DIRECTORIES="Directories" COM_JOOMGALLERY_GENERAL="General" @@ -188,6 +189,7 @@ COM_JOOMGALLERY_FILE_AUTHOR_HINT="Author of this file" COM_JOOMGALLERY_USE_SCRIPT="Use this script" COM_JOOMGALLERY_AVAILABLE_SCRIPT="Available migration scripts" COM_JOOMGALLERY_MIGRATION_STEP1_BTN_TXT="Check migration capability" +COM_JOOMGALLERY_MIGRATION_STEP2_BTN_TXT="Start migration manager" ;Messages COM_JOOMGALLERY_NOTE_DEVELOPMENT_VERSION="Attention!
----------------
This version of JoomGallery is still under development. Do not use this version on a live website. It is intended for testing purposes only..." @@ -279,6 +281,7 @@ COM_JOOMGALLERY_SUCCESS_IMAGETYPE="Image-Type (%s) could not be replaced. Error: COM_JOOMGALLERY_SUCCESS_REPLACE_IMAGETYPE="Image-Type (%s) successfully replaced." COM_JOOMGALLERY_ERROR_REPLACE_IMAGETYPE="Image-Type (%s) could not be replaced. Error: %s." COM_JOOMGALLERY_ERROR_TABLE_NOT_EXISTING="Table for content type '%s' does not exist." +COM_JOOMGALLERY_ERROR_PATH_NOT_EXISTING="Path does not exist." ;Mail Templates COM_JOOMGALLERY_MAIL_NEWIMAGE_TITLE="JoomGallery: New Image" @@ -639,17 +642,14 @@ COM_JOOMGALLERY_SERVICE_ERROR_LOAD_CONFIG="Error: Configuration Set not properly COM_JOOMGALLERY_SERVICE_ERROR_UPLOAD_ANIMATED_WEBP="Error: Uploading WEBP files with animation is not possible." COM_JOOMGALLERY_SERVICE_ERROR_FILENOTFOUND="File not found." COM_JOOMGALLERY_SERVICE_ERROR_CREATE_FILE="File could not be created." +COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_NOT_A_DIRECTORY="Requested path is not a directory." COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_NOT_FOUND="No filesystem plugin available for the specified adapter (%s)." COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_ERROR="Filesystem plugin is reporting the following error: %s" -COM_JOOMGALLERY_SERVICE_MIGRATION_LOGFILE_CHECK_DESC="Log file existant and writeable." -COM_JOOMGALLERY_SERVICE_MIGRATION_DIRECTORY_CHECK_DESC="Existance and writeability of directories." -COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_CHECK_DESC="Version and compatibility checks of the extension." -COM_JOOMGALLERY_SERVICE_MIGRATION_STATE_CHECK_LABEL="State of the site" -COM_JOOMGALLERY_SERVICE_MIGRATION_SOURCE_DIRS_LABEL="Source directories" -COM_JOOMGALLERY_SERVICE_MIGRATION_DEST_DIRS_LABEL="Destination directories" -COM_JOOMGALLERY_SERVICE_MIGRATION_TABLES_CHECK_DESC="Existance and integrity of databasetables." -COM_JOOMGALLERY_SERVICE_MIGRATION_SOURCE_TABLES_LABEL="Source databasetables" -COM_JOOMGALLERY_SERVICE_MIGRATION_DEST_TABLES_LABEL="Destination databasetables" +COM_JOOMGALLERY_SERVICE_MIGRATION_PRECHECK_TITLE="Results of the pre migration checks" +COM_JOOMGALLERY_SERVICE_MIGRATION_GENERAL_CHECK_DESC="General checks like log file, site state, ..." +COM_JOOMGALLERY_SERVICE_MIGRATION_SOURCE_CHECK_DESC="Check source extension, directories and tables if they are compatible and existent." +COM_JOOMGALLERY_SERVICE_MIGRATION_DESTINATION_CHECK_DESC="Check destination extension, directories and tables if they are compatible, existent and writeable." +COM_JOOMGALLERY_SERVICE_MIGRATION_DIRECTORY_SUCCESS="Directory exists and is usable." COM_JOOMGALLERY_SERVICE_MIGRATION_LOG_DIR_LABEL="Logging directory" COM_JOOMGALLERY_SERVICE_MIGRATION_LOGFILE_ERROR="Log file '%s' is not writable. Please check permissions." COM_JOOMGALLERY_SERVICE_MIGRATION_LOGFILE_SUCCESS="Log file '%s' is writable." @@ -661,6 +661,8 @@ COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_NOT_SUPPORTED="Extension not support COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_WRONG_VERSION="The current version of the extension is not supported. Current version: %s. Supported version: %s" COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_SUCCESS="Extension is compatible (Extension: %s, Version: %s)" COM_JOOMGALLERY_SERVICE_MIGRATION_PHP_WRONG_VERSION="The current PHP version is not supported. Current version: %s. Minimum requirement: %s" +COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP2="Step 2: Migration pre-check failed. Reason: %s" +COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP2_CHECKS_FAILED="Some of the checks failed." ;Plugin events COM_JOOMGALLERY_PLUGIN_ERROR_RETURN_VALUE="Return value of the plugin event '%s' must be of type %s and contain : %s" diff --git a/administrator/com_joomgallery/src/Controller/MigrationController.php b/administrator/com_joomgallery/src/Controller/MigrationController.php index 337a36b6..bbe11d56 100644 --- a/administrator/com_joomgallery/src/Controller/MigrationController.php +++ b/administrator/com_joomgallery/src/Controller/MigrationController.php @@ -234,7 +234,7 @@ public function precheck() if(!$success) { // Pre-checks not successful. Show error message. - $this->setMessage(Text::sprintf('COM_JOOMGALLERY_ERROR_MIGRATION_STEP2_FAILED', $model->getError()), 'error'); + $this->setMessage(Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP2', $model->getError()), 'error'); } else { @@ -292,7 +292,8 @@ public function migrate() if(!$precheck) { // Pre-checks not successful. Show error message. - $this->setMessage(Text::sprintf('COM_JOOMGALLERY_ERROR_MIGRATION_STEP2_FAILED', 'Previous step not completed.'), 'error'); + $msg = Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP2_CHECKS_FAILED'); + $this->setMessage(Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP2', $msg), 'error'); // Redirect to the step 2 screen $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration&layout=step2', false)); } diff --git a/administrator/com_joomgallery/src/Service/Migration/Checks.php b/administrator/com_joomgallery/src/Service/Migration/Checks.php index bf1d8fc3..87b45347 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Checks.php +++ b/administrator/com_joomgallery/src/Service/Migration/Checks.php @@ -13,6 +13,8 @@ // No direct access \defined('_JEXEC') or die; +use \Joomla\CMS\Language\Text; + /** * Migration Checks Class * Providing a structure for the results of migration checks @@ -63,7 +65,7 @@ class Checks * @since 4.0.0 * @throws \Exception */ - public function addCategory(string $name, string $title = '', string $desc = '') + public function addCategory(string $name, string $title = '', string $desc = '', string $colTitle = '') { // Make category name lowercase $name = \strtolower(\trim($name)); @@ -72,10 +74,11 @@ public function addCategory(string $name, string $title = '', string $desc = '') { // Category not yet existing, create a new one $cat = new \stdClass(); - $cat->name = $name; - $cat->title = $title; - $cat->desc = $desc; - $cat->checks = []; + $cat->name = $name; + $cat->title = $title; + $cat->desc = $desc; + $cat->colTitle = (empty($colTitle)) ? Text::_('COM_JOOMGALLERY_CHECK') : $colTitle; + $cat->checks = []; // Add category to check-objects array $key = $this->array_push($this->objects, $cat); @@ -161,7 +164,7 @@ public function addCheck(string $category, string $name, bool $result, string $t if(!\in_array($asset, \array_keys($this->assets))) { // Get category key - $catKey = $this->assets[$asset]; + $catKey = $this->assets[$category]; // Asset not yet existing, create a new one $check = new \stdClass(); @@ -175,9 +178,9 @@ public function addCheck(string $category, string $name, bool $result, string $t $key = $this->array_push($this->objects[$catKey]->checks, $check); // Add check to assets array - $this->assets[$name] = $catKey.'.'.$key; + $this->assets[$asset] = $catKey.'.'.$key; - // Modify the oversall success if needed + // Modify the overall success if needed if($result === false) { $this->success = false; diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index 7199dd0f..3a3cdd14 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -17,8 +17,10 @@ use \Joomla\CMS\Log\Log; use \Joomla\CMS\Language\Text; use \Joomla\CMS\Filesystem\Path; +use \Joomla\Component\Media\Administrator\Exception\FileNotFoundException; +use \Joomgallery\Component\Joomgallery\Administrator\Helper\JoomHelper; use \Joomgallery\Component\Joomgallery\Administrator\Extension\ServiceTrait; -use Joomgallery\Component\Joomgallery\Administrator\Service\Migration\Checks\Checks; +use \Joomgallery\Component\Joomgallery\Administrator\Service\Migration\Checks; use \Joomgallery\Component\Joomgallery\Administrator\Service\Migration\MigrationInterface; /** @@ -138,30 +140,30 @@ public function precheck(): array // Instantiate a new checks class $checks = new Checks(); - // Check log file - $checks->addCategory('general', Text::_('COM_JOOMGALLERY_GENERAL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_LOGFILE_CHECK_DESC')); + // Check general requirements + $checks->addCategory('general', Text::_('COM_JOOMGALLERY_GENERAL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_GENERAL_CHECK_DESC')); $this->checkLogFile($checks, 'general'); $this->checkSiteState($checks, 'general'); // Check source extension (version, compatibility) - $checks->addCategory('source', Text::_('COM_JOOMGALLERY_SOURCE'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_CHECK_DESC')); + $checks->addCategory('source', Text::_('COM_JOOMGALLERY_SOURCE'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_SOURCE_CHECK_DESC')); $this->checkSourceExtension($checks, 'source'); // Check existance and writeability of source directories - $this->checkSourceDir($checks, 'source_directories'); + $this->checkSourceDir($checks, 'source'); // Check existence and integrity of source databasetables - $this->checkSourceTable($checks, 'source_tables'); + //$this->checkSourceTable($checks, 'source'); // Check destination extension (version, compatibility) - $checks->addCategory('destination', Text::_('COM_JOOMGALLERY_DESTINATION'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_CHECK_DESC')); + $checks->addCategory('destination', Text::_('COM_JOOMGALLERY_DESTINATION'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_DESTINATION_CHECK_DESC')); $this->checkDestExtension($checks, 'destination'); // Check existance and writeability of destination directories - $this->checkDestDir($checks, 'dest_directories'); + $this->checkDestDir($checks, 'destination'); // Check existence and integrity of destination databasetables - $this->checkDestTable($checks, 'dest_tables'); + //$this->checkDestTable($checks, 'destination'); return $checks->getAll(); } @@ -228,13 +230,13 @@ protected function addLog($txt, $priority) * Precheck: Check logfile and add check to checks array. * * @param Checks $checks The checks object - * @param int $category The checks-category into which to add the new check + * @param string $category The checks-category into which to add the new check * * @return void * * @since 4.0.0 */ - protected function checkLogFile(Checks &$checks, int $category) + protected function checkLogFile(Checks &$checks, string $category) { $log_dir = Path::clean($this->app->get('log_path')); @@ -276,35 +278,36 @@ protected function checkLogFile(Checks &$checks, int $category) * Precheck: Check the source extension to be the correct one for this migration script * * @param Checks $checks The checks object - * @param int $category The checks-category into which to add the new check + * @param string $category The checks-category into which to add the new check * * @return void * * @since 4.0.0 */ - protected function checkSourceExtension(Checks &$checks, int $category) + protected function checkSourceExtension(Checks &$checks, string $category) { $src_info = $this->getTargetinfo('source'); + $src_xml = $this->getSourceXML(); - if(\version_compare(PHP_VERSION, $src_info->php_min, '<')) + if(\version_compare(PHP_VERSION, $src_info->get('php_min'), '<')) { // PHP version not supported - $checks->addCheck($category, 'src_extension', false, Text::sprintf('COM_JOOMGALLERY_FIELDS_SRC_EXTENSION_LABEL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_PHP_WRONG_VERSION', PHP_VERSION, $src_info->php_min)); + $checks->addCheck($category, 'src_extension', false, Text::_('COM_JOOMGALLERY_FIELDS_SRC_EXTENSION_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_PHP_WRONG_VERSION', PHP_VERSION, $src_info->get('php_min'))); } - elseif($this->component->xml->name !== $src_info->extension) + elseif(\strval($src_xml->name) !== $src_info->get('extension')) { - // Wrong destination extension - $checks->addCheck($category, 'src_extension', false, Text::sprintf('COM_JOOMGALLERY_FIELDS_SRC_EXTENSION_LABEL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_NOT_SUPPORTED', $this->component->xml->name)); + // Wrong source extension + $checks->addCheck($category, 'src_extension', false, Text::_('COM_JOOMGALLERY_FIELDS_SRC_EXTENSION_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_NOT_SUPPORTED', \strval($src_xml->name))); } - elseif(\version_compare($this->component->version, $src_info->min, '<') || \version_compare($this->component->version, $src_info->max, '>')) + elseif(\version_compare($src_xml->version, $src_info->get('min'), '<') || \version_compare($src_xml->version, $src_info->get('max'), '>')) { // Version not correct - $checks->addCheck($category, 'src_extension', false, Text::sprintf('COM_JOOMGALLERY_FIELDS_SRC_EXTENSION_LABEL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_WRONG_VERSION', $this->component->version, $src_info->min . ' - ' . $src_info->max)); - } + $checks->addCheck($category, 'src_extension', false, Text::_('COM_JOOMGALLERY_FIELDS_SRC_EXTENSION_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_WRONG_VERSION', $src_xml->version, $src_info->get('min') . ' - ' . $src_info->get('max'))); + } else { // Check successful - $checks->addCheck($category, 'src_extension', true, Text::sprintf('COM_JOOMGALLERY_FIELDS_SRC_EXTENSION_LABEL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_SUCCESS', $this->component->xml->name, $this->component->version)); + $checks->addCheck($category, 'src_extension', true, Text::_('COM_JOOMGALLERY_FIELDS_SRC_EXTENSION_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_SUCCESS', \strval($src_xml->name), $src_xml->version)); } } @@ -312,35 +315,35 @@ protected function checkSourceExtension(Checks &$checks, int $category) * Precheck: Check the destination extension to be the correct one for this migration script * * @param Checks $checks The checks object - * @param int $category The checks-category into which to add the new check + * @param string $category The checks-category into which to add the new check * * @return void * * @since 4.0.0 */ - protected function checkDestExtension(Checks &$checks, int $category) + protected function checkDestExtension(Checks &$checks, string $category) { - $dest_info = $this->getTargetinfo('destination'); + $dest_info = $this->getTargetinfo('destination'); - if(\version_compare(PHP_VERSION, $dest_info->php_min, '<')) + if(\version_compare(PHP_VERSION, $dest_info->get('php_min'), '<')) { // PHP version not supported - $checks->addCheck($category, 'dest_extension', false, Text::sprintf('COM_JOOMGALLERY_FIELDS_SRC_EXTENSION_LABEL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_PHP_WRONG_VERSION', PHP_VERSION, $dest_info->php_min)); + $checks->addCheck($category, 'dest_extension', false, Text::_('COM_JOOMGALLERY_FIELDS_SRC_EXTENSION_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_PHP_WRONG_VERSION', PHP_VERSION, $dest_info->get('php_min'))); } - elseif($this->component->xml->name !== $dest_info->extension) + elseif(\strval($this->component->xml->name) !== $dest_info->get('extension')) { // Wrong destination extension - $checks->addCheck($category, 'dest_extension', false, Text::sprintf('COM_JOOMGALLERY_FIELDS_DEST_EXTENSION_LABEL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_NOT_SUPPORTED', $this->component->xml->name)); + $checks->addCheck($category, 'dest_extension', false, Text::_('COM_JOOMGALLERY_FIELDS_DEST_EXTENSION_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_NOT_SUPPORTED', \strval($this->component->xml->name))); } - elseif(\version_compare($this->component->version, $dest_info->min, '<') || \version_compare($this->component->version, $dest_info->max, '>')) + elseif(\version_compare($this->component->version, $dest_info->get('min'), '<') || \version_compare($this->component->version, $dest_info->get('max'), '>')) { // Version not correct - $checks->addCheck($category, 'dest_extension', false, Text::sprintf('COM_JOOMGALLERY_FIELDS_DEST_EXTENSION_LABEL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_WRONG_VERSION', $this->component->version, $dest_info->min . ' - ' . $dest_info->max)); + $checks->addCheck($category, 'dest_extension', false, Text::_('COM_JOOMGALLERY_FIELDS_DEST_EXTENSION_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_WRONG_VERSION', $this->component->version, $dest_info->get('min') . ' - ' . $dest_info->get('max'))); } else { // Check successful - $checks->addCheck($category, 'dest_extension', true, Text::sprintf('COM_JOOMGALLERY_FIELDS_DEST_EXTENSION_LABEL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_SUCCESS', $this->component->xml->name, $this->component->version)); + $checks->addCheck($category, 'dest_extension', true, Text::_('COM_JOOMGALLERY_FIELDS_DEST_EXTENSION_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_SUCCESS', \strval($this->component->xml->name), $this->component->version)); } } @@ -348,13 +351,13 @@ protected function checkDestExtension(Checks &$checks, int $category) * Precheck: Check site state and add check to checks array. * * @param Checks $checks The checks object - * @param int $category The checks-category into which to add the new check + * @param string $category The checks-category into which to add the new check * * @return void * * @since 4.0.0 */ - protected function checkSiteState(Checks &$checks, int $category) + protected function checkSiteState(Checks &$checks, string $category) { if($this->app->get('offline')) { @@ -365,4 +368,90 @@ protected function checkSiteState(Checks &$checks, int $category) $checks->addCheck($category, 'offline', false, Text::_('COM_JOOMGALLERY_SITE_OFFLINE'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_OFFLINE_ERROR')); } } + + /** + * Precheck: Check directories of the source to be existent + * + * @param Checks $checks The checks object + * @param string $category The checks-category into which to add the new check + * + * @return void + * + * @since 4.0.0 + */ + protected function checkSourceDir(Checks &$checks, string $category) + { + // Retrieve a list of source directories involved in migration + $directories = $this->getSourceDirs(); + + foreach($directories as $dir) + { + $check_name = 'src_dir_' . \basename($dir); + + if(!\is_dir($dir)) + { + // Path is not a directory + $checks->addCheck($category, $check_name, false, $dir, Text::_('COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_NOT_A_DIRECTORY')); + } + else + { + $checks->addCheck($category, $check_name, true, $dir, Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_DIRECTORY_SUCCESS')); + } + } + } + + /** + * Precheck: Check directories of the destination to be existent and writeable + * + * @param Checks $checks The checks object + * @param string $category The checks-category into which to add the new check + * + * @return void + * + * @since 4.0.0 + */ + protected function checkDestDir(Checks &$checks, string $category) + { + // Instantiate filesystem service + $this->component->createFilesystem($this->component->getConfig()->get('jg_filesystem','local-images')); + + // Get all imagetypes + $imagetypes = JoomHelper::getRecords('imagetypes', $this->component); + + foreach($imagetypes as $imagetype) + { + $check_name = 'dest_dir_' . $imagetype->typename; + $error = false; + + try + { + $dir_info = $this->component->getFilesystem()->getFile($imagetype->path); + } + catch(FileNotFoundException $msg) + { + // Path doesn't exist + $checks->addCheck($category, $check_name, false, $imagetype->path, Text::_('COM_JOOMGALLERY_ERROR_PATH_NOT_EXISTING')); + $error = true; + } + catch(\Exception $msg) + { + // Error in filesystem + $checks->addCheck($category, $check_name, false, $imagetype->path, Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_ERROR', $msg)); + $error = true; + } + + if(!$error) + { + if($dir_info->type !== 'dir') + { + // Path is not a directory + $checks->addCheck($category, $check_name, false, $imagetype->path, Text::_('COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_NOT_A_DIRECTORY')); + } + else + { + $checks->addCheck($category, $check_name, true, $imagetype->path, Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_DIRECTORY_SUCCESS')); + } + } + } + } } diff --git a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php index aea1f6a9..260c5292 100644 --- a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php +++ b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php @@ -80,6 +80,24 @@ public function getMigrateables(): array; */ public function getTargetinfo(string $type = 'source'): Targetinfo; + /** + * Returns the XML object of the source extension + * + * @return \SimpleXMLElement Extension XML object + * + * @since 4.0.0 + */ + public function getSourceXML(): \SimpleXMLElement; + + /** + * Returns a list of involved source directories. + * + * @return array List of paths + * + * @since 4.0.0 + */ + public function getSourceDirs(): array; + /** * Returns an associative array containing the record data from source. * diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index ce08f69d..e871f008 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -14,7 +14,9 @@ \defined('_JEXEC') or die; use \Joomla\CMS\Language\Text; +use \Joomla\CMS\Filesystem\Path; use \Joomgallery\Component\Joomgallery\Administrator\Service\Migration\Migration; +use \Joomgallery\Component\Joomgallery\Administrator\Service\Migration\Targetinfo; use \Joomgallery\Component\Joomgallery\Administrator\Service\Migration\MigrationInterface; /** @@ -57,6 +59,68 @@ public function __construct() parent::__construct(); } + /** + * Returns an object with compatibility info for this migration script. + * + * @param string $type Select if you get source or destination info + * + * @return Targetinfo Compatibility info object + * + * @since 4.0.0 + */ + public function getTargetinfo(string $type = 'source'): Targetinfo + { + $info = new Targetinfo(); + + $info->set('target', $type); + $info->set('type','component'); + + if($type === 'source') + { + $info->set('extension','JoomGallery'); + $info->set('min', '3.6.0'); + $info->set('max', '3.6.99'); + $info->set('php_min', '5.6.0'); + } + elseif($type === 'destination') + { + $info->set('extension','com_joomgallery'); + $info->set('min', '4.0.0'); + $info->set('max', '5.99.99'); + $info->set('php_min', '7.4.0'); + } + else + { + throw new \Exception('Type must be eighter "source" or "destination", but "'.$type.'" given.', 1); + } + + return $info; + } + + /** + * Returns the XML object of the source extension + * + * @return \SimpleXMLElement Extension XML object + * + * @since 4.0.0 + */ + public function getSourceXML(): \SimpleXMLElement + { + return \simplexml_load_file(Path::clean(JPATH_ADMINISTRATOR . '/components/com_joomgallery/joomgallery_old.xml')); + } + + /** + * Returns a list of involved source directories. + * + * @return array List of paths + * + * @since 4.0.0 + */ + public function getSourceDirs(): array + { + return array(); + } + /** * Returns an associative array containing the record data from source. * diff --git a/administrator/com_joomgallery/tmpl/migration/step1.php b/administrator/com_joomgallery/tmpl/migration/step1.php index 5a4c5a08..a08a5f55 100644 --- a/administrator/com_joomgallery/tmpl/migration/step1.php +++ b/administrator/com_joomgallery/tmpl/migration/step1.php @@ -68,7 +68,7 @@
- + diff --git a/administrator/com_joomgallery/tmpl/migration/step2.php b/administrator/com_joomgallery/tmpl/migration/step2.php index 0fb49244..43f86b94 100644 --- a/administrator/com_joomgallery/tmpl/migration/step2.php +++ b/administrator/com_joomgallery/tmpl/migration/step2.php @@ -56,52 +56,53 @@ name="adminForm" id="migration-form" class="form-validate" aria-label="COM_JOOMGALLERY_MIGRATION_STEP2_TITLE"> error)) : ?> -

Results of the migration check

+

precheck as $cat) : ?>
- title): ?> -
-

title; ?>

- desc): ?> - desc; ?> - +
+ title): ?> +
+

title; ?>

+ desc): ?> + desc; ?> + +
+ +
+ + + + + + + + + + + + + checks as $check) : ?> + + + + + + + + +
title; ?>
colTitle; ?>
+ title; ?>
+ desc; ?> +
result ? 'success' : 'failed'; ?>
- -
- - - - - - - - - - - - - checks as $check) : ?> - - - - - - - - -
title; ?>
colTitle; ?>
- title; ?>
- description; ?> -
result ? 'success' : 'failed'; ?>
+
-
- - + diff --git a/script.php b/script.php index b9df3677..bafa6fa3 100644 --- a/script.php +++ b/script.php @@ -136,7 +136,7 @@ public function preflight($type, $parent) { // save release code information //------------------------------- - if (File::exists(JPATH_ADMINISTRATOR.DIRECTORY_SEPARATOR.'components'.DIRECTORY_SEPARATOR.'com_joomgallery'.DIRECTORY_SEPARATOR.'joomgallery.xml')) + if(File::exists(JPATH_ADMINISTRATOR.DIRECTORY_SEPARATOR.'components'.DIRECTORY_SEPARATOR.'com_joomgallery'.DIRECTORY_SEPARATOR.'joomgallery.xml')) { $xml = simplexml_load_file(JPATH_ADMINISTRATOR.DIRECTORY_SEPARATOR.'components'.DIRECTORY_SEPARATOR.'com_joomgallery'.DIRECTORY_SEPARATOR.'joomgallery.xml'); $this->act_code = $xml->version; From 668711b57e7b6c6a3cb6377a9ebbbcec49d2391c Mon Sep 17 00:00:00 2001 From: Elfangor Date: Sat, 30 Sep 2023 15:45:48 +0200 Subject: [PATCH 012/121] add checksourcetable part1 --- .../com_joomgallery/src/Helper/JoomHelper.php | 2 +- .../src/Service/Migration/Migration.php | 50 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/administrator/com_joomgallery/src/Helper/JoomHelper.php b/administrator/com_joomgallery/src/Helper/JoomHelper.php index dcd905dd..0b2680c1 100644 --- a/administrator/com_joomgallery/src/Helper/JoomHelper.php +++ b/administrator/com_joomgallery/src/Helper/JoomHelper.php @@ -35,7 +35,7 @@ class JoomHelper * * @var array */ - protected static $content_types = array('category' => _JOOM_TABLE_CATEGORIES, + public static $content_types = array( 'category' => _JOOM_TABLE_CATEGORIES, 'comment' => _JOOM_TABLE_COMMENTS, 'config' => _JOOM_TABLE_CONFIGS, 'faulty' => _JOOM_TABLE_FAULTIES, diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index 3a3cdd14..27190b8a 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -454,4 +454,54 @@ protected function checkDestDir(Checks &$checks, string $category) } } } + + /** + * Precheck: Check db and tables of the source + * + * @param Checks $checks The checks object + * @param string $category The checks-category into which to add the new check + * + * @return void + * + * @since 4.0.0 + */ + protected function checkSourceTable(Checks &$checks, string $category) + { + // Create and check db connection + + // Check if required tables exists + + // Check number of records in tables + + // Check whether ROOT category exists + + // Check whether ROOT asset exists + + } + + /** + * Precheck: Check db and tables of the destination + * + * @param Checks $checks The checks object + * @param string $category The checks-category into which to add the new check + * + * @return void + * + * @since 4.0.0 + */ + protected function checkDestTable(Checks &$checks, string $category) + { + // Check if required tables exists + $db = $this->app->getDB; + $tables = JoomHelper::$content_types; + + $db->getTableList(); + + // Check number of records in tables + + // Check whether ROOT category exists + + // Check whether ROOT asset exists + + } } From 44b107cbe1e45420a1ca282c502cc332b81e5b3d Mon Sep 17 00:00:00 2001 From: Elfangor Date: Mon, 9 Oct 2023 13:04:56 +0200 Subject: [PATCH 013/121] Add destination tables precheck --- .../language/en-GB/com_joomgallery.ini | 20 ++- .../src/Model/MigrationModel.php | 2 +- .../src/Service/Migration/Migration.php | 125 +++++++++++++++--- .../Service/Migration/Scripts/Jg3ToJg4.xml | 10 ++ 4 files changed, 137 insertions(+), 20 deletions(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index 559d6cb3..0883e973 100755 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -87,6 +87,11 @@ COM_JOOMGALLERY_DIRECTORY="Directory" COM_JOOMGALLERY_DIRECTORIES="Directories" COM_JOOMGALLERY_GENERAL="General" COM_JOOMGALLERY_SITE_OFFLINE="Site offline" +COM_JOOMGALLERY_ROOT_CATEGORY="Root Category" +COM_JOOMGALLERY_ROOT_ASSET="Root Asset" +COM_JOOMGALLERY_ROOT_CAT_ASSET="Root Category Asset" +COM_JOOMGALLERY_PATH="Path" +COM_JOOMGALLERY_TABLE="Table" ;Control panel COM_JOOMGALLERY_CONTROL_PANEL="Control Panel" @@ -280,7 +285,8 @@ COM_JOOMGALLERY_ERROR_REPLACE_IMAGETYPE="Image-Type (%s) successfully replaced." COM_JOOMGALLERY_SUCCESS_IMAGETYPE="Image-Type (%s) could not be replaced. Error: %s." COM_JOOMGALLERY_SUCCESS_REPLACE_IMAGETYPE="Image-Type (%s) successfully replaced." COM_JOOMGALLERY_ERROR_REPLACE_IMAGETYPE="Image-Type (%s) could not be replaced. Error: %s." -COM_JOOMGALLERY_ERROR_TABLE_NOT_EXISTING="Table for content type '%s' does not exist." +COM_JOOMGALLERY_ERROR_IMGTYPE_TABLE_NOT_EXISTING="Table for content type '%s' does not exist." +COM_JOOMGALLERY_ERROR_TABLE_NOT_EXISTING="Table (%s) does not exist." COM_JOOMGALLERY_ERROR_PATH_NOT_EXISTING="Path does not exist." ;Mail Templates @@ -342,6 +348,8 @@ COM_JOOMGALLERY_FIELDS_SOURCE_IMAGE_LABEL="Source image" COM_JOOMGALLERY_FIELDS_DEST_IMAGE_LABEL="Destination imagetype" COM_JOOMGALLERY_FIELDS_DEST_EXTENSION_LABEL="Destination extension compatibility" COM_JOOMGALLERY_FIELDS_SRC_EXTENSION_LABEL="Source extension compatibility" +COM_JOOMGALLERY_FIELDS_SOURCE_IDS_LABEL="Use source ID's" +COM_JOOMGALLERY_FIELDS_SOURCE_IDS_DESC="If set to yes, the ID's of the source records are used to create the destination records.{tip}In order to use this option, the ID's from source must not exist in the destination datebase table. Tip: Make sure, the destination tables are empty." ;Configuration COM_JOOMGALLERY_CONFIG_INHERITANCE_METHOD_LABEL="Configuration inheritance" @@ -663,6 +671,16 @@ COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_SUCCESS="Extension is compatible (Ex COM_JOOMGALLERY_SERVICE_MIGRATION_PHP_WRONG_VERSION="The current PHP version is not supported. Current version: %s. Minimum requirement: %s" COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP2="Step 2: Migration pre-check failed. Reason: %s" COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP2_CHECKS_FAILED="Some of the checks failed." +COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES="There are already %s records in this table." +COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES_EMPTY="This table is empty." +COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES_USE_IDS_HINT="You have chosen that the ID's from the source are also used in the destination table, but your tabele is not empty. Empty the table or make sure the ID's to be created are still free in the destination table!" +COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_CAT_SUCCESS="Root category exists and is set up correctly." +COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_CAT_ERROR="Root category not existent or not set up correctly. Please make sure Root category is set up correctly." +COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_ASSET_SUCCESS="Root asset exists and is set up correctly." +COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_ASSET_ERROR="Root asset not existent or not set up correctly. Please make sure root asset is set up correctly." +COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_CAT_ASSET_SUCCESS="Root category asset exists and is set up correctly." +COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_CAT_ASSET_ERROR="Root category asset not existent or not set up correctly. Please make sure root category asset is set up correctly." + ;Plugin events COM_JOOMGALLERY_PLUGIN_ERROR_RETURN_VALUE="Return value of the plugin event '%s' must be of type %s and contain : %s" diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index f9b90f19..1ea997d2 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -301,7 +301,7 @@ protected function insertRecord(string $type, array $data, bool $newID=true): bo // Create table if(!$table = $this->getMVCFactory()->createTable($type, 'administrator')) { - $this->setError(Text::sprintf('COM_JOOMGALLERY_ERROR_TABLE_NOT_EXISTING', $type)); + $this->setError(Text::sprintf('COM_JOOMGALLERY_ERROR_IMGTYPE_TABLE_NOT_EXISTING', $type)); return false; } diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index 27190b8a..eaa27ccb 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -17,6 +17,7 @@ use \Joomla\CMS\Log\Log; use \Joomla\CMS\Language\Text; use \Joomla\CMS\Filesystem\Path; +use \Joomla\Database\DatabaseInterface; use \Joomla\Component\Media\Administrator\Exception\FileNotFoundException; use \Joomgallery\Component\Joomgallery\Administrator\Helper\JoomHelper; use \Joomgallery\Component\Joomgallery\Administrator\Extension\ServiceTrait; @@ -152,7 +153,7 @@ public function precheck(): array // Check existance and writeability of source directories $this->checkSourceDir($checks, 'source'); - // Check existence and integrity of source databasetables + // Check existence and integrity of source database tables //$this->checkSourceTable($checks, 'source'); // Check destination extension (version, compatibility) @@ -162,8 +163,8 @@ public function precheck(): array // Check existance and writeability of destination directories $this->checkDestDir($checks, 'destination'); - // Check existence and integrity of destination databasetables - //$this->checkDestTable($checks, 'destination'); + // Check existence and integrity of destination database tables + $this->checkDestTable($checks, 'destination'); return $checks->getAll(); } @@ -323,7 +324,8 @@ protected function checkSourceExtension(Checks &$checks, string $category) */ protected function checkDestExtension(Checks &$checks, string $category) { - $dest_info = $this->getTargetinfo('destination'); + $dest_info = $this->getTargetinfo('destination'); + $version = \str_replace('-dev', '', $this->component->version); if(\version_compare(PHP_VERSION, $dest_info->get('php_min'), '<')) { @@ -335,7 +337,7 @@ protected function checkDestExtension(Checks &$checks, string $category) // Wrong destination extension $checks->addCheck($category, 'dest_extension', false, Text::_('COM_JOOMGALLERY_FIELDS_DEST_EXTENSION_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_NOT_SUPPORTED', \strval($this->component->xml->name))); } - elseif(\version_compare($this->component->version, $dest_info->get('min'), '<') || \version_compare($this->component->version, $dest_info->get('max'), '>')) + elseif(\version_compare($version, $dest_info->get('min'), '<') || \version_compare($version, $dest_info->get('max'), '>')) { // Version not correct $checks->addCheck($category, 'dest_extension', false, Text::_('COM_JOOMGALLERY_FIELDS_DEST_EXTENSION_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_WRONG_VERSION', $this->component->version, $dest_info->get('min') . ' - ' . $dest_info->get('max'))); @@ -391,11 +393,11 @@ protected function checkSourceDir(Checks &$checks, string $category) if(!\is_dir($dir)) { // Path is not a directory - $checks->addCheck($category, $check_name, false, $dir, Text::_('COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_NOT_A_DIRECTORY')); + $checks->addCheck($category, $check_name, false, Text::_('COM_JOOMGALLERY_DIRECTORY') . ': ' . $dir, Text::_('COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_NOT_A_DIRECTORY')); } else { - $checks->addCheck($category, $check_name, true, $dir, Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_DIRECTORY_SUCCESS')); + $checks->addCheck($category, $check_name, true, Text::_('COM_JOOMGALLERY_DIRECTORY') . ': ' . $dir, Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_DIRECTORY_SUCCESS')); } } } @@ -430,13 +432,13 @@ protected function checkDestDir(Checks &$checks, string $category) catch(FileNotFoundException $msg) { // Path doesn't exist - $checks->addCheck($category, $check_name, false, $imagetype->path, Text::_('COM_JOOMGALLERY_ERROR_PATH_NOT_EXISTING')); + $checks->addCheck($category, $check_name, false, Text::_('COM_JOOMGALLERY_DIRECTORY') . ': ' . $imagetype->path, Text::_('COM_JOOMGALLERY_ERROR_PATH_NOT_EXISTING')); $error = true; } catch(\Exception $msg) { // Error in filesystem - $checks->addCheck($category, $check_name, false, $imagetype->path, Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_ERROR', $msg)); + $checks->addCheck($category, $check_name, false, Text::_('COM_JOOMGALLERY_DIRECTORY') . ': ' . $imagetype->path, Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_ERROR', $msg)); $error = true; } @@ -445,11 +447,11 @@ protected function checkDestDir(Checks &$checks, string $category) if($dir_info->type !== 'dir') { // Path is not a directory - $checks->addCheck($category, $check_name, false, $imagetype->path, Text::_('COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_NOT_A_DIRECTORY')); + $checks->addCheck($category, $check_name, false, Text::_('COM_JOOMGALLERY_DIRECTORY') . ': ' . $imagetype->path, Text::_('COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_NOT_A_DIRECTORY')); } else { - $checks->addCheck($category, $check_name, true, $imagetype->path, Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_DIRECTORY_SUCCESS')); + $checks->addCheck($category, $check_name, true, Text::_('COM_JOOMGALLERY_DIRECTORY') . ': ' . $imagetype->path, Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_DIRECTORY_SUCCESS')); } } } @@ -491,17 +493,104 @@ protected function checkSourceTable(Checks &$checks, string $category) */ protected function checkDestTable(Checks &$checks, string $category) { - // Check if required tables exists - $db = $this->app->getDB; - $tables = JoomHelper::$content_types; + // Get table info + $db = Factory::getContainer()->get(DatabaseInterface::class); + $tables = JoomHelper::$content_types; + $tableList = $db->getTableList(); + $dbPrefix = $this->app->get('dbprefix'); + + // Check whether root category exists + $rootCat = false; + $query = $db->getQuery(true) + ->select('COUNT(*)') + ->from($db->quoteName(_JOOM_TABLE_CATEGORIES)) + ->where($db->quoteName('id') . ' = 1') + ->where($db->quoteName('title') . ' = ' . $db->quote('Root')) + ->where($db->quoteName('parent_id') . ' = 0'); + $db->setQuery($query); + + if($db->loadResult()) + { + $checks->addCheck($category, 'dest_root_cat', true, Text::_('COM_JOOMGALLERY_ROOT_CATEGORY'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_CAT_SUCCESS')); + $rootCat = true; + } + else + { + $checks->addCheck($category, 'dest_root_cat', false, Text::_('COM_JOOMGALLERY_ROOT_CATEGORY'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_CAT_ERROR')); + } - $db->getTableList(); + // Check whether root asset exists + $query = $db->getQuery(true) + ->select('id') + ->from($db->quoteName('#__assets')) + ->where($db->quoteName('name') . ' = ' . $db->quote(_JOOM_OPTION)) + ->where($db->quoteName('parent_id') . ' = 1'); + $db->setQuery($query); - // Check number of records in tables + if($rootAssetID = $db->loadResult()) + { + $checks->addCheck($category, 'dest_root_asset', true, Text::_('COM_JOOMGALLERY_ROOT_ASSET'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_ASSET_SUCCESS')); + } + else + { + $checks->addCheck($category, 'dest_root_asset', false, Text::_('COM_JOOMGALLERY_ROOT_ASSET'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_ASSET_ERROR')); + } - // Check whether ROOT category exists + // Check whether root category asset exists + $query = $db->getQuery(true) + ->select('id') + ->from($db->quoteName('#__assets')) + ->where($db->quoteName('name') . ' = ' . $db->quote('com_joomgallery.category.1')) + ->where($db->quoteName('parent_id') . ' = ' . $db->quote($rootAssetID)); + $db->setQuery($query); - // Check whether ROOT asset exists + if($db->loadResult()) + { + $checks->addCheck($category, 'dest_root_cat_asset', true, Text::_('COM_JOOMGALLERY_ROOT_CAT_ASSET'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_CAT_ASSET_SUCCESS')); + } + else + { + $checks->addCheck($category, 'dest_root_cat_asset', false, Text::_('COM_JOOMGALLERY_ROOT_CAT_ASSET'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_CAT_ASSET_ERROR')); + } + + // Check required tables + foreach($tables as $tablename) + { + $check_name = 'dest_table_' . $tablename; + + // Check if required tables exists + if(!\in_array( \str_replace('#__', $dbPrefix, $tablename), $tableList)) + { + $checks->addCheck($category, $check_name, false, Text::_('COM_JOOMGALLERY_TABLE') . ': ' . $tablename, Text::_('COM_JOOMGALLERY_ERROR_TABLE_NOT_EXISTING')); + continue; + } + // Check number of records in tables + $query = $db->getQuery(true) + ->select('COUNT(*)') + ->from($tablename); + $db->setQuery($query); + + $count = $db->loadResult(); + + if($tablename == _JOOM_TABLE_CATEGORIES && $rootCat) + { + $count = $count - 1; + } + + $check_name = 'dest_table_' . $tablename . '_count'; + if($count == 0) + { + $checks->addCheck($category, $check_name, true, Text::_('COM_JOOMGALLERY_TABLE') . ': ' . $tablename, Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES_EMPTY')); + } + elseif($this->params->source_ids && $count > 0) + { + $checks->addCheck($category, $check_name, true, Text::_('COM_JOOMGALLERY_TABLE') . ': ' . $tablename, Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES', $count) . '
' . Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES_USE_IDS_HINT')); + } + else + { + $checks->addCheck($category, $check_name, true, Text::_('COM_JOOMGALLERY_TABLE') . ': ' . $tablename, Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES', $count)); + } + } } } diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.xml b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.xml index f0dc1445..eb041a03 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.xml +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.xml @@ -103,6 +103,16 @@ + + + + Date: Mon, 9 Oct 2023 16:40:03 +0200 Subject: [PATCH 014/121] finish prechecks --- .../language/en-GB/com_joomgallery.ini | 4 +- .../src/Controller/MigrationController.php | 7 +- .../src/Model/MigrationModel.php | 14 ++-- .../src/Service/Migration/Checks.php | 17 ++++- .../src/Service/Migration/Migration.php | 69 ++++++++++++++++--- .../Service/Migration/MigrationInterface.php | 18 +++++ .../Service/Migration/Scripts/Jg3ToJg4.php | 67 +++++++++++++++++- .../Service/Migration/Scripts/Jg3ToJg4.xml | 12 ++-- .../src/View/Migration/HtmlView.php | 2 +- 9 files changed, 182 insertions(+), 28 deletions(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index 0883e973..1bca5d53 100755 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -670,6 +670,7 @@ COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_WRONG_VERSION="The current version o COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_SUCCESS="Extension is compatible (Extension: %s, Version: %s)" COM_JOOMGALLERY_SERVICE_MIGRATION_PHP_WRONG_VERSION="The current PHP version is not supported. Current version: %s. Minimum requirement: %s" COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP2="Step 2: Migration pre-check failed. Reason: %s" +COM_JOOMGALLERY_SERVICE_MIGRATION_SUCCESS_MIGRATION_STEP2="Step 2: Migration pre-check successful." COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP2_CHECKS_FAILED="Some of the checks failed." COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES="There are already %s records in this table." COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES_EMPTY="This table is empty." @@ -680,7 +681,8 @@ COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_ASSET_SUCCESS="Root asset exists and is s COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_ASSET_ERROR="Root asset not existent or not set up correctly. Please make sure root asset is set up correctly." COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_CAT_ASSET_SUCCESS="Root category asset exists and is set up correctly." COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_CAT_ASSET_ERROR="Root category asset not existent or not set up correctly. Please make sure root category asset is set up correctly." - +COM_JOOMGALLERY_SERVICE_MIGRATION_TABLE_CONN_ERROR="Connection to database not possible." +COM_JOOMGALLERY_SERVICE_MIGRATION_STEP_NOT_AVAILABLE="This step is not available. Please fulfill the previous step first." ;Plugin events COM_JOOMGALLERY_PLUGIN_ERROR_RETURN_VALUE="Return value of the plugin event '%s' must be of type %s and contain : %s" diff --git a/administrator/com_joomgallery/src/Controller/MigrationController.php b/administrator/com_joomgallery/src/Controller/MigrationController.php index bbe11d56..2595f50a 100644 --- a/administrator/com_joomgallery/src/Controller/MigrationController.php +++ b/administrator/com_joomgallery/src/Controller/MigrationController.php @@ -17,6 +17,7 @@ use \Joomla\Input\Input; use \Joomla\CMS\Language\Text; use \Joomla\CMS\Router\Route; +use \Joomla\Registry\Registry; use \Joomla\CMS\MVC\Controller\BaseController; use \Joomla\CMS\Application\CMSApplication; use \Joomla\CMS\Form\FormFactoryAwareInterface; @@ -230,16 +231,16 @@ public function precheck() $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.params', $validData); // Perform the pre migration checks - list($success, $res) = $model->precheck($validData); + list($success, $res, $msg) = $model->precheck($validData); if(!$success) { // Pre-checks not successful. Show error message. - $this->setMessage(Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP2', $model->getError()), 'error'); + $this->setMessage(Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP2', $msg), 'error'); } else { // Pre-checks successful. Show success message. - $this->setMessage(Text::_('COM_JOOMGALLERY_ERROR_MIGRATION_STEP2_SUCCESSFUL')); + $this->setMessage(Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_SUCCESS_MIGRATION_STEP2')); } // Save the results of the pre migration checks in the session. diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index 1ea997d2..65e42af4 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -16,6 +16,7 @@ use \Joomla\CMS\Factory; use \Joomla\CMS\Uri\Uri; use \Joomla\CMS\Form\Form; +use \Joomla\Registry\Registry; use \Joomla\CMS\Filesystem\Folder; use \Joomla\CMS\MVC\Model\FormModel; use \Joomla\CMS\Language\Multilanguage; @@ -191,7 +192,7 @@ protected function loadFormData() $data = $this->app->getUserState($name.'.step2.data', array()); // Check the session for validated migration parameters - $params = $this->app->getUserState($name.'.params', array()); + $params = $this->app->getUserState($name.'.params', null); return (empty($params)) ? $data : $params; } @@ -210,7 +211,8 @@ public function precheck($params) $info = $this->getScript(); // Set the migration parameters - $this->component->getMigration()->set('params', (object) $params); + $params = new Registry($params); + $this->component->getMigration()->set('params', $params); // Perform the prechecks return $this->component->getMigration()->precheck(); @@ -230,7 +232,8 @@ public function postcheck($params) $info = $this->getScript(); // Set the migration parameters - $this->component->getMigration()->set('params', (object) $params); + $params = new Registry($params); + $this->component->getMigration()->set('params', $params); // Perform the prechecks return $this->component->getMigration()->postcheck(); @@ -252,7 +255,8 @@ public function migrate($type, $pk, $params) $info = $this->getScript(); // Set the migration parameters - $this->component->getMigration()->set('params', (object) $params); + $params = new Registry($params); + $this->component->getMigration()->set('params', $params); // Get record data from source $data = $this->component->getMigration()->getData($type, $pk); @@ -261,7 +265,7 @@ public function migrate($type, $pk, $params) $data = $this->component->getMigration()->applyDataMapping($data); // Create new record based on data - $record = $this->insertRecord($type, $data, $params['same_ids']); + $record = $this->insertRecord($type, $data, $params->get('same_ids')); // Create imagetypes if($type === 'image') diff --git a/administrator/com_joomgallery/src/Service/Migration/Checks.php b/administrator/com_joomgallery/src/Service/Migration/Checks.php index 87b45347..2a3a054d 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Checks.php +++ b/administrator/com_joomgallery/src/Service/Migration/Checks.php @@ -53,6 +53,16 @@ class Checks */ private $success = true; + /** + * The overall error message + * This message is displayed on top of the results display + * + * @var string + * + * @since 4.0.0 + */ + private $message = ''; + /** * Register a new category or modify an existing one * @@ -184,6 +194,11 @@ public function addCheck(string $category, string $name, bool $result, string $t if($result === false) { $this->success = false; + + if($this->message === '') + { + $this->message = $title . ' (' . $desc . ')'; + } } } else @@ -297,7 +312,7 @@ public function getSuccess(): bool */ public function getAll(): array { - return array($this->success, $this->objects); + return array($this->success, $this->objects, $this->message); } /** diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index eaa27ccb..e9f195eb 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -13,11 +13,14 @@ // No direct access \defined('_JEXEC') or die; +use Exception; use \Joomla\CMS\Factory; use \Joomla\CMS\Log\Log; use \Joomla\CMS\Language\Text; +use \Joomla\Registry\Registry; use \Joomla\CMS\Filesystem\Path; use \Joomla\Database\DatabaseInterface; +use \Joomla\Database\DatabaseFactory; use \Joomla\Component\Media\Administrator\Exception\FileNotFoundException; use \Joomgallery\Component\Joomgallery\Administrator\Helper\JoomHelper; use \Joomgallery\Component\Joomgallery\Administrator\Extension\ServiceTrait; @@ -37,7 +40,7 @@ abstract class Migration implements MigrationInterface /** * Storage for the migration form object. * - * @var \stdClass + * @var Registry * * @since 4.0.0 */ @@ -154,7 +157,7 @@ public function precheck(): array $this->checkSourceDir($checks, 'source'); // Check existence and integrity of source database tables - //$this->checkSourceTable($checks, 'source'); + $this->checkSourceTable($checks, 'source'); // Check destination extension (version, compatibility) $checks->addCategory('destination', Text::_('COM_JOOMGALLERY_DESTINATION'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_DESTINATION_CHECK_DESC')); @@ -385,12 +388,13 @@ protected function checkSourceDir(Checks &$checks, string $category) { // Retrieve a list of source directories involved in migration $directories = $this->getSourceDirs(); + $root = $this->getSourceRootPath(); foreach($directories as $dir) { $check_name = 'src_dir_' . \basename($dir); - if(!\is_dir($dir)) + if(!\is_dir($root . $dir)) { // Path is not a directory $checks->addCheck($category, $check_name, false, Text::_('COM_JOOMGALLERY_DIRECTORY') . ': ' . $dir, Text::_('COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_NOT_A_DIRECTORY')); @@ -469,16 +473,61 @@ protected function checkDestDir(Checks &$checks, string $category) */ protected function checkSourceTable(Checks &$checks, string $category) { - // Create and check db connection + // Get table info + if($this->params->get('same_db')) + { + $db = Factory::getContainer()->get(DatabaseInterface::class); + $dbPrefix = $this->app->get('dbprefix'); + } + else + { + $options = array ('driver' => $this->params->get('dbtype'), 'host' => $this->params->get('dbhost'), 'user' => $this->params->get('dbuser'), 'password' => $this->params->get('dbpass'), 'database' => $this->params->get('dbname'), 'prefix' => $this->params->get('dbprefix')); + $dbFactory = new DatabaseFactory(); + $db = $dbFactory->getDriver($this->params->get('dbtype'), $options); + $dbPrefix = $this->params->get('dbprefix'); + } - // Check if required tables exists + // Check connection to database + try + { + $tableList = $db->getTableList(); + } + catch (\Exception $msg) + { + $checks->addCheck($category, 'src_table_' . $tablename . '_connect', true, Text::_('JLIB_FORM_VALUE_SESSION_DATABASE'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_TABLE_CONN_ERROR')); + } - // Check number of records in tables + // Check required tables + $tables = $this->getSourceTables(); + foreach($tables as $tablename) + { + $check_name = 'src_table_' . $tablename; - // Check whether ROOT category exists + // Check if required tables exists + if(!\in_array(\str_replace('#__', $dbPrefix, $tablename), $tableList)) + { + $checks->addCheck($category, $check_name, false, Text::_('COM_JOOMGALLERY_TABLE') . ': ' . $tablename, Text::_('COM_JOOMGALLERY_ERROR_TABLE_NOT_EXISTING')); + continue; + } - // Check whether ROOT asset exists + $query = $db->getQuery(true) + ->select('COUNT(*)') + ->from($tablename); + $db->setQuery($query); + + $count = $db->loadResult(); + // Check number of records in tables + $check_name = 'dest_table_' . $tablename . '_count'; + if($count == 0) + { + $checks->addCheck($category, $check_name, true, Text::_('COM_JOOMGALLERY_TABLE') . ': ' . $tablename, Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES_EMPTY')); + } + else + { + $checks->addCheck($category, $check_name, true, Text::_('COM_JOOMGALLERY_TABLE') . ': ' . $tablename, Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES', $count)); + } + } } /** @@ -557,7 +606,7 @@ protected function checkDestTable(Checks &$checks, string $category) foreach($tables as $tablename) { $check_name = 'dest_table_' . $tablename; - + // Check if required tables exists if(!\in_array( \str_replace('#__', $dbPrefix, $tablename), $tableList)) { @@ -583,7 +632,7 @@ protected function checkDestTable(Checks &$checks, string $category) { $checks->addCheck($category, $check_name, true, Text::_('COM_JOOMGALLERY_TABLE') . ': ' . $tablename, Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES_EMPTY')); } - elseif($this->params->source_ids && $count > 0) + elseif($this->params->get('source_ids') && $count > 0) { $checks->addCheck($category, $check_name, true, Text::_('COM_JOOMGALLERY_TABLE') . ': ' . $tablename, Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES', $count) . '
' . Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES_USE_IDS_HINT')); } diff --git a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php index 260c5292..80525e72 100644 --- a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php +++ b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php @@ -98,6 +98,24 @@ public function getSourceXML(): \SimpleXMLElement; */ public function getSourceDirs(): array; + /** + * Returns the Joomla root path of the source. + * + * @return string Source Joomla root path + * + * @since 4.0.0 + */ + public function getSourceRootPath(): string; + + /** + * Returns a list of involved source tables. + * + * @return array List of table names (Joomla style, e.g #__joomgallery) + * + * @since 4.0.0 + */ + public function getSourceTables(): array; + /** * Returns an associative array containing the record data from source. * diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index e871f008..6484b081 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -118,7 +118,72 @@ public function getSourceXML(): \SimpleXMLElement */ public function getSourceDirs(): array { - return array(); + $dirs = array( $this->params->get('orig_path'), + $this->params->get('detail_path'), + $this->params->get('thumb_path') + ); + + return $dirs; + } + + /** + * Returns the Joomla root path of the source. + * + * @return string Source Joomla root path + * + * @since 4.0.0 + */ + public function getSourceRootPath(): string + { + if($this->params->get('same_joomla', 1)) + { + $root = Path::clean(JPATH_ROOT . '/'); + } + else + { + $root = Path::clean($this->params->get('joomla_path')); + + if(\substr($root, -1) != '/') + { + $root = Path::clean($root . '/'); + } + } + + return $root; + } + + /** + * Returns a list of involved source tables. + * + * @return array List of table names (Joomla style, e.g #__joomgallery) + * + * @since 4.0.0 + */ + public function getSourceTables(): array + { + $tables = array( '#__joomgallery', + '#__joomgallery_image_details', + '#__joomgallery_catg', + '#__joomgallery_category_details', + '#__joomgallery_comments', + '#__joomgallery_config', + '#__joomgallery_countstop', + '#__joomgallery_maintenance', + '#__joomgallery_nameshields', + '#__joomgallery_orphans', + '#__joomgallery_users', + '#__joomgallery_votes' + ); + + if($this->params->get('same_db')) + { + foreach($tables as $key => $table) + { + $tables[$key] = $table . '_old'; + } + } + + return $tables; } /** diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.xml b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.xml index eb041a03..0e6c0881 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.xml +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.xml @@ -52,7 +52,7 @@
- - - - - - params) && $this->layout != 'step1') { // Requested script does not exists - \array_push($this->error, 'COM_JOOMGALLERY_MIGRATION_PARAMS_NOT_EXIST'); + \array_push($this->error, 'COM_JOOMGALLERY_SERVICE_MIGRATION_STEP_NOT_AVAILABLE'); } } From 8762741c061dcd2f6764f029c6571dfd8ad43127 Mon Sep 17 00:00:00 2001 From: Elfangor Date: Tue, 10 Oct 2023 20:08:10 +0200 Subject: [PATCH 015/121] add migrateables --- .../language/en-GB/com_joomgallery.ini | 1 + .../com_joomgallery.migration.Jg3ToJg4.ini | 4 +- .../src/Service/Migration/Migrateables.php | 128 ++++++++++++++++++ .../src/Service/Migration/Migration.php | 84 +++++++++--- .../Service/Migration/MigrationInterface.php | 12 ++ .../Service/Migration/Scripts/Jg3ToJg4.php | 28 +++- .../com_joomgallery/tmpl/migration/step3.php | 2 +- 7 files changed, 235 insertions(+), 24 deletions(-) create mode 100644 administrator/com_joomgallery/src/Service/Migration/Migrateables.php diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index 1bca5d53..445b2900 100755 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -195,6 +195,7 @@ COM_JOOMGALLERY_USE_SCRIPT="Use this script" COM_JOOMGALLERY_AVAILABLE_SCRIPT="Available migration scripts" COM_JOOMGALLERY_MIGRATION_STEP1_BTN_TXT="Check migration capability" COM_JOOMGALLERY_MIGRATION_STEP2_BTN_TXT="Start migration manager" +COM_JOOMGALLERY_MIGRATION_STEP3_BTN_TXT="Check migration success" ;Messages COM_JOOMGALLERY_NOTE_DEVELOPMENT_VERSION="Attention!
----------------
This version of JoomGallery is still under development. Do not use this version on a live website. It is intended for testing purposes only..." diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini index 91438d23..d378bdf6 100644 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini @@ -11,4 +11,6 @@ FILES_JOOMGALLERY_MIGRATION_JG3TOJG4_TITLE="JoomGallery 3.x to JoomGallery 4.x" FILES_JOOMGALLERY_MIGRATION_JG3TOJG4_DESC="Migration of content types (images, categories, tags) from JoomGallery version 3.x to JoomGallery version 4.x." FILES_JOOMGALLERY_MIGRATION_JG3TOJG4_ORIGPATH_DESC="Path to original image" FILES_JOOMGALLERY_MIGRATION_JG3TOJG4_DETAILPATH_DESC="Path to detail image" -FILES_JOOMGALLERY_MIGRATION_JG3TOJG4_THUMBPATH_DESC="Path to thumbnail image" \ No newline at end of file +FILES_JOOMGALLERY_MIGRATION_JG3TOJG4_THUMBPATH_DESC="Path to thumbnail image" +FILES_JOOMGALLERY_MIGRATION_IMAGE_TITLE="Migration: Images" +FILES_JOOMGALLERY_MIGRATION_CATEGORY_TITLE="Migration: Categories" \ No newline at end of file diff --git a/administrator/com_joomgallery/src/Service/Migration/Migrateables.php b/administrator/com_joomgallery/src/Service/Migration/Migrateables.php new file mode 100644 index 00000000..f1afb7da --- /dev/null +++ b/administrator/com_joomgallery/src/Service/Migration/Migrateables.php @@ -0,0 +1,128 @@ + ** +** @copyright 2008 - 2023 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 3 or later ** +*****************************************************************************************/ + +namespace Joomgallery\Component\Joomgallery\Administrator\Service\Migration; + +// No direct access +\defined('_JEXEC') or die; + +use \Joomgallery\Component\Joomgallery\Administrator\Extension\ServiceTrait; + +/** + * Migrateable Class + * Providing information about a record type beeing migrated + * + * @package JoomGallery + * @since 4.0.0 + */ +class Migrateables +{ + use ServiceTrait; + + /** + * Content type + * + * @var string + * + * @since 4.0.0 + */ + protected $type = ''; + + /** + * Table name + * + * @var string + * + * @since 4.0.0 + */ + protected $table = ''; + + /** + * Primary key + * + * @var string + * + * @since 4.0.0 + */ + protected $pk = ''; + + /** + * Migration progress (0-100) + * + * @var int + * + * @since 4.0.0 + */ + protected $progress = 0; + + /** + * List of source record ID's to be migrated + * + * @var array + * + * @since 4.0.0 + */ + public $queue = array(); + + /** + * List of source record ID's successfully migrated + * + * @var array + * + * @since 4.0.0 + */ + protected $success = array(); + + /** + * List of source record ID's with an error during migration + * + * @var array + * + * @since 4.0.0 + */ + protected $error = array(); + + /** + * Constructor + * + * @param string $type The content type + * @param string $pk Primary key name + * @param string $table Table name + * + * @return void + * + * @since 4.0.0 + */ + public function __construct(string $type, string $pk, string $table) + { + $this->type = $type; + $this->pk = $pk; + $this->table = $table; + } + + /** + * Load the initial queue of ids from source db + * + * @param object $db Database object + * + * @return void + * + * @since 4.0.0 + */ + public function loadQueue($db) + { + $query = $db->getQuery(true) + ->select($this->pk) + ->from($this->table); + $db->setQuery($query); + + $this->queue = $db->loadColumn(); + } +} \ No newline at end of file diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index e9f195eb..f5f78185 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -75,12 +75,22 @@ abstract class Migration implements MigrationInterface /** * List of content types which can be migrated with this script + * Use the singular form of the content type (e.g image, not images) * * @var array * * @since 4.0.0 */ - protected $contentTypes = array(); + protected $types = array(); + + /** + * List of migrateables processed/migrated with this script + * + * @var Migrateables[] + * + * @since 4.0.0 + */ + protected $migrateables = array(); /** * State if logger is created @@ -128,7 +138,21 @@ public function __construct() */ public function getMigrateables(): array { - return $this->contentTypes; + if(empty($this->migrateables)) + { + // fetch a list of migrateables from source + foreach($this->types as $key => $type) + { + list($name, $pk) = $this->getSourceTableInfo($type); + $migraeable = new Migrateables($type, $pk, $name); + + $migraeable->loadQueue($this->getDB('source')); + + \array_push($this->migrateables, $migraeable); + } + } + + return $this->migrateables; } /** @@ -230,6 +254,39 @@ protected function addLog($txt, $priority) Log::add($txt, $priority, 'com_joomgallery.migration'); } + /** + * Get a database object + * + * @param string $target The target (source or destination) + * + * @return array list($db, $dbPrefix) + * + * @since 4.0.0 + * @throws \Exception + */ + protected function getDB(string $target): array + { + if(!in_array($target, array('source', 'destination'))) + { + throw new \Exception('Taget has to be eighter "source" or "destination". Given: ' . $target, 1); + } + + if($target === 'destination' || $this->params->get('same_db')) + { + $db = Factory::getContainer()->get(DatabaseInterface::class); + $dbPrefix = $this->app->get('dbprefix'); + } + else + { + $options = array ('driver' => $this->params->get('dbtype'), 'host' => $this->params->get('dbhost'), 'user' => $this->params->get('dbuser'), 'password' => $this->params->get('dbpass'), 'database' => $this->params->get('dbname'), 'prefix' => $this->params->get('dbprefix')); + $dbFactory = new DatabaseFactory(); + $db = $dbFactory->getDriver($this->params->get('dbtype'), $options); + $dbPrefix = $this->params->get('dbprefix'); + } + + return array($db, $dbPrefix); + } + /** * Precheck: Check logfile and add check to checks array. * @@ -473,19 +530,7 @@ protected function checkDestDir(Checks &$checks, string $category) */ protected function checkSourceTable(Checks &$checks, string $category) { - // Get table info - if($this->params->get('same_db')) - { - $db = Factory::getContainer()->get(DatabaseInterface::class); - $dbPrefix = $this->app->get('dbprefix'); - } - else - { - $options = array ('driver' => $this->params->get('dbtype'), 'host' => $this->params->get('dbhost'), 'user' => $this->params->get('dbuser'), 'password' => $this->params->get('dbpass'), 'database' => $this->params->get('dbname'), 'prefix' => $this->params->get('dbprefix')); - $dbFactory = new DatabaseFactory(); - $db = $dbFactory->getDriver($this->params->get('dbtype'), $options); - $dbPrefix = $this->params->get('dbprefix'); - } + list($db, $dbPrefix) = $this->getDB('source'); // Check connection to database try @@ -494,7 +539,7 @@ protected function checkSourceTable(Checks &$checks, string $category) } catch (\Exception $msg) { - $checks->addCheck($category, 'src_table_' . $tablename . '_connect', true, Text::_('JLIB_FORM_VALUE_SESSION_DATABASE'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_TABLE_CONN_ERROR')); + $checks->addCheck($category, 'src_table_connect', true, Text::_('JLIB_FORM_VALUE_SESSION_DATABASE'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_TABLE_CONN_ERROR')); } // Check required tables @@ -543,10 +588,9 @@ protected function checkSourceTable(Checks &$checks, string $category) protected function checkDestTable(Checks &$checks, string $category) { // Get table info - $db = Factory::getContainer()->get(DatabaseInterface::class); - $tables = JoomHelper::$content_types; - $tableList = $db->getTableList(); - $dbPrefix = $this->app->get('dbprefix'); + list($db, $dbPrefix) = $this->getDB('destination'); + $tables = JoomHelper::$content_types; + $tableList = $db->getTableList(); // Check whether root category exists $rootCat = false; diff --git a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php index 80525e72..d79a2ad8 100644 --- a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php +++ b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php @@ -107,6 +107,18 @@ public function getSourceDirs(): array; */ public function getSourceRootPath(): string; + /** + * Returns the most important info of the corresponding source table + * + * @param string $type The content type name + * + * @return array The corresponding source table info + * list(tablename, primarykey) + * + * @since 4.0.0 + */ + public function getSourceTableInfo(string $type): array; + /** * Returns a list of involved source tables. * diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index 6484b081..91e33241 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -45,7 +45,7 @@ class Jg3ToJg4 extends Migration implements MigrationInterface * * @since 4.0.0 */ - protected $contentTypes = array('image', 'category'); + protected $types = array('category', 'image'); /** * Constructor @@ -153,7 +153,31 @@ public function getSourceRootPath(): string } /** - * Returns a list of involved source tables. + * Returns the most important info of the corresponding source table + * + * @param string $type The content type name + * + * @return array The corresponding source table info + * list(tablename, primarykey) + * + * @since 4.0.0 + */ + public function getSourceTableInfo(string $type): array + { + $tables = array( 'image' => array('#__joomgallery', 'id'), + 'category' => array('#__joomgallery_catg', 'cid') + ); + + if(!\in_array($type, \array_keys($tables))) + { + throw new \Exception('There is no table associated with the given content type. Given: ' . $type, 1); + } + + return $tables[$type]; + } + + /** + * Returns a list of all available source tables. * * @return array List of table names (Joomla style, e.g #__joomgallery) * diff --git a/administrator/com_joomgallery/tmpl/migration/step3.php b/administrator/com_joomgallery/tmpl/migration/step3.php index fa3740b8..455113dc 100644 --- a/administrator/com_joomgallery/tmpl/migration/step3.php +++ b/administrator/com_joomgallery/tmpl/migration/step3.php @@ -85,7 +85,7 @@
- + From baaf974c458dc258180718ba27b9bef6333dffa8 Mon Sep 17 00:00:00 2001 From: Elfangor Date: Tue, 10 Oct 2023 22:29:59 +0200 Subject: [PATCH 016/121] add migrateables --- .../src/Model/MigrationModel.php | 41 ++++++++++++++++--- .../src/Service/Migration/Migration.php | 7 ++-- .../Service/Migration/Scripts/Jg3ToJg4.php | 8 ++++ .../com_joomgallery/tmpl/migration/step3.php | 8 ++-- 4 files changed, 51 insertions(+), 13 deletions(-) diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index 65e42af4..7c7e1ffb 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -65,6 +65,36 @@ public function __construct($config = array()) $this->component->createConfig(); } + /** + * Method to set the migration parameters in the migration script. + * + * @param array $params The migration parameters entered in the migration form + * + * @return void + * + * @since 4.0.0 + * @throws \Exception Missing migration params + */ + protected function setParams($params = null) + { + $info = $this->getScript(); + + if(\is_null($params)) + { + // Check the session for validated migration parameters + $params = $this->app->getUserState(_JOOM_OPTION.'.migration.'.$info->name.'.params', null); + } + + if(\is_null($params)) + { + throw new \Exception('No migration params found. Please provide some migration params.', 1); + } + + // Set the migration parameters + $params = new Registry($params); + $this->component->getMigration()->set('params', $params); + } + /** * Method to get info array of current migration script. * @@ -133,6 +163,8 @@ public function getMigrateables() return false; } + $this->setParams(); + return $this->component->getMigration()->getMigrateables(); } @@ -211,8 +243,7 @@ public function precheck($params) $info = $this->getScript(); // Set the migration parameters - $params = new Registry($params); - $this->component->getMigration()->set('params', $params); + $this->setParams($params); // Perform the prechecks return $this->component->getMigration()->precheck(); @@ -232,8 +263,7 @@ public function postcheck($params) $info = $this->getScript(); // Set the migration parameters - $params = new Registry($params); - $this->component->getMigration()->set('params', $params); + $this->setParams($params); // Perform the prechecks return $this->component->getMigration()->postcheck(); @@ -255,8 +285,7 @@ public function migrate($type, $pk, $params) $info = $this->getScript(); // Set the migration parameters - $params = new Registry($params); - $this->component->getMigration()->set('params', $params); + $this->setParams($params); // Get record data from source $data = $this->component->getMigration()->getData($type, $pk); diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index f5f78185..f655339b 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -143,10 +143,11 @@ public function getMigrateables(): array // fetch a list of migrateables from source foreach($this->types as $key => $type) { - list($name, $pk) = $this->getSourceTableInfo($type); - $migraeable = new Migrateables($type, $pk, $name); + list($name, $pk) = $this->getSourceTableInfo($type); + list($db, $dbPrefix) = $this->getDB('source'); - $migraeable->loadQueue($this->getDB('source')); + $migraeable = new Migrateables($type, $pk, $name); + $migraeable->loadQueue($db); \array_push($this->migrateables, $migraeable); } diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index 91e33241..1f5acfd1 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -173,6 +173,14 @@ public function getSourceTableInfo(string $type): array throw new \Exception('There is no table associated with the given content type. Given: ' . $type, 1); } + if($this->params->get('same_db')) + { + foreach($tables as $key => $value) + { + $tables[$key][0] = $value[0] . '_old'; + } + } + return $tables[$type]; } diff --git a/administrator/com_joomgallery/tmpl/migration/step3.php b/administrator/com_joomgallery/tmpl/migration/step3.php index 455113dc..54ba8535 100644 --- a/administrator/com_joomgallery/tmpl/migration/step3.php +++ b/administrator/com_joomgallery/tmpl/migration/step3.php @@ -58,12 +58,12 @@
-

+

get('type')).'_TITLE'); ?>

- Pendent: ? - Successful: 0 - Failed: 0 + Pendent: get('queue')); ?> + Successful: get('success')); ?> + Failed: get('error')); ?>
From e5e7ab82de2c83154a25920ff6ec4c70423aad8f Mon Sep 17 00:00:00 2001 From: Elfangor Date: Fri, 13 Oct 2023 16:58:15 +0200 Subject: [PATCH 017/121] add migrator JS --- .../language/en-GB/com_joomgallery.ini | 1 + .../src/Controller/MigrationController.php | 127 ++++++++++++++++-- .../src/Model/MigrationModel.php | 4 +- .../com_joomgallery/tmpl/migration/step3.php | 15 ++- media/com_joomgallery/joomla.asset.json | 5 + media/com_joomgallery/js/migrator.js | 74 ++++++++++ 6 files changed, 212 insertions(+), 14 deletions(-) create mode 100644 media/com_joomgallery/js/migrator.js diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index 445b2900..51b5cdcb 100755 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -193,6 +193,7 @@ COM_JOOMGALLERY_FILE_DESCRIPTION_HINT="Description of this file" COM_JOOMGALLERY_FILE_AUTHOR_HINT="Author of this file" COM_JOOMGALLERY_USE_SCRIPT="Use this script" COM_JOOMGALLERY_AVAILABLE_SCRIPT="Available migration scripts" +COM_JOOMGALLERY_MIGRATION_SCRIPT_NOT_EXIST="The requested migration script does not exist. Please provide an available script name in the request." COM_JOOMGALLERY_MIGRATION_STEP1_BTN_TXT="Check migration capability" COM_JOOMGALLERY_MIGRATION_STEP2_BTN_TXT="Start migration manager" COM_JOOMGALLERY_MIGRATION_STEP3_BTN_TXT="Check migration success" diff --git a/administrator/com_joomgallery/src/Controller/MigrationController.php b/administrator/com_joomgallery/src/Controller/MigrationController.php index 2595f50a..c7d4cc45 100644 --- a/administrator/com_joomgallery/src/Controller/MigrationController.php +++ b/administrator/com_joomgallery/src/Controller/MigrationController.php @@ -17,7 +17,9 @@ use \Joomla\Input\Input; use \Joomla\CMS\Language\Text; use \Joomla\CMS\Router\Route; +use \Joomla\CMS\Log\Log; use \Joomla\Registry\Registry; +use \Joomla\CMS\Response\JsonResponse; use \Joomla\CMS\MVC\Controller\BaseController; use \Joomla\CMS\Application\CMSApplication; use \Joomla\CMS\Form\FormFactoryAwareInterface; @@ -267,6 +269,15 @@ public function migrate() // Check for request forgeries $this->checkToken(); + // Access check. + if(false) + { + $this->setMessage(Text::_('COM_JOOMGALLERY_ERROR_MIGRATION_NOT_PERMITTED'), 'error'); + $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration', false)); + + return false; + } + $model = $this->getModel(); $script = $this->app->getUserStateFromRequest(_JOOM_OPTION.'.migration.script', 'script', '', 'cmd'); $scripts = $model->getScripts(); @@ -278,28 +289,128 @@ public function migrate() throw new \Exception('Requested migration script does not exist.', 1); } + $precheck = $this->app->getUserState(_JOOM_OPTION.'.migration.'.$script.'.step2.success', false); + + // Check if no errors detected in precheck (step 2) + if(!$precheck) + { + // Pre-checks not successful. Show error message. + $msg = Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP2_CHECKS_FAILED'); + $this->setMessage(Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP2', $msg), 'error'); + // Redirect to the step 2 screen + $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration&layout=step2', false)); + } + + // Redirect to the step 3 screen + $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration&layout=step3', false)); + } + + /** + * Perform a migration + * + * @return void + * + * @since 4.0.0 + */ + public function start() + { + // Check for request forgeries + $this->checkToken(); + // Access check. if(false) { - $this->setMessage(Text::_('COM_JOOMGALLERY_ERROR_MIGRATION_NOT_PERMITTED'), 'error'); - $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration', false)); + $msg = Text::_('COM_JOOMGALLERY_ERROR_MIGRATION_NOT_PERMITTED'); + $this->ajaxRespond($msg); return false; } - $precheck = $this->app->getUserState(_JOOM_OPTION.'.migration.'.$script.'.step2.success', false); + $model = $this->getModel(); + $script = $this->app->getUserStateFromRequest(_JOOM_OPTION.'.migration.script', 'script', '', 'cmd'); + $scripts = $model->getScripts(); + + // Check if requested script exists + if(!\in_array($script, \array_keys($scripts))) + { + // Requested script does not exists + $msg = new \Exception('Requested migration script does not exist.', 1); + + $this->ajaxRespond($msg); + + return false; + } // Check if no errors detected in precheck (step 2) + $precheck = $this->app->getUserState(_JOOM_OPTION.'.migration.'.$script.'.step2.success', false); if(!$precheck) { // Pre-checks not successful. Show error message. $msg = Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP2_CHECKS_FAILED'); - $this->setMessage(Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP2', $msg), 'error'); - // Redirect to the step 2 screen - $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration&layout=step2', false)); + $this->ajaxRespond($msg); + + return false; } - // Redirect to the step 3 screen - $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration&layout=step3', false)); + $msg = 'Test..'; + $msg = json_encode($msg); + $this->ajaxRespond($msg); + } + + /** + * Returns an ajax response + * + * @return void + * + * @since 4.0.0 + */ + protected function ajaxRespond($results) + { + $this->app->allowCache(false); + $this->app->setHeader('X-Robots-Tag', 'noindex, nofollow'); + + // Requested format passed via URL + $format = strtolower($this->app->getInput()->getWord('format', '')); + + // Return the results in the desired format + switch ($format) + { + // JSONinzed + case 'json': + echo new JsonResponse($results, null, false, $this->app->getInput()->get('ignoreMessages', true, 'bool')); + + break; + + // Raw format + default: + // Output exception + if($results instanceof \Exception) + { + // Log an error + Log::add($results->getMessage(), Log::ERROR); + + // Set status header code + $this->app->setHeader('status', $results->getCode(), true); + + // Echo exception type and message + $out = \get_class($results) . ': ' . $results->getMessage(); + } + elseif(\is_scalar($results)) + { + // Output string/ null + $out = (string) $results; + } + else + { + // Output array/ object + $out = \implode((array) $results); + } + + echo $out; + + break; + } + + //$this->app->close(); } } diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index 7c7e1ffb..98cde307 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -108,7 +108,9 @@ public function getScript() // Retreive script variable $name = $this->app->getUserStateFromRequest(_JOOM_OPTION.'.migration.script', 'script', '', 'cmd'); - if(!$name) + $tmp = \strlen($name); + + if(!$name || \strlen($name) < 2 || \strlen($name) > 30) { $tmp = new \stdClass; $tmp->name = ''; diff --git a/administrator/com_joomgallery/tmpl/migration/step3.php b/administrator/com_joomgallery/tmpl/migration/step3.php index 54ba8535..20169383 100644 --- a/administrator/com_joomgallery/tmpl/migration/step3.php +++ b/administrator/com_joomgallery/tmpl/migration/step3.php @@ -19,7 +19,8 @@ // Import CSS $wa = Factory::getApplication()->getDocument()->getWebAssetManager(); $wa->useStyle('com_joomgallery.admin') - ->useScript('com_joomgallery.admin'); + ->useScript('com_joomgallery.admin') + ->useScript('com_joomgallery.migrator'); ?>
@@ -54,7 +55,10 @@ error) && !empty($this->migrateables)) : ?> migrateables as $key => $migrateable) : ?> - + get('type'); + ?> +
@@ -65,9 +69,10 @@ Successful: get('success')); ?> Failed: get('error')); ?>
- - - + + + +
diff --git a/media/com_joomgallery/joomla.asset.json b/media/com_joomgallery/joomla.asset.json index d5f5a513..efe19871 100755 --- a/media/com_joomgallery/joomla.asset.json +++ b/media/com_joomgallery/joomla.asset.json @@ -137,6 +137,11 @@ "name": "com_joomgallery.uppy", "type": "style", "uri": "com_joomgallery/uppy.css" + }, + { + "name": "com_joomgallery.migrator", + "type": "script", + "uri": "com_joomgallery/migrator.js" } ] } diff --git a/media/com_joomgallery/js/migrator.js b/media/com_joomgallery/js/migrator.js new file mode 100644 index 00000000..a865be0c --- /dev/null +++ b/media/com_joomgallery/js/migrator.js @@ -0,0 +1,74 @@ +// Selectors used by this script +let buttonDataSelector = 'btn-migration'; +let typeSelector = 'data-type'; +let formIdTmpl = 'migrationForm'; + +/** + * Submit a migration task + * @param task + */ +let submitTask = function(event, element) { + event.preventDefault(); + + let type = element.getAttribute(typeSelector); + let formId = formIdTmpl + '-' + type; + let res = performTask(formId); +}; + +/** + * Perform a migration task + * @param task + * + * @return json string + * {success: true, status: 200, message: '', messages: {}, data: {}} + */ +let performTask = async function(formId) { + + // Catch form and data + let formData = new FormData(document.getElementById(formId)); + formData.append('format', 'json'); + + // Set request parameters + let parameters = { + method: 'POST', + mode: 'same-origin', + cache: 'default', + redirect: 'follow', + referrerPolicy: 'no-referrer-when-downgrade', + body: formData, + }; + + // Set the url + let url = document.getElementById(formId).getAttribute('action'); + + // Perform the fetch request + let response = await fetch(url, parameters); + + // Resolve promise as text string + let txt = await response.text(); + let res = null; + + if (!response.ok) { + // Catch network error + console.log(txt); + return {success: false, status: response.status, message: response.message, messages: {}, data: {error: txt}}; + } + + if(txt.startsWith('{"success"')) { + // Response is of type json --> everything fine + res = JSON.parse(txt); + res.status = response.status; + res.data = JSON.parse(res.data); + } else if (txt.includes('Fatal error')) { + // PHP fatal error occurred + res = {success: false, status: response.status, message: response.statusText, messages: {}, data: {error: txt}}; + } else { + // Response is not of type json --> probably some php warnings/notices + let split = txt.split('\n{"'); + let temp = JSON.parse('{"'+split[1]); + let data = JSON.parse(temp.data); + res = {success: true, status: response.status, message: split[0], messages: temp.messages, data: data}; + } + + return res; +} From 8595fbd4439de7efbfe20105ba338b22186df226 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 16 Oct 2023 22:20:13 +0200 Subject: [PATCH 018/121] add precheck: ID availability --- .../language/en-GB/com_joomgallery.ini | 4 +- .../src/Service/Migration/Migrateables.php | 4 +- .../src/Service/Migration/Migration.php | 149 +++++++++++++++++- 3 files changed, 151 insertions(+), 6 deletions(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index 51b5cdcb..dd174a7d 100755 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -288,7 +288,7 @@ COM_JOOMGALLERY_SUCCESS_IMAGETYPE="Image-Type (%s) could not be replaced. Error: COM_JOOMGALLERY_SUCCESS_REPLACE_IMAGETYPE="Image-Type (%s) successfully replaced." COM_JOOMGALLERY_ERROR_REPLACE_IMAGETYPE="Image-Type (%s) could not be replaced. Error: %s." COM_JOOMGALLERY_ERROR_IMGTYPE_TABLE_NOT_EXISTING="Table for content type '%s' does not exist." -COM_JOOMGALLERY_ERROR_TABLE_NOT_EXISTING="Table (%s) does not exist." +COM_JOOMGALLERY_ERROR_TABLE_NOT_EXISTING="Table does not exist." COM_JOOMGALLERY_ERROR_PATH_NOT_EXISTING="Path does not exist." ;Mail Templates @@ -676,7 +676,7 @@ COM_JOOMGALLERY_SERVICE_MIGRATION_SUCCESS_MIGRATION_STEP2="Step 2: Migration pre COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP2_CHECKS_FAILED="Some of the checks failed." COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES="There are already %s records in this table." COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES_EMPTY="This table is empty." -COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES_USE_IDS_HINT="You have chosen that the ID's from the source are also used in the destination table, but your tabele is not empty. Empty the table or make sure the ID's to be created are still free in the destination table!" +COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES_USE_IDS_HINT="You have set 'Use source IDs' to true in step 1. But the ID's from source are not free/available in the destination.
Please delete the records with the following ID's: %s." COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_CAT_SUCCESS="Root category exists and is set up correctly." COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_CAT_ERROR="Root category not existent or not set up correctly. Please make sure Root category is set up correctly." COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_ASSET_SUCCESS="Root asset exists and is set up correctly." diff --git a/administrator/com_joomgallery/src/Service/Migration/Migrateables.php b/administrator/com_joomgallery/src/Service/Migration/Migrateables.php index f1afb7da..1728d905 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migrateables.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migrateables.php @@ -36,7 +36,7 @@ class Migrateables protected $type = ''; /** - * Table name + * Table name (source table) * * @var string * @@ -45,7 +45,7 @@ class Migrateables protected $table = ''; /** - * Primary key + * Primary key (source table) * * @var string * diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index f655339b..7baf533e 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -194,6 +194,12 @@ public function precheck(): array // Check existence and integrity of destination database tables $this->checkDestTable($checks, 'destination'); + // Check image mapping + if($this->params->get('image_usage', 0) > 0) + { + $this->checkImageMapping($checks, 'destination'); + } + return $checks->getAll(); } @@ -677,9 +683,10 @@ protected function checkDestTable(Checks &$checks, string $category) { $checks->addCheck($category, $check_name, true, Text::_('COM_JOOMGALLERY_TABLE') . ': ' . $tablename, Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES_EMPTY')); } - elseif($this->params->get('source_ids') && $count > 0) + elseif($this->params->get('source_ids', 0) > 0 && $count > 0) { - $checks->addCheck($category, $check_name, true, Text::_('COM_JOOMGALLERY_TABLE') . ': ' . $tablename, Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES', $count) . '
' . Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES_USE_IDS_HINT')); + $checks->addCheck($category, $check_name, true, Text::_('COM_JOOMGALLERY_TABLE') . ': ' . $tablename, Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES', $count)); + $this->checkDestTableIdAvailability($checks, $category, $tablename); } else { @@ -687,4 +694,142 @@ protected function checkDestTable(Checks &$checks, string $category) } } } + + /** + * Precheck: Check destination tables for already existing ids + * + * @param Checks $checks The checks object + * @param string $category The checks-category into which to add the new check + * @param string $tablename The table to be checked + * + * @return void + * + * @since 4.0.0 + */ + protected function checkDestTableIdAvailability(Checks &$checks, string $category, string $tablename) + { + // Get content type to check + $type = ''; + foreach(JoomHelper::$content_types as $type => $table) + { + if($table === $tablename) + { + break; + } + + $type = ''; + } + + // Get migrateable to check + $this->getMigrateables(); + $migrateable = null; + foreach($this->migrateables as $key => $migrateable) + { + if($migrateable->get('type', false) === $type) + { + break; + } + + $migrateable = null; + } + + if(!$migrateable) + { + // Table does not correspont to a migrateable. Exit method. + return; + } + + // Get destination database + list($db, $dbPrefix) = $this->getDB('destination'); + + // Get a list of used ids from destination database + $destQuery = $db->getQuery(true); + $destQuery->select($db->quoteName('id')) + ->from($db->quoteName($tablename)); + $destQuery_string = \trim($destQuery->__toString()); + + if($this->params->get('same_db', 1)) + { + // Get list of used ids from source databse + $srcQuery = $db->getQuery(true); + $srcQuery->select($db->quoteName($migrateable->get('pk'), 'id')) + ->from($db->quoteName($migrateable->get('table'))); + $srcQuery_string = \trim($srcQuery->__toString()); + + // Get a list of ids used in both source and destination + $query = $db->getQuery(true); + $query->select($db->quoteName('ids.id')) + ->from('(' . $srcQuery_string . ') ids') + ->where($db->quoteName('ids.id') . ' IN (' . $destQuery_string . ')'); + $db->setQuery($query); + } + else + { + // Get source database + list($src_db, $src_dbPrefix) = $this->getDB('source'); + + // Get list of used ids from the source database + $query = $src_db->getQuery(true); + $query->select($db->quoteName($migrateable->get('pk'), 'id')) + ->from($db->quoteName($migrateable->get('table'))); + $src_db->setQuery($query); + + // Load list from source database + $src_list = $src_db->loadColumn(); + + if(\count($src_list) < 1) + { + // There are no records in the source tabele. Exit method. + return; + } + + // Create UNION query string + foreach($src_list as $i => $id) + { + ${'query' . $i} = $db->getQuery(true); + ${'query' . $i}->select($db->quote($id) . ' AS ' . $db->quoteName('id')); + if($i > 0) + { + $query0->unionAll(${'query' . $i}); + } + } + $srcQuery_string = \trim($query0->__toString()); + + // Get a list of ids used in both source and destination + $query = $db->getQuery(true); + $query->select($db->quoteName('ids.id')) + ->from('(' . $srcQuery_string . ') ids') + ->where($db->quoteName('ids.id') . ' IN (' . $destQuery_string . ')'); + $db->setQuery($query); + } + + // Load list of Id's used in both tables (source and destination) + $list = $db->loadColumn(); + + // Exception for root category + if($tablename == _JOOM_TABLE_CATEGORIES) + { + $list = \array_diff($list, array(1, '1')); + } + + if(!empty($list)) + { + $checks->addCheck($category, 'dest_table_' . $tablename . '_ids', false, Text::_('COM_JOOMGALLERY_TABLE') . ': ' . $tablename, Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES_USE_IDS_HINT', \implode(',', $list))); + } + } + + /** + * Precheck: Check the configured image mapping + * + * @param Checks $checks The checks object + * @param string $category The checks-category into which to add the new check + * + * @return void + * + * @since 4.0.0 + */ + protected function checkImageMapping(Checks &$checks, string $category) + { + + } } From faca6c5fa368587f47681d4973d508fee94714d2 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Tue, 17 Oct 2023 00:05:29 +0200 Subject: [PATCH 019/121] add precheck: image mapping --- .../language/en-GB/com_joomgallery.ini | 4 ++ .../src/Service/Migration/Migration.php | 56 ++++++++++++++++++- 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index dd174a7d..f8ebd97c 100755 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -685,6 +685,10 @@ COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_CAT_ASSET_SUCCESS="Root category asset ex COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_CAT_ASSET_ERROR="Root category asset not existent or not set up correctly. Please make sure root category asset is set up correctly." COM_JOOMGALLERY_SERVICE_MIGRATION_TABLE_CONN_ERROR="Connection to database not possible." COM_JOOMGALLERY_SERVICE_MIGRATION_STEP_NOT_AVAILABLE="This step is not available. Please fulfill the previous step first." +COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_MAPPING_ERROR="Image mapping was not correctly applied in step 1. There has to be exactly one row for each available destination imagetype. Please go back to step 1 and apply the mapping correctly." +COM_JOOMGALLERY_SERVICE_MIGRATION_MAPPING_DEST_IMAGETYPE_NOT_EXIST="The destination imagetype '%s' does not exist or is used twice in the mapping. Please go back to step 1 and apply the mapping correctly." +COM_JOOMGALLERY_SERVICE_MIGRATION_MAPPING_SRC_IMAGETYPE_NOT_EXIST="The source imagetype '%s' does not exist. Please go back to step 1 and apply the mapping correctly." +COM_JOOMGALLERY_SERVICE_MIGRATION_MAPPING_IMAGETYPE_NOT_USED="The following destination imagetypes are not used in the mapping: '%s'. Please go back to step 1 and apply the mapping correctly." ;Plugin events COM_JOOMGALLERY_PLUGIN_ERROR_RETURN_VALUE="Return value of the plugin event '%s' must be of type %s and contain : %s" diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index 7baf533e..ebd7ada2 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -830,6 +830,60 @@ protected function checkDestTableIdAvailability(Checks &$checks, string $categor */ protected function checkImageMapping(Checks &$checks, string $category) { - + $mapping = $this->params->get('image_mapping'); + $dest_imagetypes = JoomHelper::getRecords('imagetypes', $this->component); + $src_imagetypes = array(); + + // Check if mapping contains enough elements + if(\count((array)$mapping) != \count($dest_imagetypes)) + { + $checks->addCheck($category, 'mapping_count', false, Text::_('COM_JOOMGALLERY_FIELDS_IMAGEMAPPING_LABEL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_MAPPING_ERROR')); + return; + } + + // Load source imagetypes from xml file + $xml = \simplexml_load_file(JPATH_ADMINISTRATOR.'/components/'._JOOM_OPTION.'/src/Service/Migration/Scripts/'. $this->name . '.xml'); + $element = $xml->xpath('/form/fieldset/field[@name="image_mapping"]/form/field[@name="source"]'); + + foreach($element[0]->option as $option) + { + \array_push($src_imagetypes, (string) $option['value']); + } + + // Prepare destination imagetypes + $tmp_dest_imagetypes = array(); + foreach($dest_imagetypes as $key => $type) + { + \array_push($tmp_dest_imagetypes, (string) $type->typename); + } + + // Check if all imagetypes are correctly set in the mapping + foreach($mapping as $key => $mapVal) + { + if(\in_array($mapVal->destination, $tmp_dest_imagetypes)) + { + // Remove imagetype from tmp_dest_imagetypes array + $tmp_dest_imagetypes = \array_diff($tmp_dest_imagetypes, array($mapVal->destination)); + } + else + { + // Destination imagetype in mapping does not exist + $checks->addCheck($category, 'mapping_dest_types_'.$mapVal->destination, false, Text::_('COM_JOOMGALLERY_FIELDS_IMAGEMAPPING_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_MAPPING_DEST_IMAGETYPE_NOT_EXIST', Text::_('COM_JOOMGALLERY_' . \strtoupper($mapVal->destination)))); + return; + } + + if(!\in_array($mapVal->source, $src_imagetypes)) + { + // Source imagetype in mapping does not exist + $checks->addCheck($category, 'mapping_src_types_'.$mapVal->source, false, Text::_('COM_JOOMGALLERY_FIELDS_IMAGEMAPPING_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_MAPPING_IMAGETYPE_NOT_EXIST', Text::_('COM_JOOMGALLERY_' . \strtoupper($mapVal->source)))); + return; + } + } + + if(!empty($tmp_dest_imagetypes)) + { + // Destination imagetype not used in the mapping + $checks->addCheck($category, 'mapping_dest_types', false, Text::_('COM_JOOMGALLERY_FIELDS_IMAGEMAPPING_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_MAPPING_IMAGETYPE_NOT_USED', \implode(', ', $tmp_dest_imagetypes))); + } } } From 3bfc7c4f2698e4f2a2bd1f9bac3996b150a37f75 Mon Sep 17 00:00:00 2001 From: Elfangor Date: Mon, 23 Oct 2023 07:09:08 +0200 Subject: [PATCH 020/121] add migration table --- .../com_joomgallery/includes/defines.php | 1 + .../sql/install.mysql.utf8.sql | 23 +++ .../src/Model/MigrationModel.php | 180 ++++++++++++++++-- .../src/Service/Migration/Migration.php | 17 +- .../src/Table/MigrationTable.php | 170 +++++++++++++++++ 5 files changed, 369 insertions(+), 22 deletions(-) create mode 100644 administrator/com_joomgallery/src/Table/MigrationTable.php diff --git a/administrator/com_joomgallery/includes/defines.php b/administrator/com_joomgallery/includes/defines.php index d06eb992..f6445e6a 100644 --- a/administrator/com_joomgallery/includes/defines.php +++ b/administrator/com_joomgallery/includes/defines.php @@ -27,6 +27,7 @@ define('_JOOM_TABLE_GALLERIES_REF', '#__joomgallery_galleries_ref'); define('_JOOM_TABLE_IMG_TYPES', '#__joomgallery_img_types'); define('_JOOM_TABLE_TYPES', '#__joomgallery_img_types'); +define('_JOOM_TABLE_MIGRATION', '#__joomgallery_migration'); define('_JOOM_TABLE_TAGS', '#__joomgallery_tags'); define('_JOOM_TABLE_TAGS_REF', '#__joomgallery_tags_ref'); define('_JOOM_TABLE_USERS', '#__joomgallery_users'); diff --git a/administrator/com_joomgallery/sql/install.mysql.utf8.sql b/administrator/com_joomgallery/sql/install.mysql.utf8.sql index 0a2f7b0c..ff48d8ce 100644 --- a/administrator/com_joomgallery/sql/install.mysql.utf8.sql +++ b/administrator/com_joomgallery/sql/install.mysql.utf8.sql @@ -372,6 +372,29 @@ KEY `idx_createdby` (`created_by`) -- -------------------------------------------------------- +-- +-- Table structure for table `#__joomgallery_migration` +-- + +CREATE TABLE IF NOT EXISTS `#__joomgallery_migration` ( +`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, +`script` VARCHAR(50) NOT NULL DEFAULT "", +`type` VARCHAR(50) NOT NULL DEFAULT "", +`src_table` VARCHAR(255) NOT NULL DEFAULT "", +`src_pk` VARCHAR(25) NOT NULL DEFAULT "id", +`dst_table` VARCHAR(255) NOT NULL DEFAULT "", +`dst_pk` VARCHAR(25) NOT NULL DEFAULT "id", +`queue` TEXT NOT NULL, +`successful` TEXT NOT NULL, +`failed` TEXT NOT NULL, +`created_time` DATETIME NOT NULL, +`checked_out` INT(11) UNSIGNED NOT NULL DEFAULT 0, +`checked_out_time` DATETIME DEFAULT NULL, +PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci; + +-- -------------------------------------------------------- + -- -- Dumping data for table `#__content_types` -- diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index 98cde307..f7835d89 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -31,19 +31,37 @@ */ class MigrationModel extends FormModel { + /** + * @var string Alias to manage history control + * + * @since 4.0.0 + */ + public $typeAlias = _JOOM_OPTION.'.migration'; + /** - * @var string The prefix to use with controller messages. + * @var string The prefix to use with controller messages * * @since 4.0.0 */ protected $text_prefix = _JOOM_OPTION_UC; - /** - * @var string Alias to manage history control + /** + * Storage for the migration form object. + * + * @var Registry * * @since 4.0.0 */ - public $typeAlias = _JOOM_OPTION.'.migration'; + protected $params = null; + + /** + * Name of the migration script. + * + * @var string + * + * @since 4.0.0 + */ + protected $name = ''; /** * Constructor @@ -75,7 +93,7 @@ public function __construct($config = array()) * @since 4.0.0 * @throws \Exception Missing migration params */ - protected function setParams($params = null) + public function setParams($params = null) { $info = $this->getScript(); @@ -91,8 +109,21 @@ protected function setParams($params = null) } // Set the migration parameters - $params = new Registry($params); - $this->component->getMigration()->set('params', $params); + $this->params = new Registry($params); + $this->component->getMigration()->set('params', $this->params); + } + + /** + * Method to get info array of current migration script. + * + * @return object|boolean Migration info object. + * + * @since 4.0.0 + * @throws \Exception + */ + public function getName() + { + return $this->getScript(); } /** @@ -108,15 +139,16 @@ public function getScript() // Retreive script variable $name = $this->app->getUserStateFromRequest(_JOOM_OPTION.'.migration.script', 'script', '', 'cmd'); - $tmp = \strlen($name); - if(!$name || \strlen($name) < 2 || \strlen($name) > 30) { - $tmp = new \stdClass; - $tmp->name = ''; + $tmp = new \stdClass; + $tmp->name = ''; + $this->name = ''; return $tmp; } + + $this->name = $name; if(!$this->component->getMigration()) { @@ -170,6 +202,132 @@ public function getMigrateables() return $this->component->getMigration()->getMigrateables(); } + /** + * Load the current queue of ids from table + * + * @param string $type Content type + * + * @return array + * + * @since 4.0.0 + */ + public function getQueue($type) + { + // Retreive script + $script = $this->getScript(); + + // Create a new query object. + $db = $this->getDbo(); + $query = $db->getQuery(true); + + if(!$script) + { + return $query; + } + + // Select the required fields from the table. + $query->select('a.*'); + $query->from($db->quoteName(_JOOM_TABLE_MIGRATION, 'a')); + + // Filter for the current script + $query->where($db->quoteName('a.script') . ' = ' . $db->quote($script)); + $query->order($db->quoteName('a.type') . ' DESC'); + } + + /** + * Method to get an array of data items based on current script. + * + * @return mixed An array of data items on success, false on failure. + * + * @since 4.0.0 + */ + public function getItems() + { + try + { + $items = $this->_getList($this->getListQuery()); + } + catch (\RuntimeException $e) + { + $this->component->setError($e->getMessage()); + + return false; + } + + // + } + + /** + * Method to get a single record. + * + * @param integer|array $pk The id of the primary key or array(fieldname => value) + * + * @return mixed Object on success, false on failure. + * + * @since 4.0.0 + */ + public function getItem($pk = null) + { + $pk = (!empty($pk)) ? $pk : 0; + $table = $this->getTable(); + + if($pk > 0 || \is_array($pk)) + { + // Attempt to load the row. + $return = $table->load($pk); + + // Check for a table object error. + if($return === false) + { + // If there was no underlying error, then the false means there simply was not a row in the db for this $pk. + if(!$table->getError()) + { + $this->component->setError(Text::_('JLIB_APPLICATION_ERROR_NOT_EXIST')); + } + else + { + $this->component->setError($table->getError()); + } + + return false; + } + } + + return $table->getFieldsValues(); + } + + /** + * Build an SQL query to load the list data. + * + * @return DatabaseQuery + * + * @since 4.0.0 + */ + protected function getListQuery() + { + // Retreive script + $script = $this->getScript(); + + // Create a new query object. + $db = $this->getDbo(); + $query = $db->getQuery(true); + + if(!$script) + { + return $query; + } + + // Select the required fields from the table. + $query->select('a.*'); + $query->from($db->quoteName(_JOOM_TABLE_MIGRATION, 'a')); + + // Filter for the current script + $query->where($db->quoteName('a.script') . ' = ' . $db->quote($script)); + $query->order($db->quoteName('a.type') . ' DESC'); + + return $query; + } + /** * Method to get the migration form. * diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index ebd7ada2..5519bf62 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -23,6 +23,7 @@ use \Joomla\Database\DatabaseFactory; use \Joomla\Component\Media\Administrator\Exception\FileNotFoundException; use \Joomgallery\Component\Joomgallery\Administrator\Helper\JoomHelper; +use \Joomgallery\Component\Joomgallery\Administrator\Table\MigrationTable; use \Joomgallery\Component\Joomgallery\Administrator\Extension\ServiceTrait; use \Joomgallery\Component\Joomgallery\Administrator\Service\Migration\Checks; use \Joomgallery\Component\Joomgallery\Administrator\Service\Migration\MigrationInterface; @@ -86,7 +87,7 @@ abstract class Migration implements MigrationInterface /** * List of migrateables processed/migrated with this script * - * @var Migrateables[] + * @var MigrationTable[] * * @since 4.0.0 */ @@ -140,17 +141,11 @@ public function getMigrateables(): array { if(empty($this->migrateables)) { - // fetch a list of migrateables from source - foreach($this->types as $key => $type) - { - list($name, $pk) = $this->getSourceTableInfo($type); - list($db, $dbPrefix) = $this->getDB('source'); - - $migraeable = new Migrateables($type, $pk, $name); - $migraeable->loadQueue($db); + // Get MigrationModel + $model = $this->component->getMVCFactory()->createModel('migration', 'administrator'); - \array_push($this->migrateables, $migraeable); - } + // Load migrateables + $this->migrateables = $model->getItems(); } return $this->migrateables; diff --git a/administrator/com_joomgallery/src/Table/MigrationTable.php b/administrator/com_joomgallery/src/Table/MigrationTable.php new file mode 100644 index 00000000..ddc995ad --- /dev/null +++ b/administrator/com_joomgallery/src/Table/MigrationTable.php @@ -0,0 +1,170 @@ + ** +** @copyright 2008 - 2023 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 3 or later ** +*****************************************************************************************/ + +namespace Joomgallery\Component\Joomgallery\Administrator\Table; + +// No direct access +defined('_JEXEC') or die; + +use \Joomla\CMS\Table\Table; +use \Joomla\Database\DatabaseDriver; + +/** + * Migration table + * + * @package JoomGallery + * @since 4.0.0 + */ +class MigrationTable extends Table +{ + /** + * Migration progress (0-100) + * + * @var int + * + * @since 4.0.0 + */ + protected $progress = 0; + + /** + * True if migration of this migrateable is completed + * + * @var bool + * + * @since 4.0.0 + */ + protected $completed = false; + + + /** + * Constructor + * + * @param JDatabase &$db A database connector object + */ + public function __construct(DatabaseDriver $db) + { + $this->typeAlias = _JOOM_OPTION.'.migration'; + + parent::__construct(_JOOM_TABLE_IMG_TYPES, 'id', $db); + } + + /** + * Get the type alias for the history table + * + * @return string The alias as described above + * + * @since 4.0.0 + */ + public function getTypeAlias() + { + return $this->typeAlias; + } + + /** + * Method to store a row in the database from the Table instance properties. + * + * If a primary key value is set the row with that primary key value will be updated with the instance property values. + * If no primary key value is set a new row will be inserted into the database with the properties from the Table instance. + * + * @param boolean $updateNulls True to update fields even if they are null. + * + * @return boolean True on success. + * + * @since 4.0.0 + */ + public function store($updateNulls = true) + { + return parent::store($updateNulls); + } + + /** + * Overloaded check function + * + * @return bool + */ + public function check() + { + // Support for subform field queue + if(is_array($this->queue)) + { + $this->queue = json_encode($this->queue, JSON_UNESCAPED_UNICODE); + } + + // Support for subform field successful + if(is_array($this->successful)) + { + $this->successful = json_encode($this->successful, JSON_UNESCAPED_UNICODE); + } + + // Support for subform field failed + if(is_array($this->failed)) + { + $this->failed = json_encode($this->failed, JSON_UNESCAPED_UNICODE); + } + + return parent::check(); + } + + /** + * Delete a record by id + * + * @param mixed $pk Primary key value to delete. Optional + * + * @return bool + */ + public function delete($pk = null) + { + $this->load($pk); + $result = parent::delete($pk); + + return $result; + } + + /** + * Overloaded bind function to pre-process the params. + * + * @param array $array Named array + * @param mixed $ignore Optional array or list of parameters to ignore + * + * @return boolean True on success. + * + * @see Table:bind + * @since 4.0.0 + * @throws \InvalidArgumentException + */ + public function bind($array, $ignore = '') + { + // Support for queue field + if(isset($array['queue']) && is_array($array['queue'])) + { + $registry = new Registry; + $registry->loadArray($array['queue']); + $array['queue'] = (string) $registry; + } + + // Support for successful field + if(isset($array['successful']) && is_array($array['successful'])) + { + $registry = new Registry; + $registry->loadArray($array['successful']); + $array['successful'] = (string) $registry; + } + + // Support for failed field + if(isset($array['failed']) && is_array($array['failed'])) + { + $registry = new Registry; + $registry->loadArray($array['failed']); + $array['failed'] = (string) $registry; + } + + return parent::bind($array, $ignore); + } +} From 7ade55d1327199adf92ab22bf225d2c2ddd81b20 Mon Sep 17 00:00:00 2001 From: Elfangor Date: Fri, 27 Oct 2023 08:48:34 +0200 Subject: [PATCH 021/121] replace Migrateables class --- .../sql/install.mysql.utf8.sql | 1 + .../src/Model/MigrationModel.php | 155 +++++++++++++----- .../src/Service/Migration/Migrateables.php | 128 --------------- .../src/Service/Migration/Migration.php | 2 +- .../Service/Migration/MigrationInterface.php | 13 ++ .../Service/Migration/Scripts/Jg3ToJg4.php | 6 +- .../src/Table/MigrationTable.php | 11 +- .../com_joomgallery/tmpl/migration/step3.php | 19 ++- 8 files changed, 159 insertions(+), 176 deletions(-) delete mode 100644 administrator/com_joomgallery/src/Service/Migration/Migrateables.php diff --git a/administrator/com_joomgallery/sql/install.mysql.utf8.sql b/administrator/com_joomgallery/sql/install.mysql.utf8.sql index ff48d8ce..418548a3 100644 --- a/administrator/com_joomgallery/sql/install.mysql.utf8.sql +++ b/administrator/com_joomgallery/sql/install.mysql.utf8.sql @@ -387,6 +387,7 @@ CREATE TABLE IF NOT EXISTS `#__joomgallery_migration` ( `queue` TEXT NOT NULL, `successful` TEXT NOT NULL, `failed` TEXT NOT NULL, +`params` TEXT NOT NULL, `created_time` DATETIME NOT NULL, `checked_out` INT(11) UNSIGNED NOT NULL DEFAULT 0, `checked_out_time` DATETIME DEFAULT NULL, diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index f7835d89..e744268b 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -61,7 +61,7 @@ class MigrationModel extends FormModel * * @since 4.0.0 */ - protected $name = ''; + protected $scriptName = ''; /** * Constructor @@ -121,7 +121,7 @@ public function setParams($params = null) * @since 4.0.0 * @throws \Exception */ - public function getName() + public function getScriptName() { return $this->getScript(); } @@ -143,12 +143,12 @@ public function getScript() { $tmp = new \stdClass; $tmp->name = ''; - $this->name = ''; + $this->scriptName = ''; return $tmp; } - $this->name = $name; + $this->scriptName = $name; if(!$this->component->getMigration()) { @@ -181,7 +181,7 @@ public function getScripts() } /** - * Method to a list of content types which can be migrated using the selected script. + * Method to fetch a list of content types which can be migrated using the selected script. * * @return array|boolean List of content types on success, false otherwise * @@ -203,58 +203,140 @@ public function getMigrateables() } /** + * Method to get an array of migrateables based on current script. + * + * @return MigrationTable[] An array of data items + * + * @since 4.0.0 + */ + public function getItems(): array + { + // Get types from migration service + $types = $this->component->getMigration()->get('types'); + + // Get available types from db + try + { + $db = $this->getDbo(); + $query = $this->getListQuery(); + + if(\is_string($query)) + { + $query = $db->getQuery(true)->setQuery($query); + } + + $db->setQuery($query); + $items = $db->loadObjectList('type'); + } + catch (\RuntimeException $e) + { + $this->component->setError($e->getMessage()); + + return false; + } + + $tables = array(); + foreach($types as $key => $type) + { + $table = $this->getTable(); + + if(!empty($items) && \key_exists($type, $items)) + { + // Attempt to load the row. + $return = $table->load($items[$type]->id); + + // Check for a table object error. + if($return === false) + { + $this->component->setError($e->getMessage()); + + return false; + } + } + + // Add script if empty + if(empty($table->script)) + { + $table->script = $this->scriptName; + } + + // Add type if empty + if(empty($table->type)) + { + $table->type = $type; + } + + // Add source and destination table info if empty + if(empty($table->src_table)) + { + // Get table information + list($src_table, $src_pk) = $this->component->getMigration()->getSourceTableInfo($type); + $table->src_table = $src_table; + $table->src_pk = $src_pk; + $table->dst_table = JoomHelper::$content_types[$type]; + $table->dst_pk = 'id'; + } + + // Add queue if empty + if(\is_null($table->queue) || empty($table->queue)) + { + // Load queue + $table->queue = $this->getQueue($type, $table); + } + + // Add params + if($this->component->getMigration()->get('params')) + { + $table->params = $this->component->getMigration()->get('params'); + } + + array_push($tables, $table); + } + + return $tables; + } + + /** * Load the current queue of ids from table * - * @param string $type Content type + * @param string $type Content type + * @param MigrationTable $table Table object * * @return array * * @since 4.0.0 */ - public function getQueue($type) + public function getQueue($type, $table=null): array { // Retreive script $script = $this->getScript(); // Create a new query object. - $db = $this->getDbo(); - $query = $db->getQuery(true); + list($db, $dbPrefix) = $this->component->getMigration()->getDB('source'); + $query = $db->getQuery(true); if(!$script) { return $query; } - // Select the required fields from the table. - $query->select('a.*'); - $query->from($db->quoteName(_JOOM_TABLE_MIGRATION, 'a')); - - // Filter for the current script - $query->where($db->quoteName('a.script') . ' = ' . $db->quote($script)); - $query->order($db->quoteName('a.type') . ' DESC'); - } - - /** - * Method to get an array of data items based on current script. - * - * @return mixed An array of data items on success, false on failure. - * - * @since 4.0.0 - */ - public function getItems() - { - try + if(\is_null($table)) { - $items = $this->_getList($this->getListQuery()); + $migrateables = $this->component->getMigration()->getMigrateables(); + $migrateable = $migrateables[$type]; } - catch (\RuntimeException $e) + else { - $this->component->setError($e->getMessage()); - - return false; + $migrateable = $table; } - // + // Select the required fields from the table. + $query->select($db->quoteName($migrateable->get('src_pk', 'id'))) + ->from($db->quoteName($migrateable->get('src_table'))) + ->order($db->quoteName($migrateable->get('src_pk', 'id')) . ' ASC'); + $db->setQuery($query); + + return $db->loadColumn(); } /** @@ -318,12 +400,11 @@ protected function getListQuery() } // Select the required fields from the table. - $query->select('a.*'); + $query->select(array('a.id', 'a.type')); $query->from($db->quoteName(_JOOM_TABLE_MIGRATION, 'a')); // Filter for the current script - $query->where($db->quoteName('a.script') . ' = ' . $db->quote($script)); - $query->order($db->quoteName('a.type') . ' DESC'); + $query->where($db->quoteName('a.script') . ' = ' . $db->quote($script->name)); return $query; } diff --git a/administrator/com_joomgallery/src/Service/Migration/Migrateables.php b/administrator/com_joomgallery/src/Service/Migration/Migrateables.php deleted file mode 100644 index 1728d905..00000000 --- a/administrator/com_joomgallery/src/Service/Migration/Migrateables.php +++ /dev/null @@ -1,128 +0,0 @@ - ** -** @copyright 2008 - 2023 JoomGallery::ProjectTeam ** -** @license GNU General Public License version 3 or later ** -*****************************************************************************************/ - -namespace Joomgallery\Component\Joomgallery\Administrator\Service\Migration; - -// No direct access -\defined('_JEXEC') or die; - -use \Joomgallery\Component\Joomgallery\Administrator\Extension\ServiceTrait; - -/** - * Migrateable Class - * Providing information about a record type beeing migrated - * - * @package JoomGallery - * @since 4.0.0 - */ -class Migrateables -{ - use ServiceTrait; - - /** - * Content type - * - * @var string - * - * @since 4.0.0 - */ - protected $type = ''; - - /** - * Table name (source table) - * - * @var string - * - * @since 4.0.0 - */ - protected $table = ''; - - /** - * Primary key (source table) - * - * @var string - * - * @since 4.0.0 - */ - protected $pk = ''; - - /** - * Migration progress (0-100) - * - * @var int - * - * @since 4.0.0 - */ - protected $progress = 0; - - /** - * List of source record ID's to be migrated - * - * @var array - * - * @since 4.0.0 - */ - public $queue = array(); - - /** - * List of source record ID's successfully migrated - * - * @var array - * - * @since 4.0.0 - */ - protected $success = array(); - - /** - * List of source record ID's with an error during migration - * - * @var array - * - * @since 4.0.0 - */ - protected $error = array(); - - /** - * Constructor - * - * @param string $type The content type - * @param string $pk Primary key name - * @param string $table Table name - * - * @return void - * - * @since 4.0.0 - */ - public function __construct(string $type, string $pk, string $table) - { - $this->type = $type; - $this->pk = $pk; - $this->table = $table; - } - - /** - * Load the initial queue of ids from source db - * - * @param object $db Database object - * - * @return void - * - * @since 4.0.0 - */ - public function loadQueue($db) - { - $query = $db->getQuery(true) - ->select($this->pk) - ->from($this->table); - $db->setQuery($query); - - $this->queue = $db->loadColumn(); - } -} \ No newline at end of file diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index 5519bf62..57ec0410 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -266,7 +266,7 @@ protected function addLog($txt, $priority) * @since 4.0.0 * @throws \Exception */ - protected function getDB(string $target): array + public function getDB(string $target): array { if(!in_array($target, array('source', 'destination'))) { diff --git a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php index d79a2ad8..d2ac67bc 100644 --- a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php +++ b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php @@ -60,6 +60,18 @@ public function postcheck(); */ public function migrate($type, $source, $dest); + /** + * Get a database object + * + * @param string $target The target (source or destination) + * + * @return array list($db, $dbPrefix) + * + * @since 4.0.0 + * @throws \Exception + */ + public function getDB(string $target): array; + /** * Returns a list of content types which can be migrated. * @@ -123,6 +135,7 @@ public function getSourceTableInfo(string $type): array; * Returns a list of involved source tables. * * @return array List of table names (Joomla style, e.g #__joomgallery) + * array('image' => '#__joomgallery', ...) * * @since 4.0.0 */ diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index 1f5acfd1..e4fb0fb6 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -40,6 +40,7 @@ class Jg3ToJg4 extends Migration implements MigrationInterface /** * List of content types which can be migrated with this script * Use the singular form of the content type (e.g image, not images) + * Order in the list corresponds to migration order! * * @var array * @@ -170,7 +171,7 @@ public function getSourceTableInfo(string $type): array if(!\in_array($type, \array_keys($tables))) { - throw new \Exception('There is no table associated with the given content type. Given: ' . $type, 1); + throw new \Exception('There is no migration source table associated with the given content type. Given: ' . $type, 1); } if($this->params->get('same_db')) @@ -185,9 +186,10 @@ public function getSourceTableInfo(string $type): array } /** - * Returns a list of all available source tables. + * Returns a list of involved source tables. * * @return array List of table names (Joomla style, e.g #__joomgallery) + * array('image' => '#__joomgallery', ...) * * @since 4.0.0 */ diff --git a/administrator/com_joomgallery/src/Table/MigrationTable.php b/administrator/com_joomgallery/src/Table/MigrationTable.php index ddc995ad..dc467f15 100644 --- a/administrator/com_joomgallery/src/Table/MigrationTable.php +++ b/administrator/com_joomgallery/src/Table/MigrationTable.php @@ -31,7 +31,7 @@ class MigrationTable extends Table * * @since 4.0.0 */ - protected $progress = 0; + public $progress = 0; /** * True if migration of this migrateable is completed @@ -40,7 +40,7 @@ class MigrationTable extends Table * * @since 4.0.0 */ - protected $completed = false; + public $completed = false; /** @@ -52,7 +52,12 @@ public function __construct(DatabaseDriver $db) { $this->typeAlias = _JOOM_OPTION.'.migration'; - parent::__construct(_JOOM_TABLE_IMG_TYPES, 'id', $db); + parent::__construct(_JOOM_TABLE_MIGRATION, 'id', $db); + + // Initialize queue, successful and failed + $this->queue = array(); + $this->successful = array(); + $this->failed = array(); } /** diff --git a/administrator/com_joomgallery/tmpl/migration/step3.php b/administrator/com_joomgallery/tmpl/migration/step3.php index 20169383..4bd72f5c 100644 --- a/administrator/com_joomgallery/tmpl/migration/step3.php +++ b/administrator/com_joomgallery/tmpl/migration/step3.php @@ -54,6 +54,10 @@
error) && !empty($this->migrateables)) : ?> + migrateables as $key => $migrateable) : ?> get('type'); @@ -62,16 +66,17 @@
-

get('type')).'_TITLE'); ?>

+

- Pendent: get('queue')); ?> - Successful: get('success')); ?> - Failed: get('error')); ?> + Pendent: queue); ?> + Successful: successful); ?> + Failed: failed); ?>
- + +
@@ -84,6 +89,10 @@

+ completed; + $i++; + ?> From 6f4d9257242e80fc6a2d45e4b166e2a4db542a22 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Sun, 29 Oct 2023 20:53:31 +0100 Subject: [PATCH 022/121] add migrator.js --- .../language/en-GB/com_joomgallery.ini | 2 + .../src/Controller/MigrationController.php | 81 +- .../src/Model/MigrationModel.php | 100 +- .../src/Service/Migration/Migration.php | 26 + .../Service/Migration/MigrationInterface.php | 11 +- .../Service/Migration/Scripts/Jg3ToJg4.php | 28 +- .../src/Table/MigrationTable.php | 80 +- .../com_joomgallery/tmpl/migration/step3.php | 4 +- media/com_joomgallery/joomla.asset.json | 2 +- .../js/migrator/dist/migrator.js | 147 ++ .../js/migrator/dist/migrator.js.map | 1 + .../js/migrator/package-lock.json | 1371 +++++++++++++++++ .../com_joomgallery/js/migrator/package.json | 18 + .../js/{migrator.js => migrator/src/index.js} | 59 +- .../js/migrator/webpack.config.js | 15 + 15 files changed, 1816 insertions(+), 129 deletions(-) create mode 100644 media/com_joomgallery/js/migrator/dist/migrator.js create mode 100644 media/com_joomgallery/js/migrator/dist/migrator.js.map create mode 100644 media/com_joomgallery/js/migrator/package-lock.json create mode 100644 media/com_joomgallery/js/migrator/package.json rename media/com_joomgallery/js/{migrator.js => migrator/src/index.js} (54%) create mode 100644 media/com_joomgallery/js/migrator/webpack.config.js diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index f8ebd97c..b13efaec 100755 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -290,6 +290,7 @@ COM_JOOMGALLERY_ERROR_REPLACE_IMAGETYPE="Image-Type (%s) could not be replaced. COM_JOOMGALLERY_ERROR_IMGTYPE_TABLE_NOT_EXISTING="Table for content type '%s' does not exist." COM_JOOMGALLERY_ERROR_TABLE_NOT_EXISTING="Table does not exist." COM_JOOMGALLERY_ERROR_PATH_NOT_EXISTING="Path does not exist." +COM_JOOMGALLERY_ERROR_TASK_NOT_PERMITTED="You are not permitted to perform this task. Requested task: %s" ;Mail Templates COM_JOOMGALLERY_MAIL_NEWIMAGE_TITLE="JoomGallery: New Image" @@ -689,6 +690,7 @@ COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_MAPPING_ERROR="Image mapping was not cor COM_JOOMGALLERY_SERVICE_MIGRATION_MAPPING_DEST_IMAGETYPE_NOT_EXIST="The destination imagetype '%s' does not exist or is used twice in the mapping. Please go back to step 1 and apply the mapping correctly." COM_JOOMGALLERY_SERVICE_MIGRATION_MAPPING_SRC_IMAGETYPE_NOT_EXIST="The source imagetype '%s' does not exist. Please go back to step 1 and apply the mapping correctly." COM_JOOMGALLERY_SERVICE_MIGRATION_MAPPING_IMAGETYPE_NOT_USED="The following destination imagetypes are not used in the mapping: '%s'. Please go back to step 1 and apply the mapping correctly." +COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_RECORD_ID_MISSING="Starting a migration without source data ID not possible. Contact the developer of the migration script to solve this." ;Plugin events COM_JOOMGALLERY_PLUGIN_ERROR_RETURN_VALUE="Return value of the plugin event '%s' must be of type %s and contain : %s" diff --git a/administrator/com_joomgallery/src/Controller/MigrationController.php b/administrator/com_joomgallery/src/Controller/MigrationController.php index c7d4cc45..8fbaee98 100644 --- a/administrator/com_joomgallery/src/Controller/MigrationController.php +++ b/administrator/com_joomgallery/src/Controller/MigrationController.php @@ -89,6 +89,7 @@ public function __construct($config = [], MVCFactoryInterface $factory = null, ? $this->setFormFactory($formFactory); $this->component = $this->app->bootComponent(_JOOM_OPTION); + $this->component->createAccess(); // As copy should be standard on forms. $this->registerTask('check', 'precheck'); @@ -179,7 +180,8 @@ public function precheck() $task = $this->getTask(); // Access check. - if(false) + $acl = $this->component->getAccess(); + if(!$acl->checkACL('admin', 'com_joomgallery')) { $this->setMessage(Text::_('COM_JOOMGALLERY_ERROR_MIGRATION_NOT_PERMITTED'), 'error'); $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration', false)); @@ -270,7 +272,8 @@ public function migrate() $this->checkToken(); // Access check. - if(false) + $acl = $this->component->getAccess(); + if(!$acl->checkACL('admin', 'com_joomgallery')) { $this->setMessage(Text::_('COM_JOOMGALLERY_ERROR_MIGRATION_NOT_PERMITTED'), 'error'); $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration', false)); @@ -317,11 +320,15 @@ public function start() // Check for request forgeries $this->checkToken(); + // Get request format + $format = strtolower($this->app->getInput()->getWord('format', 'json')); + // Access check. - if(false) + $acl = $this->component->getAccess(); + if(!$acl->checkACL('admin', 'com_joomgallery')) { - $msg = Text::_('COM_JOOMGALLERY_ERROR_MIGRATION_NOT_PERMITTED'); - $this->ajaxRespond($msg); + $msg = Text::sprintf('COM_JOOMGALLERY_ERROR_TASK_NOT_PERMITTED', 'migration.start'); + $this->ajaxRespond($msg, $format); return false; } @@ -336,7 +343,7 @@ public function start() // Requested script does not exists $msg = new \Exception('Requested migration script does not exist.', 1); - $this->ajaxRespond($msg); + $this->ajaxRespond($msg, $format); return false; } @@ -347,33 +354,79 @@ public function start() { // Pre-checks not successful. Show error message. $msg = Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP2_CHECKS_FAILED'); - $this->ajaxRespond($msg); + $this->ajaxRespond($msg, $format); return false; } - $msg = 'Test..'; + // Get input params for migration + $type = $this->app->getInput()->get('type', '', 'string'); + $id = $this->app->getInput()->get('id', '', 'int'); + $json = \json_decode(\base64_decode($this->app->getInput()->get('migrateable', '', 'string')), true); + + // Check if a record id to be migrated is given + if(empty($id) || $id == 0) + { + // No record id given. Show error message. + $msg = Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_RECORD_ID_MISSING'); + $this->ajaxRespond($msg, $format); + + return false; + } + + // Attempt ot load migrateable from database + $item = $model->getItem($json->id); + + // Insert migrateable in database if not existing + if(\is_null($item->id)) + { + // Save migration record to database + $model->save($json); + + // Attempt ot load migrateable from database + $item = $model->getItem($model->getState('migration.id')); + } + + // Check out migration record if not already checked out + if(\is_null($item->checked_out) || \intval($item->checked_out) < 1) + { + // Check out record + $model->checkout($item->id); + + // Attempt ot load migrateable from database + $item = $model->getItem($model->getState('migration.id')); + } + + // Perform the migration + $msg = $model->migrate($type, $id, $item); + + // Send migration results $msg = json_encode($msg); - $this->ajaxRespond($msg); + $this->ajaxRespond($msg, $format); } /** * Returns an ajax response + * + * @param mixed $results The result to be returned + * @param string $format The format in which the result should be returned * * @return void * * @since 4.0.0 */ - protected function ajaxRespond($results) + protected function ajaxRespond($results, $format=null) { $this->app->allowCache(false); $this->app->setHeader('X-Robots-Tag', 'noindex, nofollow'); - // Requested format passed via URL - $format = strtolower($this->app->getInput()->getWord('format', '')); + if(\is_null($format)) + { + $format = strtolower($this->app->getInput()->getWord('format', 'raw')); + } // Return the results in the desired format - switch ($format) + switch($format) { // JSONinzed case 'json': @@ -410,7 +463,5 @@ protected function ajaxRespond($results) break; } - - //$this->app->close(); } } diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index e744268b..75ef05e5 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -18,8 +18,9 @@ use \Joomla\CMS\Form\Form; use \Joomla\Registry\Registry; use \Joomla\CMS\Filesystem\Folder; -use \Joomla\CMS\MVC\Model\FormModel; +use \Joomla\CMS\MVC\Model\AdminModel; use \Joomla\CMS\Language\Multilanguage; +use \Joomgallery\Component\Joomgallery\Administrator\Table\MigrationTable; use \Joomgallery\Component\Joomgallery\Administrator\Table\ImageTable; use \Joomgallery\Component\Joomgallery\Administrator\Helper\JoomHelper; @@ -29,7 +30,7 @@ * @package JoomGallery * @since 4.0.0 */ -class MigrationModel extends FormModel +class MigrationModel extends AdminModel { /** * @var string Alias to manage history control @@ -202,6 +203,37 @@ public function getMigrateables() return $this->component->getMigration()->getMigrateables(); } + /** + * Method to get a migrateable record. + * + * @param integer $pk The id of the primary key. + * + * @return CMSObject|boolean Object on success, false on failure. + * + * @since 4.0.0 + */ + public function getItem($pk = null) + { + $item = parent::getItem($pk); + + if(\property_exists($item, 'queue')) + { + $item->queue = \json_decode($item->queue); + } + + if(\property_exists($item, 'failed')) + { + $item->failed = \json_decode($item->failed); + } + + if(\property_exists($item, 'successful')) + { + $item->successful = \json_decode($item->successful, true); + } + + return $item; + } + /** * Method to get an array of migrateables based on current script. * @@ -232,7 +264,7 @@ public function getItems(): array { $this->component->setError($e->getMessage()); - return false; + return array(); } $tables = array(); @@ -250,7 +282,7 @@ public function getItems(): array { $this->component->setError($e->getMessage()); - return false; + return array(); } } @@ -339,45 +371,6 @@ public function getQueue($type, $table=null): array return $db->loadColumn(); } - /** - * Method to get a single record. - * - * @param integer|array $pk The id of the primary key or array(fieldname => value) - * - * @return mixed Object on success, false on failure. - * - * @since 4.0.0 - */ - public function getItem($pk = null) - { - $pk = (!empty($pk)) ? $pk : 0; - $table = $this->getTable(); - - if($pk > 0 || \is_array($pk)) - { - // Attempt to load the row. - $return = $table->load($pk); - - // Check for a table object error. - if($return === false) - { - // If there was no underlying error, then the false means there simply was not a row in the db for this $pk. - if(!$table->getError()) - { - $this->component->setError(Text::_('JLIB_APPLICATION_ERROR_NOT_EXIST')); - } - else - { - $this->component->setError($table->getError()); - } - - return false; - } - } - - return $table->getFieldsValues(); - } - /** * Build an SQL query to load the list data. * @@ -513,31 +506,32 @@ public function postcheck($params) /** * Method to perform one migration of one record. * - * @param string $type Name of the content type to migrate. - * @param integer $pk The primary key of the source record. - * @param array $params The migration parameters entered in the migration form + * @param string $type Name of the content type to migrate. + * @param integer $pk The primary key of the source record. + * @param MigrationTable $mig The MigrationTable object. * * @return object The object containing the migration results. * * @since 4.0.0 */ - public function migrate($type, $pk, $params) + public function migrate(string $type, int $pk, MigrationTable $mig): MigrationTable { $info = $this->getScript(); // Set the migration parameters - $this->setParams($params); + $this->setParams($mig->params); // Get record data from source $data = $this->component->getMigration()->getData($type, $pk); - // Apply data mapping based on migration parameters - $data = $this->component->getMigration()->applyDataMapping($data); + // Convert record data into structure needed for JoomGallery v4+ + $data = $this->component->getMigration()->convertData($data); - // Create new record based on data - $record = $this->insertRecord($type, $data, $params->get('same_ids')); + // Create new record based on data array + $sameIDs = \boolval($mig->params->get('source_ids', '0')); + $record = $this->insertRecord($type, $data, $sameIDs); - // Create imagetypes + // if($type === 'image') { $img_source = $this->component->getMigration()->getImageSource($data); diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index 57ec0410..522c6818 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -289,6 +289,32 @@ public function getDB(string $target): array return array($db, $dbPrefix); } + /** + * Returns the Joomla root path of the source. + * + * @return string Source Joomla root path + * + * @since 4.0.0 + */ + protected function getSourceRootPath(): string + { + if($this->params->get('same_joomla', 1)) + { + $root = Path::clean(JPATH_ROOT . '/'); + } + else + { + $root = Path::clean($this->params->get('joomla_path')); + + if(\substr($root, -1) != '/') + { + $root = Path::clean($root . '/'); + } + } + + return $root; + } + /** * Precheck: Check logfile and add check to checks array. * diff --git a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php index d2ac67bc..694b370d 100644 --- a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php +++ b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php @@ -111,16 +111,7 @@ public function getSourceXML(): \SimpleXMLElement; public function getSourceDirs(): array; /** - * Returns the Joomla root path of the source. - * - * @return string Source Joomla root path - * - * @since 4.0.0 - */ - public function getSourceRootPath(): string; - - /** - * Returns the most important info of the corresponding source table + * Returns tablename and primarykey name of the source table * * @param string $type The content type name * diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index e4fb0fb6..3b1fd70f 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -128,33 +128,7 @@ public function getSourceDirs(): array } /** - * Returns the Joomla root path of the source. - * - * @return string Source Joomla root path - * - * @since 4.0.0 - */ - public function getSourceRootPath(): string - { - if($this->params->get('same_joomla', 1)) - { - $root = Path::clean(JPATH_ROOT . '/'); - } - else - { - $root = Path::clean($this->params->get('joomla_path')); - - if(\substr($root, -1) != '/') - { - $root = Path::clean($root . '/'); - } - } - - return $root; - } - - /** - * Returns the most important info of the corresponding source table + * Returns tablename and primarykey name of the source table * * @param string $type The content type name * diff --git a/administrator/com_joomgallery/src/Table/MigrationTable.php b/administrator/com_joomgallery/src/Table/MigrationTable.php index dc467f15..a8ef0ae0 100644 --- a/administrator/com_joomgallery/src/Table/MigrationTable.php +++ b/administrator/com_joomgallery/src/Table/MigrationTable.php @@ -13,7 +13,9 @@ // No direct access defined('_JEXEC') or die; +use \Joomla\CMS\Factory; use \Joomla\CMS\Table\Table; +use \Joomla\Registry\Registry; use \Joomla\Database\DatabaseDriver; /** @@ -140,36 +142,92 @@ public function delete($pk = null) * * @return boolean True on success. * - * @see Table:bind * @since 4.0.0 - * @throws \InvalidArgumentException */ public function bind($array, $ignore = '') { + $date = Factory::getDate(); + // Support for queue field if(isset($array['queue']) && is_array($array['queue'])) { - $registry = new Registry; - $registry->loadArray($array['queue']); - $array['queue'] = (string) $registry; + $array['queue'] = \json_encode($array['queue'], JSON_UNESCAPED_UNICODE); } // Support for successful field if(isset($array['successful']) && is_array($array['successful'])) { - $registry = new Registry; - $registry->loadArray($array['successful']); - $array['successful'] = (string) $registry; + $array['successful'] = \json_encode($array['successful'], JSON_UNESCAPED_UNICODE); } // Support for failed field if(isset($array['failed']) && is_array($array['failed'])) + { + $array['failed'] = \json_encode($array['failed'], JSON_UNESCAPED_UNICODE); + } + + // Support for params field + if(isset($array['params']) && is_array($array['params'])) { $registry = new Registry; - $registry->loadArray($array['failed']); - $array['failed'] = (string) $registry; + $registry->loadArray($array['params']); + $array['params'] = (string) $registry; + } + + if($array['id'] == 0) + { + $array['created_time'] = $date->toSql(); } - return parent::bind($array, $ignore); + return parent::bind($array, array('progress', 'completed')); + } + + /** + * Method to load a row from the database by primary key and bind the fields to the Table instance properties. + * + * @param mixed $keys An optional primary key value to load the row by, or an array of fields to match. + * If not set the instance property value is used. + * @param boolean $reset True to reset the default values before loading the new row. + * + * @return boolean True if successful. False if row not found. + * + * @see Table:bind + * @since 4.0.0 + */ + public function load($keys = null, $reset = true) + { + $success = parent::load($keys, $reset); + + if($success) + { + // Support for queue field + if(isset($this->queue) && !is_array($this->queue)) + { + $this->queue = \json_decode($this->queue); + } + + // Support for successful field + if(isset($this->successful) && !is_array($this->successful)) + { + $this->successful = \json_decode($this->successful, true); + } + + // Support for failed field + if(isset($this->failed) && !is_array($this->failed)) + { + $this->failed = \json_decode($this->failed); + } + + // Calculate progress property + $this->progress = (int) \round((100 / \count($this->queue)) * (\count($this->successful) + \count($this->failed))); + + // Update completed property + if(\count($this->queue) === \count($this->successful)) + { + $this->completed = true; + } + } + + return $success; } } diff --git a/administrator/com_joomgallery/tmpl/migration/step3.php b/administrator/com_joomgallery/tmpl/migration/step3.php index 4bd72f5c..8f8311e4 100644 --- a/administrator/com_joomgallery/tmpl/migration/step3.php +++ b/administrator/com_joomgallery/tmpl/migration/step3.php @@ -73,10 +73,10 @@ Successful: successful); ?> Failed: failed); ?>
- + - +
diff --git a/media/com_joomgallery/joomla.asset.json b/media/com_joomgallery/joomla.asset.json index efe19871..01996329 100755 --- a/media/com_joomgallery/joomla.asset.json +++ b/media/com_joomgallery/joomla.asset.json @@ -141,7 +141,7 @@ { "name": "com_joomgallery.migrator", "type": "script", - "uri": "com_joomgallery/migrator.js" + "uri": "com_joomgallery/migrator/dist/migrator.js" } ] } diff --git a/media/com_joomgallery/js/migrator/dist/migrator.js b/media/com_joomgallery/js/migrator/dist/migrator.js new file mode 100644 index 00000000..84e7c745 --- /dev/null +++ b/media/com_joomgallery/js/migrator/dist/migrator.js @@ -0,0 +1,147 @@ +var Migrator; +/******/ (() => { // webpackBootstrap +/******/ "use strict"; +/******/ // The require scope +/******/ var __webpack_require__ = {}; +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ (() => { +/******/ // define __esModule on exports +/******/ __webpack_require__.r = (exports) => { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ })(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; +/*!**********************!*\ + !*** ./src/index.js ***! + \**********************/ +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ submitTask: () => (/* binding */ submitTask) +/* harmony export */ }); +// Selectors used by this script +let buttonDataSelector = 'btn-migration'; +let typeSelector = 'data-type'; +let formIdTmpl = 'migrationForm'; + +/** + * Submit a migration task + * @param {Object} event Event object + * @param {Object} element DOM element object + */ +let submitTask = function(event, element) { + event.preventDefault(); + + let type = element.getAttribute(typeSelector); + let formId = formIdTmpl + '-' + type; + let task = element.parentNode.querySelector('[name="task"]').value; + let res = performTask(formId, task); +}; + +/** + * Perform a migration task + * @param {String} formId Id of the form element + * @param {String} task Name of the task + * + * @returns {Object} Result object + * {success: true, status: 200, message: '', messages: {}, data: {}} + */ +let performTask = async function(formId, task) { + + // Catch form and data + let formData = new FormData(document.getElementById(formId)); + formData.append('format', 'json'); + + if(task == 'migration.start') { + formData.append('id', getNextMigrationID(formId)); + } + + // Set request parameters + let parameters = { + method: 'POST', + mode: 'same-origin', + cache: 'default', + redirect: 'follow', + referrerPolicy: 'no-referrer-when-downgrade', + body: formData, + }; + + // Set the url + let url = document.getElementById(formId).getAttribute('action'); + + return + + // Perform the fetch request + let response = await fetch(url, parameters); + + // Resolve promise as text string + let txt = await response.text(); + let res = null; + + if (!response.ok) { + // Catch network error + console.log(txt); + return {success: false, status: response.status, message: response.message, messages: {}, data: {error: txt}}; + } + + if(txt.startsWith('{"success"')) { + // Response is of type json --> everything fine + res = JSON.parse(txt); + res.status = response.status; + res.data = JSON.parse(res.data); + } else if (txt.includes('Fatal error')) { + // PHP fatal error occurred + res = {success: false, status: response.status, message: response.statusText, messages: {}, data: {error: txt}}; + } else { + // Response is not of type json --> probably some php warnings/notices + let split = txt.split('\n{"'); + let temp = JSON.parse('{"'+split[1]); + let data = JSON.parse(temp.data); + res = {success: true, status: response.status, message: split[0], messages: temp.messages, data: data}; + } + + return res; +} + +/** + * Perform a migration task + * @param {String} formId Id of the form element + * + * @returns {String} Id of the database record to be migrated + */ +let getNextMigrationID = function(formId) { + let type = formId.replace(formIdTmpl + '-', ''); + let form = document.getElementById(formId); + + let migrateable = atob(form.querySelector('[name="migrateable"]').value); + migrateable = JSON.parse(migrateable); + + console.log(migrateable); +} +Migrator = __webpack_exports__; +/******/ })() +; +//# sourceMappingURL=migrator.js.map \ No newline at end of file diff --git a/media/com_joomgallery/js/migrator/dist/migrator.js.map b/media/com_joomgallery/js/migrator/dist/migrator.js.map new file mode 100644 index 00000000..23e31eb0 --- /dev/null +++ b/media/com_joomgallery/js/migrator/dist/migrator.js.map @@ -0,0 +1 @@ +{"version":3,"file":"migrator.js","mappings":";;;UAAA;UACA;;;;;WCDA;WACA;WACA;WACA;WACA,yCAAyC,wCAAwC;WACjF;WACA;WACA;;;;;WCPA;;;;;WCAA;WACA;WACA;WACA,uDAAuD,iBAAiB;WACxE;WACA,gDAAgD,aAAa;WAC7D;;;;;;;;;;;;ACNA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB;AACA,aAAa,UAAU;AACvB,aAAa,qDAAqD;AAClE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,gFAAgF,SAAS;AACrG;AACA;AACA,sBAAsB;AACtB;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA,WAAW,mFAAmF,SAAS;AACvG,IAAI;AACJ;AACA,8BAA8B;AAC9B,6BAA6B;AAC7B;AACA,WAAW;AACX;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB;AACA,aAAa,UAAU;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,C","sources":["webpack://Migrator/webpack/bootstrap","webpack://Migrator/webpack/runtime/define property getters","webpack://Migrator/webpack/runtime/hasOwnProperty shorthand","webpack://Migrator/webpack/runtime/make namespace object","webpack://Migrator/./src/index.js"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","// Selectors used by this script\r\nlet buttonDataSelector = 'btn-migration';\r\nlet typeSelector = 'data-type';\r\nlet formIdTmpl = 'migrationForm';\r\n\r\n/**\r\n * Submit a migration task\r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let submitTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n let type = element.getAttribute(typeSelector);\r\n let formId = formIdTmpl + '-' + type;\r\n let task = element.parentNode.querySelector('[name=\"task\"]').value;\r\n let res = performTask(formId, task);\r\n};\r\n\r\n/**\r\n * Perform a migration task\r\n * @param {String} formId Id of the form element\r\n * @param {String} task Name of the task\r\n * \r\n * @returns {Object} Result object\r\n * {success: true, status: 200, message: '', messages: {}, data: {}}\r\n */\r\nlet performTask = async function(formId, task) {\r\n\r\n // Catch form and data\r\n let formData = new FormData(document.getElementById(formId));\r\n formData.append('format', 'json');\r\n\r\n if(task == 'migration.start') {\r\n formData.append('id', getNextMigrationID(formId));\r\n }\r\n\r\n // Set request parameters\r\n let parameters = {\r\n method: 'POST',\r\n mode: 'same-origin',\r\n cache: 'default',\r\n redirect: 'follow',\r\n referrerPolicy: 'no-referrer-when-downgrade',\r\n body: formData,\r\n };\r\n\r\n // Set the url\r\n let url = document.getElementById(formId).getAttribute('action');\r\n\r\n return\r\n\r\n // Perform the fetch request\r\n let response = await fetch(url, parameters);\r\n\r\n // Resolve promise as text string\r\n let txt = await response.text();\r\n let res = null;\r\n\r\n if (!response.ok) {\r\n // Catch network error\r\n console.log(txt);\r\n return {success: false, status: response.status, message: response.message, messages: {}, data: {error: txt}};\r\n }\r\n\r\n if(txt.startsWith('{\"success\"')) {\r\n // Response is of type json --> everything fine\r\n res = JSON.parse(txt);\r\n res.status = response.status;\r\n res.data = JSON.parse(res.data);\r\n } else if (txt.includes('Fatal error')) {\r\n // PHP fatal error occurred\r\n res = {success: false, status: response.status, message: response.statusText, messages: {}, data: {error: txt}};\r\n } else {\r\n // Response is not of type json --> probably some php warnings/notices\r\n let split = txt.split('\\n{\"');\r\n let temp = JSON.parse('{\"'+split[1]);\r\n let data = JSON.parse(temp.data);\r\n res = {success: true, status: response.status, message: split[0], messages: temp.messages, data: data};\r\n }\r\n\r\n return res;\r\n}\r\n\r\n/**\r\n * Perform a migration task\r\n * @param {String} formId Id of the form element\r\n * \r\n * @returns {String} Id of the database record to be migrated\r\n */\r\nlet getNextMigrationID = function(formId) {\r\n let type = formId.replace(formIdTmpl + '-', '');\r\n let form = document.getElementById(formId);\r\n\r\n let migrateable = atob(form.querySelector('[name=\"migrateable\"]').value);\r\n migrateable = JSON.parse(migrateable);\r\n\r\n console.log(migrateable);\r\n}"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/media/com_joomgallery/js/migrator/package-lock.json b/media/com_joomgallery/js/migrator/package-lock.json new file mode 100644 index 00000000..677c63ed --- /dev/null +++ b/media/com_joomgallery/js/migrator/package-lock.json @@ -0,0 +1,1371 @@ +{ + "name": "migrator", + "version": "4.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "migrator", + "version": "4.0.0", + "license": "GNU", + "devDependencies": { + "i": "^0.3.7", + "webpack": "^5.80.0", + "webpack-cli": "^5.0.2" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", + "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@types/eslint": { + "version": "8.44.6", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.6.tgz", + "integrity": "sha512-P6bY56TVmX8y9J87jHNgQh43h6VVU+6H7oN7hgvivV81K2XY8qJZ5vqPy/HdUoVIelii2kChYVzQanlswPWVFw==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.6", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.6.tgz", + "integrity": "sha512-zfM4ipmxVKWdxtDaJ3MP3pBurDXOCoyjvlpE3u6Qzrmw4BPbfm4/ambIeTk/r/J0iq/+2/xp0Fmt+gFvXJY2PQ==", + "dev": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.3.tgz", + "integrity": "sha512-CS2rOaoQ/eAgAfcTfq6amKG7bsN+EMcgGY4FAFQdvSj2y1ixvOZTUA9mOtCai7E1SYu283XNw7urKK30nP3wkQ==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz", + "integrity": "sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.8.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.9.tgz", + "integrity": "sha512-UzykFsT3FhHb1h7yD4CA4YhBHq545JC0YnEz41xkipN88eKQtL6rSgocL5tbAP6Ola9Izm/Aw4Ora8He4x0BHg==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", + "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", + "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "dev": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", + "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", + "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-opt": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6", + "@webassemblyjs/wast-printer": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", + "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", + "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", + "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", + "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webpack-cli/configtest": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", + "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", + "dev": true, + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + } + }, + "node_modules/@webpack-cli/info": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", + "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", + "dev": true, + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + } + }, + "node_modules/@webpack-cli/serve": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", + "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", + "dev": true, + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "dev": true, + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/browserslist": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", + "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001541", + "electron-to-chromium": "^1.4.535", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001557", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001557.tgz", + "integrity": "sha512-91oR7hLNUP3gG6MLU+n96em322a8Xzes8wWdBKhLgUoiJsAF5irZnxSUCbc+qUZXNnPCfUwLOi9ZCZpkvjQajw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.569", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.569.tgz", + "integrity": "sha512-LsrJjZ0IbVy12ApW3gpYpcmHS3iRxH4bkKOW98y1/D+3cvDUWGcbzbsFinfUS8knpcZk/PG/2p/RnkMCYN7PVg==", + "dev": true + }, + "node_modules/enhanced-resolve": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", + "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/envinfo": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.10.0.tgz", + "integrity": "sha512-ZtUjZO6l5mwTHvc1L9+1q5p/R3wTopcfqMW8r5t8SJSKqeVI/LtajORwRFEKpEFuekjD0VBjwu1HMxL4UalIRw==", + "dev": true, + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.1.tgz", + "integrity": "sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/i": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/i/-/i-0.3.7.tgz", + "integrity": "sha512-FYz4wlXgkQwIPqhzC5TdNMLSE5+GS1IIDJZY/1ZiEPCT2S3COUVZeT5OW4BmW4r5LHLQuOosSwsvnroG9GR59Q==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/interpret": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "dev": true + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "dev": true, + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.22.0.tgz", + "integrity": "sha512-hHZVLgRA2z4NWcN6aS5rQDc+7Dcy58HOf2zbYwmFcQ+ua3h6eEFf5lIDKTzbWwlazPyOZsFQO8V80/IjVNExEw==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", + "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.17", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.16.8" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "dev": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack": { + "version": "5.89.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz", + "integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.0", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.9.0", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.15.0", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.7", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-cli": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", + "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", + "dev": true, + "dependencies": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^2.1.1", + "@webpack-cli/info": "^2.0.2", + "@webpack-cli/serve": "^2.0.5", + "colorette": "^2.0.14", + "commander": "^10.0.1", + "cross-spawn": "^7.0.3", + "envinfo": "^7.7.3", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", + "webpack-merge": "^5.7.3" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "5.x.x" + }, + "peerDependenciesMeta": { + "@webpack-cli/generators": { + "optional": true + }, + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/webpack-cli/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/webpack-merge": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", + "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", + "dev": true + } + } +} diff --git a/media/com_joomgallery/js/migrator/package.json b/media/com_joomgallery/js/migrator/package.json new file mode 100644 index 00000000..f4374b18 --- /dev/null +++ b/media/com_joomgallery/js/migrator/package.json @@ -0,0 +1,18 @@ +{ + "name": "migrator", + "version": "4.0.0", + "description": "JavaScript app to handle migration process", + "main": "index.js", + "scripts": { + "build": "webpack" + }, + "keywords": [], + "author": "JoomGallery::ProjectTeam", + "license": "GNU", + "devDependencies": { + "webpack": "^5.80.0", + "webpack-cli": "^5.0.2" + }, + "dependencies": { + } +} diff --git a/media/com_joomgallery/js/migrator.js b/media/com_joomgallery/js/migrator/src/index.js similarity index 54% rename from media/com_joomgallery/js/migrator.js rename to media/com_joomgallery/js/migrator/src/index.js index a865be0c..1c58382e 100644 --- a/media/com_joomgallery/js/migrator.js +++ b/media/com_joomgallery/js/migrator/src/index.js @@ -1,33 +1,45 @@ // Selectors used by this script -let buttonDataSelector = 'btn-migration'; -let typeSelector = 'data-type'; -let formIdTmpl = 'migrationForm'; +let typeSelector = 'data-type'; +let formIdTmpl = 'migrationForm'; + +/** + * Storage for migrateables + * @var {Object} migrateablesList + */ +let migrateablesList = {}; /** * Submit a migration task - * @param task + * @param {Object} event Event object + * @param {Object} element DOM element object */ -let submitTask = function(event, element) { +export let submitTask = function(event, element) { event.preventDefault(); let type = element.getAttribute(typeSelector); - let formId = formIdTmpl + '-' + type; - let res = performTask(formId); + let formId = formIdTmpl + '-' + type; + let task = element.parentNode.querySelector('[name="task"]').value; + let res = performTask(formId, task); }; /** * Perform a migration task - * @param task + * @param {String} formId Id of the form element + * @param {String} task Name of the task * - * @return json string + * @returns {Object} Result object * {success: true, status: 200, message: '', messages: {}, data: {}} */ -let performTask = async function(formId) { +let performTask = async function(formId, task) { // Catch form and data let formData = new FormData(document.getElementById(formId)); formData.append('format', 'json'); + if(task == 'migration.start') { + formData.append('id', getNextMigrationID(formId)); + } + // Set request parameters let parameters = { method: 'POST', @@ -72,3 +84,30 @@ let performTask = async function(formId) { return res; } + +/** + * Perform a migration task + * @param {String} formId Id of the form element + * + * @returns {String} Id of the database record to be migrated + */ +let getNextMigrationID = function(formId) { + let type = formId.replace(formIdTmpl + '-', ''); + let form = document.getElementById(formId); + + let migrateable = atob(form.querySelector('[name="migrateable"]').value); + migrateable = JSON.parse(migrateable); + + // Overwrite migrateable in list + migrateablesList[type] = migrateable; + + // Loop through queue + migrateable.queue.forEach(function(id, i) { + if(id in migrateable.successful || id in migrateable.failed) { + migrateablesList[type]['currentID'] = id; + return; + } + }); + + return migrateablesList[type]['currentID']; +} \ No newline at end of file diff --git a/media/com_joomgallery/js/migrator/webpack.config.js b/media/com_joomgallery/js/migrator/webpack.config.js new file mode 100644 index 00000000..f49c3318 --- /dev/null +++ b/media/com_joomgallery/js/migrator/webpack.config.js @@ -0,0 +1,15 @@ +const path = require('path'); + +module.exports = { + target: 'web', + //mode: 'production', + mode: 'development', + entry: './src/index.js', + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'migrator.js', + library: 'Migrator', + libraryTarget: 'var' + }, + devtool: 'source-map', +} From 00ae807a34df137d31abc267757b68fbfa11c0c1 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 30 Oct 2023 19:22:22 +0100 Subject: [PATCH 023/121] add proper ajax request structure --- .../language/en-GB/com_joomgallery.ini | 1 + .../src/Controller/MigrationController.php | 195 ++++++++++++++++-- .../src/Model/MigrationModel.php | 142 ++++++++----- .../src/Table/MigrationTable.php | 42 +--- .../js/migrator/dist/migrator.js | 65 ++++-- .../js/migrator/dist/migrator.js.map | 2 +- .../com_joomgallery/js/migrator/src/index.js | 43 +++- 7 files changed, 359 insertions(+), 131 deletions(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index b13efaec..3b70cbbd 100755 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -691,6 +691,7 @@ COM_JOOMGALLERY_SERVICE_MIGRATION_MAPPING_DEST_IMAGETYPE_NOT_EXIST="The destinat COM_JOOMGALLERY_SERVICE_MIGRATION_MAPPING_SRC_IMAGETYPE_NOT_EXIST="The source imagetype '%s' does not exist. Please go back to step 1 and apply the mapping correctly." COM_JOOMGALLERY_SERVICE_MIGRATION_MAPPING_IMAGETYPE_NOT_USED="The following destination imagetypes are not used in the mapping: '%s'. Please go back to step 1 and apply the mapping correctly." COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_RECORD_ID_MISSING="Starting a migration without source data ID not possible. Contact the developer of the migration script to solve this." +COM_JOOMGALLERY_SERVICE_ERROR_MIGRATION_RESUME="Migration can not be resumed. There is a problem in fetching the required info." ;Plugin events COM_JOOMGALLERY_PLUGIN_ERROR_RETURN_VALUE="Return value of the plugin event '%s' must be of type %s and contain : %s" diff --git a/administrator/com_joomgallery/src/Controller/MigrationController.php b/administrator/com_joomgallery/src/Controller/MigrationController.php index 8fbaee98..738ecf0d 100644 --- a/administrator/com_joomgallery/src/Controller/MigrationController.php +++ b/administrator/com_joomgallery/src/Controller/MigrationController.php @@ -27,6 +27,7 @@ use \Joomla\CMS\Form\FormFactoryInterface; use \Joomla\CMS\MVC\Factory\MVCFactoryInterface; use \Joomgallery\Component\Joomgallery\Administrator\Extension\JoomgalleryComponent; +use stdClass; /** * Migration controller class. @@ -114,7 +115,7 @@ public function getModel($name = 'Migration', $prefix = 'Administrator', $config /** * Method to cancel a migration. * - * @return boolean True if access level checks pass, false otherwise. + * @return boolean True on success, false otherwise * * @since 4.0.0 */ @@ -133,6 +134,16 @@ public function cancel() throw new Exception('Requested migration script does not exist.', 1); } + // Access check. + $acl = $this->component->getAccess(); + if(!$acl->checkACL('admin', 'com_joomgallery')) + { + $this->setMessage(Text::sprintf('COM_JOOMGALLERY_ERROR_TASK_NOT_PERMITTED', 'migration.start'), 'error'); + $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration', false)); + + return false; + } + // Clean the session data and redirect. $this->app->setUserState(_JOOM_OPTION.'.migration.script', null); $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.params', null); @@ -150,6 +161,127 @@ public function cancel() return true; } + /** + * Method to resume a previously paused or canceled migration. + * + * @return boolean True on success, false otherwise + * + * @since 4.0.0 + */ + public function resume() + { + $this->checkToken(); + + $model = $this->getModel(); + $script = $this->app->getUserStateFromRequest(_JOOM_OPTION.'.migration.script', 'script', '', 'cmd'); + $scripts = $model->getScripts(); + + // Check if requested script exists + if(!\in_array($script, \array_keys($scripts))) + { + // Requested script does not exists + throw new Exception('Requested migration script does not exist.', 1); + } + + // Access check. + $acl = $this->component->getAccess(); + if(!$acl->checkACL('admin', 'com_joomgallery')) + { + $this->setMessage(Text::sprintf('COM_JOOMGALLERY_ERROR_TASK_NOT_PERMITTED', 'migration.start'), 'error'); + $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration', false)); + + return false; + } + + // Get item to resume. + $id = $this->input->get('resume_id', 0, 'int'); + if($id < 1) + { + $this->setMessage(Text::_('COM_JOOMGALLERY_SERVICE_ERROR_MIGRATION_RESUME'), 'error'); + $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration', false)); + + return false; + } + + // Attempt to load the migration items + $item = $model->getItem($id); + if(!$item || $item->script != $script) + { + $this->setMessage(Text::_('COM_JOOMGALLERY_SERVICE_ERROR_MIGRATION_RESUME'), 'error'); + $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration', false)); + + return false; + } + + // Set params data to user state + $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.params', $item->params); + + // Redirect to the from screen (step 1). + $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration&layout=step1', false)); + + return true; + } + + /** + * Method to remove one or more item from database. + * + * @return boolean True on success, false otherwise + * + * @since 4.0.0 + */ + public function delete() + { + $this->checkToken(); + + $model = $this->getModel(); + $script = $this->app->getUserStateFromRequest(_JOOM_OPTION.'.migration.script', 'script', '', 'cmd'); + $scripts = $model->getScripts(); + + // Check if requested script exists + if(!\in_array($script, \array_keys($scripts))) + { + // Requested script does not exists + throw new Exception('Requested migration script does not exist.', 1); + } + + // Access check. + $acl = $this->component->getAccess(); + if(!$acl->checkACL('admin', 'com_joomgallery')) + { + $this->setMessage(Text::sprintf('COM_JOOMGALLERY_ERROR_TASK_NOT_PERMITTED', 'migration.start'), 'error'); + $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration', false)); + + return false; + } + + // Get items to remove from the request. + $cid = (array) $this->input->get('cid', [], 'int'); + + // Remove zero values resulting from input filter + $cid = array_filter($cid); + + if(!empty($cid)) + { + // Get the model. + $model = $this->getModel(); + + // Remove the items. + if($model->delete($cid)) + { + $this->app->enqueueMessage(Text::plural($this->text_prefix . '_N_ITEMS_DELETED', \count($cid))); + } + else + { + $this->app->enqueueMessage($model->getError(), 'error'); + } + } + + // Redirect to the list screen. + $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration', false)); + + return true; + } + /** * Step 2 * Validate the form input data and perform the pre migration checks. @@ -183,7 +315,7 @@ public function precheck() $acl = $this->component->getAccess(); if(!$acl->checkACL('admin', 'com_joomgallery')) { - $this->setMessage(Text::_('COM_JOOMGALLERY_ERROR_MIGRATION_NOT_PERMITTED'), 'error'); + $this->setMessage(Text::sprintf('COM_JOOMGALLERY_ERROR_TASK_NOT_PERMITTED', 'migration.start'), 'error'); $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration', false)); return false; @@ -275,7 +407,7 @@ public function migrate() $acl = $this->component->getAccess(); if(!$acl->checkACL('admin', 'com_joomgallery')) { - $this->setMessage(Text::_('COM_JOOMGALLERY_ERROR_MIGRATION_NOT_PERMITTED'), 'error'); + $this->setMessage(Text::sprintf('COM_JOOMGALLERY_ERROR_TASK_NOT_PERMITTED', 'migration.start'), 'error'); $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration', false)); return false; @@ -310,6 +442,7 @@ public function migrate() /** * Perform a migration + * Called by Ajax requests * * @return void * @@ -327,8 +460,8 @@ public function start() $acl = $this->component->getAccess(); if(!$acl->checkACL('admin', 'com_joomgallery')) { - $msg = Text::sprintf('COM_JOOMGALLERY_ERROR_TASK_NOT_PERMITTED', 'migration.start'); - $this->ajaxRespond($msg, $format); + $response = $this->createRespond(null, false, Text::sprintf('COM_JOOMGALLERY_ERROR_TASK_NOT_PERMITTED', 'migration.start')); + $this->ajaxRespond($response, $format); return false; } @@ -341,9 +474,8 @@ public function start() if(!\in_array($script, \array_keys($scripts))) { // Requested script does not exists - $msg = new \Exception('Requested migration script does not exist.', 1); - - $this->ajaxRespond($msg, $format); + $response = $this->createRespond(null, false, 'Requested migration script does not exist.'); + $this->ajaxRespond($response, $format); return false; } @@ -353,8 +485,8 @@ public function start() if(!$precheck) { // Pre-checks not successful. Show error message. - $msg = Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP2_CHECKS_FAILED'); - $this->ajaxRespond($msg, $format); + $response = $this->createRespond(null, false, Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP2_CHECKS_FAILED')); + $this->ajaxRespond($response, $format); return false; } @@ -368,22 +500,22 @@ public function start() if(empty($id) || $id == 0) { // No record id given. Show error message. - $msg = Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_RECORD_ID_MISSING'); - $this->ajaxRespond($msg, $format); + $response = $this->createRespond(null, false, Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_RECORD_ID_MISSING')); + $this->ajaxRespond($response, $format); return false; } - // Attempt ot load migrateable from database + // Attempt to load migration record from database $item = $model->getItem($json->id); - // Insert migrateable in database if not existing if(\is_null($item->id)) { + // It seems that the migration record does not yet exists in the database // Save migration record to database $model->save($json); - // Attempt ot load migrateable from database + // Attempt to load migration record from database $item = $model->getItem($model->getState('migration.id')); } @@ -394,15 +526,38 @@ public function start() $model->checkout($item->id); // Attempt ot load migrateable from database - $item = $model->getItem($model->getState('migration.id')); - } + $item = $model->getItem($item->id); + } // Perform the migration - $msg = $model->migrate($type, $id, $item); + $data = $model->migrate($type, $id, $item); // Send migration results - $msg = json_encode($msg); - $this->ajaxRespond($msg, $format); + $response = $this->createRespond($data, true); + $this->ajaxRespond($response, $format); + } + + /** + * Create a response object + * {success: bool, message: string, data: mixed} + * + * @param mixed $data The data returned to the frontend + * @param bool $success True if everything was good, false otherwise + * @param string $message A message to be printed in the frontend + * + * @return string Response json string + * + * @since 4.0.0 + */ + protected function createRespond($data, bool $success = true, string $message = ''): string + { + $obj = new stdClass; + + $obj->success = $success; + $obj->data = $data; + $obj->message = $message; + + return \json_encode($obj, JSON_UNESCAPED_UNICODE); } /** diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index 75ef05e5..c9521e71 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -64,6 +64,15 @@ class MigrationModel extends AdminModel */ protected $scriptName = ''; + /** + * Temporary storage of type name. + * + * @var string + * + * @since 4.0.0 + */ + protected $tmp_type = null; + /** * Constructor * @@ -218,19 +227,73 @@ public function getItem($pk = null) if(\property_exists($item, 'queue')) { - $item->queue = \json_decode($item->queue); + $registry = new Registry($item->queue); + $item->queue = $registry->toArray(); + } + + if(\property_exists($item, 'successful')) + { + $registry = new Registry($item->successful); + $item->successful = $registry; + //$item->successful = $registry->toArray(); } if(\property_exists($item, 'failed')) { - $item->failed = \json_decode($item->failed); + $registry = new Registry($item->failed); + $item->failed = $registry; + //$item->failed = $registry->toArray(); } - if(\property_exists($item, 'successful')) + // Add script if empty + if(empty($item->script)) + { + $item->script = $this->scriptName; + } + + // We can not go further without knowledge about the type + if(\is_null($this->tmp_type)) + { + return $item; + } + else + { + $type = $this->tmp_type; + } + + // Add type if empty + if(empty($item->type)) + { + $item->type = $type; + } + + // Add source and destination table info if empty + if(empty($item->src_table)) + { + // Get table information + list($src_table, $src_pk) = $this->component->getMigration()->getSourceTableInfo($type); + $item->src_table = $src_table; + $item->src_pk = $src_pk; + $item->dst_table = JoomHelper::$content_types[$type]; + $item->dst_pk = 'id'; + } + + // Add queue if empty + if(\is_null($item->queue) || empty($item->queue)) { - $item->successful = \json_decode($item->successful, true); + // Load queue + $item->queue = $this->getQueue($type, $item); } + // Add params + if($this->component->getMigration()->get('params')) + { + $item->params = $this->component->getMigration()->get('params'); + } + + // Empty type storage + $this->tmp_type = null; + return $item; } @@ -258,7 +321,7 @@ public function getItems(): array } $db->setQuery($query); - $items = $db->loadObjectList('type'); + $tables = $db->loadObjectList('type'); } catch (\RuntimeException $e) { @@ -267,72 +330,45 @@ public function getItems(): array return array(); } - $tables = array(); + $items = array(); foreach($types as $key => $type) { - $table = $this->getTable(); + // Fill type storage + $this->tmp_type = $type; - if(!empty($items) && \key_exists($type, $items)) + if(!empty($tables) && \key_exists($type, $tables)) { - // Attempt to load the row. - $return = $table->load($items[$type]->id); - - // Check for a table object error. - if($return === false) - { - $this->component->setError($e->getMessage()); - - return array(); - } + // Load item based on id. + $item = $this->getItem($tables[$type]->id); } - - // Add script if empty - if(empty($table->script)) - { - $table->script = $this->scriptName; - } - - // Add type if empty - if(empty($table->type)) + else { - $table->type = $type; + // Load empty item. + $item = $this->getItem(0); } - // Add source and destination table info if empty - if(empty($table->src_table)) - { - // Get table information - list($src_table, $src_pk) = $this->component->getMigration()->getSourceTableInfo($type); - $table->src_table = $src_table; - $table->src_pk = $src_pk; - $table->dst_table = JoomHelper::$content_types[$type]; - $table->dst_pk = 'id'; - } + // Empty type storage + $this->tmp_type = null; - // Add queue if empty - if(\is_null($table->queue) || empty($table->queue)) + // Check for a table object error. + if($item === false) { - // Load queue - $table->queue = $this->getQueue($type, $table); - } + $this->component->setError($e->getMessage()); - // Add params - if($this->component->getMigration()->get('params')) - { - $table->params = $this->component->getMigration()->get('params'); + return array(); } - array_push($tables, $table); + array_push($items, $item); } - return $tables; + return $items; } - /** + /** * Load the current queue of ids from table * - * @param string $type Content type - * @param MigrationTable $table Table object + * @param string $type Content type + * @param object $table Object containing migration item properties * * @return array * @@ -531,7 +567,7 @@ public function migrate(string $type, int $pk, MigrationTable $mig): MigrationTa $sameIDs = \boolval($mig->params->get('source_ids', '0')); $record = $this->insertRecord($type, $data, $sameIDs); - // + // Recreate images if($type === 'image') { $img_source = $this->component->getMigration()->getImageSource($data); diff --git a/administrator/com_joomgallery/src/Table/MigrationTable.php b/administrator/com_joomgallery/src/Table/MigrationTable.php index a8ef0ae0..210614fc 100644 --- a/administrator/com_joomgallery/src/Table/MigrationTable.php +++ b/administrator/com_joomgallery/src/Table/MigrationTable.php @@ -91,34 +91,6 @@ public function store($updateNulls = true) return parent::store($updateNulls); } - /** - * Overloaded check function - * - * @return bool - */ - public function check() - { - // Support for subform field queue - if(is_array($this->queue)) - { - $this->queue = json_encode($this->queue, JSON_UNESCAPED_UNICODE); - } - - // Support for subform field successful - if(is_array($this->successful)) - { - $this->successful = json_encode($this->successful, JSON_UNESCAPED_UNICODE); - } - - // Support for subform field failed - if(is_array($this->failed)) - { - $this->failed = json_encode($this->failed, JSON_UNESCAPED_UNICODE); - } - - return parent::check(); - } - /** * Delete a record by id * @@ -157,13 +129,17 @@ public function bind($array, $ignore = '') // Support for successful field if(isset($array['successful']) && is_array($array['successful'])) { - $array['successful'] = \json_encode($array['successful'], JSON_UNESCAPED_UNICODE); + $registry = new Registry; + $registry->loadArray($array['successful']); + $array['successful'] = (string) $registry; } // Support for failed field if(isset($array['failed']) && is_array($array['failed'])) { - $array['failed'] = \json_encode($array['failed'], JSON_UNESCAPED_UNICODE); + $registry = new Registry; + $registry->loadArray($array['failed']); + $array['failed'] = (string) $registry; } // Support for params field @@ -209,7 +185,7 @@ public function load($keys = null, $reset = true) // Support for successful field if(isset($this->successful) && !is_array($this->successful)) { - $this->successful = \json_decode($this->successful, true); + $this->successful = \json_decode($this->successful); } // Support for failed field @@ -225,8 +201,8 @@ public function load($keys = null, $reset = true) if(\count($this->queue) === \count($this->successful)) { $this->completed = true; - } - } + } + } return $success; } diff --git a/media/com_joomgallery/js/migrator/dist/migrator.js b/media/com_joomgallery/js/migrator/dist/migrator.js index 84e7c745..a332b218 100644 --- a/media/com_joomgallery/js/migrator/dist/migrator.js +++ b/media/com_joomgallery/js/migrator/dist/migrator.js @@ -43,12 +43,18 @@ __webpack_require__.r(__webpack_exports__); /* harmony export */ submitTask: () => (/* binding */ submitTask) /* harmony export */ }); // Selectors used by this script -let buttonDataSelector = 'btn-migration'; -let typeSelector = 'data-type'; -let formIdTmpl = 'migrationForm'; +let typeSelector = 'data-type'; +let formIdTmpl = 'migrationForm'; + +/** + * Storage for migrateables + * @var {Object} migrateablesList + */ +let migrateablesList = {}; /** * Submit a migration task + * * @param {Object} event Event object * @param {Object} element DOM element object */ @@ -58,18 +64,28 @@ let submitTask = function(event, element) { let type = element.getAttribute(typeSelector); let formId = formIdTmpl + '-' + type; let task = element.parentNode.querySelector('[name="task"]').value; - let res = performTask(formId, task); + + ajax(formId, task) + .then(res => { + // Handle the successful result here + responseHandler(res); + }) + .catch(error => { + // Handle any errors here + console.error(error); + }); }; /** - * Perform a migration task + * Perform an ajax request in json format + * * @param {String} formId Id of the form element * @param {String} task Name of the task * * @returns {Object} Result object - * {success: true, status: 200, message: '', messages: {}, data: {}} + * {success: true, status: 200, message: '', messages: {}, data: { {success: bool, message: string, data: mixed} }} */ -let performTask = async function(formId, task) { +let ajax = async function(formId, task) { // Catch form and data let formData = new FormData(document.getElementById(formId)); @@ -92,8 +108,6 @@ let performTask = async function(formId, task) { // Set the url let url = document.getElementById(formId).getAttribute('action'); - return - // Perform the fetch request let response = await fetch(url, parameters); @@ -104,7 +118,7 @@ let performTask = async function(formId, task) { if (!response.ok) { // Catch network error console.log(txt); - return {success: false, status: response.status, message: response.message, messages: {}, data: {error: txt}}; + return {success: false, status: response.status, message: response.message, messages: {}, data: {message: txt}}; } if(txt.startsWith('{"success"')) { @@ -114,7 +128,7 @@ let performTask = async function(formId, task) { res.data = JSON.parse(res.data); } else if (txt.includes('Fatal error')) { // PHP fatal error occurred - res = {success: false, status: response.status, message: response.statusText, messages: {}, data: {error: txt}}; + res = {success: false, status: response.status, message: response.statusText, messages: {}, data: {message: txt}}; } else { // Response is not of type json --> probably some php warnings/notices let split = txt.split('\n{"'); @@ -133,13 +147,36 @@ let performTask = async function(formId, task) { * @returns {String} Id of the database record to be migrated */ let getNextMigrationID = function(formId) { - let type = formId.replace(formIdTmpl + '-', ''); - let form = document.getElementById(formId); + let type = formId.replace(formIdTmpl + '-', ''); + let form = document.getElementById(formId); let migrateable = atob(form.querySelector('[name="migrateable"]').value); migrateable = JSON.parse(migrateable); - console.log(migrateable); + // Overwrite migrateable in list + migrateablesList[type] = migrateable; + + // Loop through queue + for (let id of migrateable.queue) { + if (!(id in migrateable.successful) && !(id in migrateable.failed)) { + migrateablesList[type]['currentID'] = id; + break; + } + } + + return migrateablesList[type]['currentID']; +} + +/** + * Handle migration response + * + * @param {Object} response The response object in the form of + * {success: true, status: 200, message: '', messages: {}, data: { {success: bool, message: string, data: mixed} }} + * + * @returns void + */ +let responseHandler = function(response) { + console.log(response); } Migrator = __webpack_exports__; /******/ })() diff --git a/media/com_joomgallery/js/migrator/dist/migrator.js.map b/media/com_joomgallery/js/migrator/dist/migrator.js.map index 23e31eb0..21b02350 100644 --- a/media/com_joomgallery/js/migrator/dist/migrator.js.map +++ b/media/com_joomgallery/js/migrator/dist/migrator.js.map @@ -1 +1 @@ -{"version":3,"file":"migrator.js","mappings":";;;UAAA;UACA;;;;;WCDA;WACA;WACA;WACA;WACA,yCAAyC,wCAAwC;WACjF;WACA;WACA;;;;;WCPA;;;;;WCAA;WACA;WACA;WACA,uDAAuD,iBAAiB;WACxE;WACA,gDAAgD,aAAa;WAC7D;;;;;;;;;;;;ACNA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB;AACA,aAAa,UAAU;AACvB,aAAa,qDAAqD;AAClE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,gFAAgF,SAAS;AACrG;AACA;AACA,sBAAsB;AACtB;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA,WAAW,mFAAmF,SAAS;AACvG,IAAI;AACJ;AACA,8BAA8B;AAC9B,6BAA6B;AAC7B;AACA,WAAW;AACX;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB;AACA,aAAa,UAAU;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,C","sources":["webpack://Migrator/webpack/bootstrap","webpack://Migrator/webpack/runtime/define property getters","webpack://Migrator/webpack/runtime/hasOwnProperty shorthand","webpack://Migrator/webpack/runtime/make namespace object","webpack://Migrator/./src/index.js"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","// Selectors used by this script\r\nlet buttonDataSelector = 'btn-migration';\r\nlet typeSelector = 'data-type';\r\nlet formIdTmpl = 'migrationForm';\r\n\r\n/**\r\n * Submit a migration task\r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let submitTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n let type = element.getAttribute(typeSelector);\r\n let formId = formIdTmpl + '-' + type;\r\n let task = element.parentNode.querySelector('[name=\"task\"]').value;\r\n let res = performTask(formId, task);\r\n};\r\n\r\n/**\r\n * Perform a migration task\r\n * @param {String} formId Id of the form element\r\n * @param {String} task Name of the task\r\n * \r\n * @returns {Object} Result object\r\n * {success: true, status: 200, message: '', messages: {}, data: {}}\r\n */\r\nlet performTask = async function(formId, task) {\r\n\r\n // Catch form and data\r\n let formData = new FormData(document.getElementById(formId));\r\n formData.append('format', 'json');\r\n\r\n if(task == 'migration.start') {\r\n formData.append('id', getNextMigrationID(formId));\r\n }\r\n\r\n // Set request parameters\r\n let parameters = {\r\n method: 'POST',\r\n mode: 'same-origin',\r\n cache: 'default',\r\n redirect: 'follow',\r\n referrerPolicy: 'no-referrer-when-downgrade',\r\n body: formData,\r\n };\r\n\r\n // Set the url\r\n let url = document.getElementById(formId).getAttribute('action');\r\n\r\n return\r\n\r\n // Perform the fetch request\r\n let response = await fetch(url, parameters);\r\n\r\n // Resolve promise as text string\r\n let txt = await response.text();\r\n let res = null;\r\n\r\n if (!response.ok) {\r\n // Catch network error\r\n console.log(txt);\r\n return {success: false, status: response.status, message: response.message, messages: {}, data: {error: txt}};\r\n }\r\n\r\n if(txt.startsWith('{\"success\"')) {\r\n // Response is of type json --> everything fine\r\n res = JSON.parse(txt);\r\n res.status = response.status;\r\n res.data = JSON.parse(res.data);\r\n } else if (txt.includes('Fatal error')) {\r\n // PHP fatal error occurred\r\n res = {success: false, status: response.status, message: response.statusText, messages: {}, data: {error: txt}};\r\n } else {\r\n // Response is not of type json --> probably some php warnings/notices\r\n let split = txt.split('\\n{\"');\r\n let temp = JSON.parse('{\"'+split[1]);\r\n let data = JSON.parse(temp.data);\r\n res = {success: true, status: response.status, message: split[0], messages: temp.messages, data: data};\r\n }\r\n\r\n return res;\r\n}\r\n\r\n/**\r\n * Perform a migration task\r\n * @param {String} formId Id of the form element\r\n * \r\n * @returns {String} Id of the database record to be migrated\r\n */\r\nlet getNextMigrationID = function(formId) {\r\n let type = formId.replace(formIdTmpl + '-', '');\r\n let form = document.getElementById(formId);\r\n\r\n let migrateable = atob(form.querySelector('[name=\"migrateable\"]').value);\r\n migrateable = JSON.parse(migrateable);\r\n\r\n console.log(migrateable);\r\n}"],"names":[],"sourceRoot":""} \ No newline at end of file +{"version":3,"file":"migrator.js","mappings":";;;UAAA;UACA;;;;;WCDA;WACA;WACA;WACA;WACA,yCAAyC,wCAAwC;WACjF;WACA;WACA;;;;;WCPA;;;;;WCAA;WACA;WACA;WACA,uDAAuD,iBAAiB;WACxE;WACA,gDAAgD,aAAa;WAC7D;;;;;;;;;;;;ACNA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,SAAS;AAClB;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB;AACA,aAAa,UAAU;AACvB,aAAa,qDAAqD,WAAW;AAC7E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,gFAAgF,SAAS;AACrG;AACA;AACA,sBAAsB;AACtB;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA,WAAW,mFAAmF,SAAS;AACvG,IAAI;AACJ;AACA,8BAA8B;AAC9B,6BAA6B;AAC7B;AACA,WAAW;AACX;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB;AACA,aAAa,UAAU;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,qDAAqD,WAAW;AAC7E;AACA;AACA;AACA;AACA;AACA,C","sources":["webpack://Migrator/webpack/bootstrap","webpack://Migrator/webpack/runtime/define property getters","webpack://Migrator/webpack/runtime/hasOwnProperty shorthand","webpack://Migrator/webpack/runtime/make namespace object","webpack://Migrator/./src/index.js"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","// Selectors used by this script\r\nlet typeSelector = 'data-type';\r\nlet formIdTmpl = 'migrationForm';\r\n\r\n/**\r\n * Storage for migrateables\r\n * @var {Object} migrateablesList\r\n */\r\nlet migrateablesList = {};\r\n\r\n/**\r\n * Submit a migration task\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let submitTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n let type = element.getAttribute(typeSelector);\r\n let formId = formIdTmpl + '-' + type;\r\n let task = element.parentNode.querySelector('[name=\"task\"]').value;\r\n\r\n ajax(formId, task)\r\n .then(res => {\r\n // Handle the successful result here\r\n responseHandler(res);\r\n })\r\n .catch(error => {\r\n // Handle any errors here\r\n console.error(error);\r\n });\r\n};\r\n\r\n/**\r\n * Perform an ajax request in json format\r\n * \r\n * @param {String} formId Id of the form element\r\n * @param {String} task Name of the task\r\n * \r\n * @returns {Object} Result object\r\n * {success: true, status: 200, message: '', messages: {}, data: { {success: bool, message: string, data: mixed} }}\r\n */\r\nlet ajax = async function(formId, task) {\r\n\r\n // Catch form and data\r\n let formData = new FormData(document.getElementById(formId));\r\n formData.append('format', 'json');\r\n\r\n if(task == 'migration.start') {\r\n formData.append('id', getNextMigrationID(formId));\r\n }\r\n\r\n // Set request parameters\r\n let parameters = {\r\n method: 'POST',\r\n mode: 'same-origin',\r\n cache: 'default',\r\n redirect: 'follow',\r\n referrerPolicy: 'no-referrer-when-downgrade',\r\n body: formData,\r\n };\r\n\r\n // Set the url\r\n let url = document.getElementById(formId).getAttribute('action');\r\n\r\n // Perform the fetch request\r\n let response = await fetch(url, parameters);\r\n\r\n // Resolve promise as text string\r\n let txt = await response.text();\r\n let res = null;\r\n\r\n if (!response.ok) {\r\n // Catch network error\r\n console.log(txt);\r\n return {success: false, status: response.status, message: response.message, messages: {}, data: {message: txt}};\r\n }\r\n\r\n if(txt.startsWith('{\"success\"')) {\r\n // Response is of type json --> everything fine\r\n res = JSON.parse(txt);\r\n res.status = response.status;\r\n res.data = JSON.parse(res.data);\r\n } else if (txt.includes('Fatal error')) {\r\n // PHP fatal error occurred\r\n res = {success: false, status: response.status, message: response.statusText, messages: {}, data: {message: txt}};\r\n } else {\r\n // Response is not of type json --> probably some php warnings/notices\r\n let split = txt.split('\\n{\"');\r\n let temp = JSON.parse('{\"'+split[1]);\r\n let data = JSON.parse(temp.data);\r\n res = {success: true, status: response.status, message: split[0], messages: temp.messages, data: data};\r\n }\r\n\r\n return res;\r\n}\r\n\r\n/**\r\n * Perform a migration task\r\n * @param {String} formId Id of the form element\r\n * \r\n * @returns {String} Id of the database record to be migrated\r\n */\r\nlet getNextMigrationID = function(formId) {\r\n let type = formId.replace(formIdTmpl + '-', '');\r\n let form = document.getElementById(formId);\r\n\r\n let migrateable = atob(form.querySelector('[name=\"migrateable\"]').value);\r\n migrateable = JSON.parse(migrateable);\r\n\r\n // Overwrite migrateable in list\r\n migrateablesList[type] = migrateable;\r\n\r\n // Loop through queue\r\n for (let id of migrateable.queue) {\r\n if (!(id in migrateable.successful) && !(id in migrateable.failed)) {\r\n migrateablesList[type]['currentID'] = id;\r\n break;\r\n }\r\n }\r\n\r\n return migrateablesList[type]['currentID'];\r\n}\r\n\r\n/**\r\n * Handle migration response\r\n * \r\n * @param {Object} response The response object in the form of\r\n * {success: true, status: 200, message: '', messages: {}, data: { {success: bool, message: string, data: mixed} }}\r\n * \r\n * @returns void\r\n */\r\nlet responseHandler = function(response) {\r\n console.log(response);\r\n}"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/media/com_joomgallery/js/migrator/src/index.js b/media/com_joomgallery/js/migrator/src/index.js index 1c58382e..8cf704c2 100644 --- a/media/com_joomgallery/js/migrator/src/index.js +++ b/media/com_joomgallery/js/migrator/src/index.js @@ -10,6 +10,7 @@ let migrateablesList = {}; /** * Submit a migration task + * * @param {Object} event Event object * @param {Object} element DOM element object */ @@ -19,18 +20,28 @@ export let submitTask = function(event, element) { let type = element.getAttribute(typeSelector); let formId = formIdTmpl + '-' + type; let task = element.parentNode.querySelector('[name="task"]').value; - let res = performTask(formId, task); + + ajax(formId, task) + .then(res => { + // Handle the successful result here + responseHandler(res); + }) + .catch(error => { + // Handle any errors here + console.error(error); + }); }; /** - * Perform a migration task + * Perform an ajax request in json format + * * @param {String} formId Id of the form element * @param {String} task Name of the task * * @returns {Object} Result object - * {success: true, status: 200, message: '', messages: {}, data: {}} + * {success: true, status: 200, message: '', messages: {}, data: { {success: bool, message: string, data: mixed} }} */ -let performTask = async function(formId, task) { +let ajax = async function(formId, task) { // Catch form and data let formData = new FormData(document.getElementById(formId)); @@ -63,7 +74,7 @@ let performTask = async function(formId, task) { if (!response.ok) { // Catch network error console.log(txt); - return {success: false, status: response.status, message: response.message, messages: {}, data: {error: txt}}; + return {success: false, status: response.status, message: response.message, messages: {}, data: {message: txt}}; } if(txt.startsWith('{"success"')) { @@ -73,7 +84,7 @@ let performTask = async function(formId, task) { res.data = JSON.parse(res.data); } else if (txt.includes('Fatal error')) { // PHP fatal error occurred - res = {success: false, status: response.status, message: response.statusText, messages: {}, data: {error: txt}}; + res = {success: false, status: response.status, message: response.statusText, messages: {}, data: {message: txt}}; } else { // Response is not of type json --> probably some php warnings/notices let split = txt.split('\n{"'); @@ -102,12 +113,24 @@ let getNextMigrationID = function(formId) { migrateablesList[type] = migrateable; // Loop through queue - migrateable.queue.forEach(function(id, i) { - if(id in migrateable.successful || id in migrateable.failed) { + for (let id of migrateable.queue) { + if (!(id in migrateable.successful) && !(id in migrateable.failed)) { migrateablesList[type]['currentID'] = id; - return; + break; } - }); + } return migrateablesList[type]['currentID']; +} + +/** + * Handle migration response + * + * @param {Object} response The response object in the form of + * {success: true, status: 200, message: '', messages: {}, data: { {success: bool, message: string, data: mixed} }} + * + * @returns void + */ +let responseHandler = function(response) { + console.log(response); } \ No newline at end of file From 7621d0eca47330450648c877ba7b824a09995d65 Mon Sep 17 00:00:00 2001 From: Elfangor Date: Fri, 3 Nov 2023 08:19:14 +0100 Subject: [PATCH 024/121] add delete and resume --- .../language/en-GB/com_joomgallery.ini | 7 +++- .../src/Controller/MigrationController.php | 9 ++-- .../src/Model/MigrationModel.php | 36 +++++++++++++++- .../src/View/Migration/HtmlView.php | 3 +- .../tmpl/migration/default.php | 21 +++++++--- media/com_joomgallery/joomla.asset.json | 7 +++- media/com_joomgallery/js/tasklessForm.js | 42 +++++++++++++++++++ 7 files changed, 111 insertions(+), 14 deletions(-) mode change 100755 => 100644 media/com_joomgallery/joomla.asset.json create mode 100644 media/com_joomgallery/js/tasklessForm.js diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index 3b70cbbd..b0405ca9 100755 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -92,6 +92,7 @@ COM_JOOMGALLERY_ROOT_ASSET="Root Asset" COM_JOOMGALLERY_ROOT_CAT_ASSET="Root Category Asset" COM_JOOMGALLERY_PATH="Path" COM_JOOMGALLERY_TABLE="Table" +COM_JOOMGALLERY_RESUME="Resume" ;Control panel COM_JOOMGALLERY_CONTROL_PANEL="Control Panel" @@ -191,13 +192,15 @@ COM_JOOMGALLERY_GENERIC_UPLOAD_DATA="Data entered in this form is applied on all COM_JOOMGALLERY_FILE_TITLE_HINT="Title of this file" COM_JOOMGALLERY_FILE_DESCRIPTION_HINT="Description of this file" COM_JOOMGALLERY_FILE_AUTHOR_HINT="Author of this file" -COM_JOOMGALLERY_USE_SCRIPT="Use this script" -COM_JOOMGALLERY_AVAILABLE_SCRIPT="Available migration scripts" +COM_JOOMGALLERY_MIGRATION_START_SCRIPT="Start script" +COM_JOOMGALLERY_MIGRATION_AVAILABLE_SCRIPTS="Available migration scripts" +COM_JOOMGALLERY_MIGRATION_ABORT_MIGRATION="Abort open migrations" COM_JOOMGALLERY_MIGRATION_SCRIPT_NOT_EXIST="The requested migration script does not exist. Please provide an available script name in the request." COM_JOOMGALLERY_MIGRATION_STEP1_BTN_TXT="Check migration capability" COM_JOOMGALLERY_MIGRATION_STEP2_BTN_TXT="Start migration manager" COM_JOOMGALLERY_MIGRATION_STEP3_BTN_TXT="Check migration success" + ;Messages COM_JOOMGALLERY_NOTE_DEVELOPMENT_VERSION="Attention!
----------------
This version of JoomGallery is still under development. Do not use this version on a live website. It is intended for testing purposes only..." COM_JOOMGALLERY_ERROR_NOT_YET_AVAILABLE="The view or feature you are looking for is not yet available..." diff --git a/administrator/com_joomgallery/src/Controller/MigrationController.php b/administrator/com_joomgallery/src/Controller/MigrationController.php index 738ecf0d..683c9eb7 100644 --- a/administrator/com_joomgallery/src/Controller/MigrationController.php +++ b/administrator/com_joomgallery/src/Controller/MigrationController.php @@ -193,8 +193,11 @@ public function resume() return false; } - // Get item to resume. - $id = $this->input->get('resume_id', 0, 'int'); + // Get item to resume from the request. + $cid = (array) $this->input->get('cid', [], 'int'); + $cid = \array_filter($cid); + $id = $cid[0]; + if($id < 1) { $this->setMessage(Text::_('COM_JOOMGALLERY_SERVICE_ERROR_MIGRATION_RESUME'), 'error'); @@ -258,7 +261,7 @@ public function delete() $cid = (array) $this->input->get('cid', [], 'int'); // Remove zero values resulting from input filter - $cid = array_filter($cid); + $cid = \array_filter($cid); if(!empty($cid)) { diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index c9521e71..1858366d 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -298,9 +298,9 @@ public function getItem($pk = null) } /** - * Method to get an array of migrateables based on current script. + * Method to get an array of migration records based on current script. * - * @return MigrationTable[] An array of data items + * @return Migrationtable[] An array of migration tables * * @since 4.0.0 */ @@ -321,6 +321,7 @@ public function getItems(): array } $db->setQuery($query); + $tables = $db->loadObjectList('type'); } catch (\RuntimeException $e) @@ -364,6 +365,37 @@ public function getItems(): array return $items; } + /** + * Method to get an array of IDs based on current script. + * + * @return array List of IDs + * + * @since 4.0.0 + */ + public function getIdList(): array + { + // Create a new query object. + try + { + $db = $this->getDbo(); + $query = $db->getQuery(true); + + // Select the required fields from the table. + $query->select(array('a.id', 'a.script', 'a.type')); + $query->from($db->quoteName(_JOOM_TABLE_MIGRATION, 'a')); + + $db->setQuery($query); + + return $db->loadObjectList('script'); + } + catch (\RuntimeException $e) + { + $this->component->setError($e->getMessage()); + + return array(); + } + } + /** * Load the current queue of ids from table * diff --git a/administrator/com_joomgallery/src/View/Migration/HtmlView.php b/administrator/com_joomgallery/src/View/Migration/HtmlView.php index 95d4a0b4..05aba02f 100644 --- a/administrator/com_joomgallery/src/View/Migration/HtmlView.php +++ b/administrator/com_joomgallery/src/View/Migration/HtmlView.php @@ -99,7 +99,8 @@ public function display($tpl = null) break; default: - # code... + // ID list of open migrations + $this->openMigrations = $this->get('IdList'); break; } } diff --git a/administrator/com_joomgallery/tmpl/migration/default.php b/administrator/com_joomgallery/tmpl/migration/default.php index aba84e09..1d0eae04 100644 --- a/administrator/com_joomgallery/tmpl/migration/default.php +++ b/administrator/com_joomgallery/tmpl/migration/default.php @@ -19,11 +19,12 @@ // Import CSS $wa = Factory::getApplication()->getDocument()->getWebAssetManager(); $wa->useStyle('com_joomgallery.admin') - ->useScript('com_joomgallery.admin'); + ->useScript('com_joomgallery.admin') + ->useScript('com_joomgallery.tasklessForm'); ?>
-

:

+

:


scripts as $name => $script) : ?> @@ -36,10 +37,20 @@
<?php echo $name; ?> logo
-
+

- - + + + + + + + + + + + + diff --git a/media/com_joomgallery/joomla.asset.json b/media/com_joomgallery/joomla.asset.json old mode 100755 new mode 100644 index 01996329..4c112312 --- a/media/com_joomgallery/joomla.asset.json +++ b/media/com_joomgallery/joomla.asset.json @@ -112,7 +112,12 @@ { "name": "com_joomgallery.masonry", "type": "script", - "uri": "com_joomgallery/masonry.min.js", + "uri": "com_joomgallery/masonry.min.js" + }, + { + "name": "com_joomgallery.tasklessForm", + "type": "script", + "uri": "com_joomgallery/tasklessForm.js", "attributes": { "type": "module" } diff --git a/media/com_joomgallery/js/tasklessForm.js b/media/com_joomgallery/js/tasklessForm.js new file mode 100644 index 00000000..0dcd13b4 --- /dev/null +++ b/media/com_joomgallery/js/tasklessForm.js @@ -0,0 +1,42 @@ +/** + * @copyright (C) 2018 Open Source Matters, Inc. + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ +((document, submitForm) => { + + // Selectors used by this script + const buttonDataSelector = 'data-submit-task'; + const formId = 'adminForm'; + const containerId = 'formInputContainer'; + + /** + * Submit the task + * @param task + */ + const submitTask = task => { + const form = document.getElementById(formId); + const container = document.getElementById(containerId); + + // add task input element + let input = document.createElement('input'); + input.type = 'text'; + input.name = 'task'; + input.value = task; + container.appendChild(input); + + // submit form + form.submit(); + }; + + // Register events + document.addEventListener('DOMContentLoaded', () => { + const buttons = [].slice.call(document.querySelectorAll(`[${buttonDataSelector}]`)); + buttons.forEach(button => { + button.addEventListener('click', e => { + e.preventDefault(); + const task = e.target.getAttribute(buttonDataSelector); + submitTask(task); + }); + }); + }); +})(document, Joomla.submitform); From d7ef1cee6cd1e8718d1b78cd8b43a8685cac55a1 Mon Sep 17 00:00:00 2001 From: Elfangor Date: Sat, 4 Nov 2023 13:38:35 +0100 Subject: [PATCH 025/121] add component logger --- .../language/en-GB/com_joomgallery.ini | 3 + .../src/Controller/MigrationController.php | 20 ++- .../src/Extension/MessageTrait.php | 24 ++- .../src/Model/MigrationModel.php | 150 +++++++++++++++--- .../src/Service/Migration/Migration.php | 7 +- .../src/Table/MigrationTable.php | 39 +++-- media/com_joomgallery/js/tasklessForm.js | 2 +- 7 files changed, 200 insertions(+), 45 deletions(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index b0405ca9..7e555e15 100755 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -695,6 +695,9 @@ COM_JOOMGALLERY_SERVICE_MIGRATION_MAPPING_SRC_IMAGETYPE_NOT_EXIST="The source im COM_JOOMGALLERY_SERVICE_MIGRATION_MAPPING_IMAGETYPE_NOT_USED="The following destination imagetypes are not used in the mapping: '%s'. Please go back to step 1 and apply the mapping correctly." COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_RECORD_ID_MISSING="Starting a migration without source data ID not possible. Contact the developer of the migration script to solve this." COM_JOOMGALLERY_SERVICE_ERROR_MIGRATION_RESUME="Migration can not be resumed. There is a problem in fetching the required info." +COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_CONVERT_DATA="Data from source could not be converted to new data structure. See log file for more destails." +COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_INSERT_RECORD="Data could not be inserted into destination database. See log file for more destails." +COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_CREATE_IMGTYPE="Error in creating imagetypes. See log file for more destails." ;Plugin events COM_JOOMGALLERY_PLUGIN_ERROR_RETURN_VALUE="Return value of the plugin event '%s' must be of type %s and contain : %s" diff --git a/administrator/com_joomgallery/src/Controller/MigrationController.php b/administrator/com_joomgallery/src/Controller/MigrationController.php index 683c9eb7..d5a273b4 100644 --- a/administrator/com_joomgallery/src/Controller/MigrationController.php +++ b/administrator/com_joomgallery/src/Controller/MigrationController.php @@ -508,6 +508,9 @@ public function start() return false; } + + // Start migration + //------------------ // Attempt to load migration record from database $item = $model->getItem($json->id); @@ -533,10 +536,23 @@ public function start() } // Perform the migration - $data = $model->migrate($type, $id, $item); + $table = $model->migrate($type, $id, $item); + + // Check for errors + $errors = $this->component->getError(); + + if(!empty($errors)) + { + // Error during migration + $response = $this->createRespond($table, false, $this->component->getError(true)); + } + else + { + // Migration successful + $response = $this->createRespond($table, true); + } // Send migration results - $response = $this->createRespond($data, true); $this->ajaxRespond($response, $format); } diff --git a/administrator/com_joomgallery/src/Extension/MessageTrait.php b/administrator/com_joomgallery/src/Extension/MessageTrait.php index 82c227bb..7b1f0ed1 100644 --- a/administrator/com_joomgallery/src/Extension/MessageTrait.php +++ b/administrator/com_joomgallery/src/Extension/MessageTrait.php @@ -124,16 +124,25 @@ public function msgFromSession() /** * Add a JoomGallery logger to the JLog class + * + * @param string Name of the specific logger * * @return void * * @since 4.0.0 */ - protected function addLogger() + protected function addLogger(string $name = null) { if(!$this->log) { - Log::addLogger(['text_file' => 'com_joomgallery.log.php'], Log::ALL, ['com_joomgallery']); + if(\is_null($name)) + { + Log::addLogger(['text_file' => 'com_joomgallery.log.php'], Log::ALL, ['com_joomgallery']); + } + else + { + Log::addLogger(['text_file' => 'com_joomgallery.'.$name.'.log.php'], Log::ALL, ['com_joomgallery.'.$name]); + } } $this->log = true; @@ -149,9 +158,16 @@ protected function addLogger() * * @since 4.0.0 */ - protected function addLog($txt, $priority) + protected function addLog(string $txt, int $priority = 8, string $name = null) { - Log::add($txt, $priority, 'com_joomgallery'); + if(\is_null($name)) + { + Log::add($txt, $priority, 'com_joomgallery'); + } + else + { + Log::add($txt, $priority, 'com_joomgallery'.$name); + } } /** diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index 1858366d..34fbeb72 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -16,6 +16,7 @@ use \Joomla\CMS\Factory; use \Joomla\CMS\Uri\Uri; use \Joomla\CMS\Form\Form; +use \Joomla\CMS\Language\Text; use \Joomla\Registry\Registry; use \Joomla\CMS\Filesystem\Folder; use \Joomla\CMS\MVC\Model\AdminModel; @@ -576,46 +577,101 @@ public function postcheck($params) * * @param string $type Name of the content type to migrate. * @param integer $pk The primary key of the source record. - * @param MigrationTable $mig The MigrationTable object. + * @param object $mig The MigrationTable object. * * @return object The object containing the migration results. * * @since 4.0.0 */ - public function migrate(string $type, int $pk, MigrationTable $mig): MigrationTable + public function migrate(string $type, int $pk, object $mig): object { - $info = $this->getScript(); + // Initialise variables + $info = $this->getScript(); + $new_pk = 0; + $success = true; + $error_msg = ''; // Set the migration parameters $this->setParams($mig->params); // Get record data from source - $data = $this->component->getMigration()->getData($type, $pk); - - // Convert record data into structure needed for JoomGallery v4+ - $data = $this->component->getMigration()->convertData($data); - - // Create new record based on data array - $sameIDs = \boolval($mig->params->get('source_ids', '0')); - $record = $this->insertRecord($type, $data, $sameIDs); - - // Recreate images - if($type === 'image') + if($data = $this->component->getMigration()->getData($type, $pk)) { - $img_source = $this->component->getMigration()->getImageSource($data); - if(\array_key_first($img_source) === 0) + // Convert record data into structure needed for JoomGallery v4+ + $data = $this->component->getMigration()->convertData($data); + + if(!$data) { - // Create imagetypes based on one given image - $this->createImages($record, $img_source[0]); + $success = false; + $error_msg = Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_CONVERT_DATA'); } else { - // Reuse images from source as imagetypes - $this->reuseImages($record, $img_source); + // Create new record based on data array + $sameIDs = \boolval($mig->params->get('source_ids', '0')); + $record = $this->insertRecord($type, $data, $sameIDs); + + // Set primary key value of new created record + $new_pk = $record->id; + + if(!$record) + { + $success = false; + $error_msg = Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_INSERT_RECORD'); + } + else + { + // Recreate images + if($type === 'image') + { + $img_source = $this->component->getMigration()->getImageSource($data); + if(\array_key_first($img_source) === 0) + { + // Create imagetypes based on given image and mapping + $res = $this->createImages($record, $img_source[0]); + } + else + { + // Reuse images from source as imagetypes (no image creation) + $res = $this->reuseImages($record, $img_source); + } + + if(!$res) + { + $record = $this->deleteRecord($type, $new_pk); + $success = false; + $error_msg = Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_CREATE_IMGTYPE'); + } + } + } } } - return $this->component->getMigration()->migrate(); + // Load migration data table + $table = $this->getTable(); + $table->load($mig->id); + + // Remove migrated primary key from queue + if(($key = \array_search($pk, $table->queue)) !== false) + { + unset($table->queue[$key]); + } + + if($success) + { + // Add migrated primary key to successful object + $table->successful->set($pk, $new_pk); + } + else + { + // Add migrated primary key to failed object + $table->failed->set($pk, $error_msg); + } + + // Calculate progress and completed state + $table->clcProgress(); + + return $table; } /** @@ -637,7 +693,7 @@ protected function insertRecord(string $type, array $data, bool $newID=true): bo // Create table if(!$table = $this->getMVCFactory()->createTable($type, 'administrator')) { - $this->setError(Text::sprintf('COM_JOOMGALLERY_ERROR_IMGTYPE_TABLE_NOT_EXISTING', $type)); + $this->component->setError(Text::sprintf('COM_JOOMGALLERY_ERROR_IMGTYPE_TABLE_NOT_EXISTING', $type)); return false; } @@ -660,7 +716,7 @@ protected function insertRecord(string $type, array $data, bool $newID=true): bo // Bind migrated data to table object if(!$table->bind($data)) { - $this->setError($table->getError()); + $this->component->setError($table->getError()); return false; } @@ -687,6 +743,54 @@ protected function insertRecord(string $type, array $data, bool $newID=true): bo return $table; } + /** + * Method to delete a content type record in destination table. + * + * @param string $type Name of the content type to insert. + * @param array $data The record data gathered from the migration source. + * @param bool $newID True to auto-increment the id in the database + * + * @return object Inserted record object on success, False on error. + * + * @since 4.0.0 + */ + protected function deleteRecord(string $type, int $pk): bool + { + // Check content type + JoomHelper::isAvailable($type); + + // Create table + if(!$table = $this->getMVCFactory()->createTable($type, 'administrator')) + { + $this->component->setError(Text::sprintf('COM_JOOMGALLERY_ERROR_IMGTYPE_TABLE_NOT_EXISTING', $type)); + + return false; + } + + // Load the table. + $table->load($pk); + + if($type === 'image') + { + // Delete corresponding imagetypes + $manager = JoomHelper::getService('FileManager'); + + if(!$manager->deleteImages($table)) + { + $this->component->setError($this->component->getDebug(true)); + + return false; + } + } + + if(!$table->delete($pk)) + { + $this->component->setError($table->getError()); + + return false; + } + } + /** * Creation of imagetypes based on one source file. * Source file has to be given with a full system path. diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index 522c6818..9fb7f1cd 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -233,10 +233,7 @@ public function migrate($type, $source, $dest) */ protected function addLogger() { - if(!$this->log) - { - Log::addLogger(['text_file' => 'com_joomgallery.migration.log.php'], Log::ALL, ['com_joomgallery.migration']); - } + $this->component->addLogger('migration'); $this->log = true; } @@ -253,7 +250,7 @@ protected function addLogger() */ protected function addLog($txt, $priority) { - Log::add($txt, $priority, 'com_joomgallery.migration'); + $this->component->addLog($txt, $priority, 'migration'); } /** diff --git a/administrator/com_joomgallery/src/Table/MigrationTable.php b/administrator/com_joomgallery/src/Table/MigrationTable.php index 210614fc..736f5a9a 100644 --- a/administrator/com_joomgallery/src/Table/MigrationTable.php +++ b/administrator/com_joomgallery/src/Table/MigrationTable.php @@ -58,8 +58,8 @@ public function __construct(DatabaseDriver $db) // Initialize queue, successful and failed $this->queue = array(); - $this->successful = array(); - $this->failed = array(); + $this->successful = new Registry(); + $this->failed = new Registry(); } /** @@ -185,25 +185,44 @@ public function load($keys = null, $reset = true) // Support for successful field if(isset($this->successful) && !is_array($this->successful)) { - $this->successful = \json_decode($this->successful); + $this->successful = new Registry(\json_decode($this->successful)); } // Support for failed field if(isset($this->failed) && !is_array($this->failed)) { - $this->failed = \json_decode($this->failed); + $this->failed = new Registry(\json_decode($this->failed)); } - // Calculate progress property - $this->progress = (int) \round((100 / \count($this->queue)) * (\count($this->successful) + \count($this->failed))); - - // Update completed property - if(\count($this->queue) === \count($this->successful)) + // Support for params field + if(isset($this->params) && !is_array($this->params)) { - $this->completed = true; + $this->params = new Registry(\json_decode($this->params)); } + + // Calculate progress and completed state + $this->clcProgress(); } return $success; } + + /** + * Method to calculate progress and completed state. + * + * @return void + * + * @since 4.0.0 + */ + public function clcProgress() + { + // Calculate progress property + $this->progress = (int) \round((100 / \count($this->queue)) * ($this->successful->count() + $this->failed->count())); + + // Update completed property + if(\count($this->queue) === $this->successful->count()) + { + $this->completed = true; + } + } } diff --git a/media/com_joomgallery/js/tasklessForm.js b/media/com_joomgallery/js/tasklessForm.js index 0dcd13b4..6aeaccd1 100644 --- a/media/com_joomgallery/js/tasklessForm.js +++ b/media/com_joomgallery/js/tasklessForm.js @@ -19,7 +19,7 @@ // add task input element let input = document.createElement('input'); - input.type = 'text'; + input.type = 'hidden'; input.name = 'task'; input.value = task; container.appendChild(input); From 98abeffb6f45270df5e89c288d091b51a1c2167e Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 6 Nov 2023 10:03:40 +0100 Subject: [PATCH 026/121] adjust logging and messaging --- .../src/Controller/MigrationController.php | 41 +++++++++++--- .../src/Extension/MessageTrait.php | 52 ++++++++++++++---- .../src/Model/MigrationModel.php | 13 ++++- .../src/Service/Migration/Migration.php | 55 +++++-------------- 4 files changed, 101 insertions(+), 60 deletions(-) diff --git a/administrator/com_joomgallery/src/Controller/MigrationController.php b/administrator/com_joomgallery/src/Controller/MigrationController.php index d5a273b4..87fc4b0d 100644 --- a/administrator/com_joomgallery/src/Controller/MigrationController.php +++ b/administrator/com_joomgallery/src/Controller/MigrationController.php @@ -519,7 +519,12 @@ public function start() { // It seems that the migration record does not yet exists in the database // Save migration record to database - $model->save($json); + if(!$model->save($json)) + { + $this->component->setError($model->getError()); + + return false; + } // Attempt to load migration record from database $item = $model->getItem($model->getState('migration.id')); @@ -539,12 +544,10 @@ public function start() $table = $model->migrate($type, $id, $item); // Check for errors - $errors = $this->component->getError(); - - if(!empty($errors)) + if(!empty($this->component->getError())) { // Error during migration - $response = $this->createRespond($table, false, $this->component->getError(true)); + $response = $this->createRespond($table, false, $this->component->getError()); } else { @@ -558,23 +561,43 @@ public function start() /** * Create a response object - * {success: bool, message: string, data: mixed} + * {success: bool, message: string|array, data: mixed} * * @param mixed $data The data returned to the frontend * @param bool $success True if everything was good, false otherwise - * @param string $message A message to be printed in the frontend + * @param array $error An array of error messages to be printed in the frontend * * @return string Response json string * * @since 4.0.0 */ - protected function createRespond($data, bool $success = true, string $message = ''): string + protected function createRespond($data, bool $success = true, array $error = null): string { $obj = new stdClass; $obj->success = $success; $obj->data = $data; - $obj->message = $message; + $obj->error = array(); + $obj->debug = array(); + $obj->warning = array(); + + // Get debug output + if(!empty($debug = $this->component->getDebug())) + { + $obj->debug = $debug; + } + + // Get warning output + if(!empty($warning = $this->component->getWarning())) + { + $obj->warning = $warning; + } + + // Get error output + if(!empty($error)) + { + $obj->error = $error; + } return \json_encode($obj, JSON_UNESCAPED_UNICODE); } diff --git a/administrator/com_joomgallery/src/Extension/MessageTrait.php b/administrator/com_joomgallery/src/Extension/MessageTrait.php index 7b1f0ed1..a0f19857 100644 --- a/administrator/com_joomgallery/src/Extension/MessageTrait.php +++ b/administrator/com_joomgallery/src/Extension/MessageTrait.php @@ -90,6 +90,15 @@ trait MessageTrait */ protected $log = false; + /** + * Name of the logger to be used + * + * @var string + * + * @since 4.0.0 + */ + protected $logName = null; + /** * Adds the storages to the session * @@ -160,16 +169,36 @@ protected function addLogger(string $name = null) */ protected function addLog(string $txt, int $priority = 8, string $name = null) { - if(\is_null($name)) + if(\is_null($name) && \is_null($this->logName)) { Log::add($txt, $priority, 'com_joomgallery'); } else { + if(\is_null($name)) + { + $name = $this->logName; + } + Log::add($txt, $priority, 'com_joomgallery'.$name); } } + /** + * Set a default logger to be used from now on + * + * @param string $name Name of the logger. Set null to use the default JoomGallery logger + * + * @return void + * + * @since 4.0.0 + */ + public function setLogger(string $name) + { + $this->addLogger($name); + $this->logName = $name; + } + /** * Add text to the debug information storage * @@ -177,19 +206,20 @@ protected function addLog(string $txt, int $priority = 8, string $name = null) * @param bool $new_line True to add text to a new line (default: true) * @param bool $margin_top True to add an empty line in front (default: false) * @param bool $log True to add error message to logfile (default: false) + * @param string $name Name of the logger to be used (default: null) * * @return void * * @since 4.0.0 */ - public function addDebug($txt, $new_line=true, $margin_top=false, $log=false) + public function addDebug($txt, $new_line=true, $margin_top=false, $log=false, $name=null) { $this->setMsg($txt, 'debug', $new_line, $margin_top); if($log) { - $this->addLogger(); - $this->addLog($txt, Log::DEBUG); + $this->addLogger($name); + $this->addLog($txt, Log::DEBUG, $name); } } @@ -200,19 +230,20 @@ public function addDebug($txt, $new_line=true, $margin_top=false, $log=false) * @param bool $new_line True to add text to a new line (default: true) * @param bool $margin_top True to add an empty line in front (default: false) * @param bool $log True to add error message to logfile (default: false) + * @param string $name Name of the logger to be used (default: null) * * @return void * * @since 4.0.0 */ - public function addWarning($txt, $new_line=true, $margin_top=false, $log=false) + public function addWarning($txt, $new_line=true, $margin_top=false, $log=false, $name=null) { $this->setMsg($txt, 'warning', $new_line, $margin_top); if($log) { - $this->addLogger(); - $this->addLog($txt, Log::WARNING); + $this->addLogger($name); + $this->addLog($txt, Log::WARNING, $name); } } @@ -223,20 +254,21 @@ public function addWarning($txt, $new_line=true, $margin_top=false, $log=false) * @param bool $new_line True to add text to a new line (default: true) * @param bool $margin_top True to add an empty line in front (default: false) * @param bool $log True to add error message to logfile (default: true) + * @param string $name Name of the logger to be used (default: null) * * @return void * * @since 4.0.0 */ - public function setError($txt, $new_line=true, $margin_top=false, $log=true) + public function setError($txt, $new_line=true, $margin_top=false, $log=true, $name=null) { $this->setMsg($txt, 'error', $new_line, $margin_top); $this->error = true; if($log) { - $this->addLogger(); - $this->addLog($txt, Log::ERROR); + $this->addLogger($name); + $this->addLog($txt, Log::ERROR, $name); } } diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index 34fbeb72..2f0a384e 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -649,7 +649,12 @@ public function migrate(string $type, int $pk, object $mig): object // Load migration data table $table = $this->getTable(); - $table->load($mig->id); + if(!$table->load($mig->id)) + { + $this->component->setError($table->getError()); + + return $mig; + } // Remove migrated primary key from queue if(($key = \array_search($pk, $table->queue)) !== false) @@ -668,6 +673,12 @@ public function migrate(string $type, int $pk, object $mig): object $table->failed->set($pk, $error_msg); } + // Add errors + if($error_msg !== '') + { + $this->component->setError($error_msg); + } + // Calculate progress and completed state $table->clcProgress(); diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index 9fb7f1cd..588cdc4b 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -93,15 +93,6 @@ abstract class Migration implements MigrationInterface */ protected $migrateables = array(); - /** - * State if logger is created - * - * @var bool - * - * @since 4.0.0 - */ - protected $log = false; - /** * Constructor * @@ -120,8 +111,8 @@ public function __construct() // Try to load language file of the migration script $this->app->getLanguage()->load('com_joomgallery.migration.'.$this->name, _JOOM_PATH_ADMIN); - // Create logger - $this->addLogger(); + // Set logger + $this->component->setLogger('migration'); // Fill info object $this->info = new \stdClass; @@ -130,6 +121,19 @@ public function __construct() $this->info->description = Text::_('FILES_JOOMGALLERY_MIGRATION_'.strtoupper($this->name).'_DESC'); } + /** + * Destructor + * + * @return void + * + * @since 4.0.0 + */ + public function __destruct() + { + // Set logger + $this->component->setLogger(null); + } + /** * Returns a list of content types which can be migrated. * @@ -224,35 +228,6 @@ public function migrate($type, $source, $dest) return; } - /** - * Add a JoomGallery migration logger to the JLog class - * - * @return void - * - * @since 4.0.0 - */ - protected function addLogger() - { - $this->component->addLogger('migration'); - - $this->log = true; - } - - /** - * Log a message - * - * @param string $txt The message for a new log entry. - * @param integer $priority Message priority. - * - * @return void - * - * @since 4.0.0 - */ - protected function addLog($txt, $priority) - { - $this->component->addLog($txt, $priority, 'migration'); - } - /** * Get a database object * From fea3069aeaab4752e49c36880e9c843e4baf1259 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 6 Nov 2023 18:28:11 +0100 Subject: [PATCH 027/121] add convertData method to script --- .../com_joomgallery/forms/config.xml | 13 ++ .../forms/subform_catparams.xml | 17 +- .../language/en-GB/com_joomgallery.ini | 9 +- .../com_joomgallery.migration.Jg3ToJg4.ini | 5 +- .../sql/install.mysql.utf8.sql | 1 + .../src/Controller/MigrationController.php | 7 +- .../src/Extension/MessageTrait.php | 4 +- .../src/Model/MigrationModel.php | 14 +- .../src/Service/Migration/Migration.php | 135 +++++++++++- .../Service/Migration/MigrationInterface.php | 3 +- .../Service/Migration/Scripts/Jg3ToJg4.php | 207 +++++++++++++----- .../com_joomgallery/tmpl/migration/step2.php | 35 ++- 12 files changed, 371 insertions(+), 79 deletions(-) diff --git a/administrator/com_joomgallery/forms/config.xml b/administrator/com_joomgallery/forms/config.xml index 31c8c250..2cc5b105 100644 --- a/administrator/com_joomgallery/forms/config.xml +++ b/administrator/com_joomgallery/forms/config.xml @@ -638,6 +638,19 @@ +
+ + + + +
+
+ label="COM_JOOMGALLERY_CONFIG_RATING" + description="COM_JOOMGALLERY_CONFIG_RATING_CAT_LONG"> + + + + + + diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index 7e555e15..615b403a 100755 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -386,10 +386,6 @@ COM_JOOMGALLERY_CONFIG_SHOW_IPTCDATA="Show IPTC data?" COM_JOOMGALLERY_CONFIG_SHOW_IPTCDATA_IMG_LONG="Shall the IPTC data of the image be shown if available?{tip}If \"Yes\" by the following options you can decide which information will be published. Otherwise they are ineffective." COM_JOOMGALLERY_CONFIG_OFFER_DOWNLOAD="Offer Downloads" COM_JOOMGALLERY_CONFIG_OFFER_DOWNLOAD_CAT_LONG="Here you can set whether the download is allowed in this category.{tip}This can overwrite the setting in tab 'User Access Rights->Download->Offer Downloads' in JoomGallery Configuration" -COM_JOOMGALLERY_CONFIG_ALLOW_COMMENT="Allow Comments" -COM_JOOMGALLERY_CONFIG_ALLOW_COMMENT_CAT_LONG="Here you can set whether the commenting is allowed in this category.{tip}This can overwrite the setting in tab 'User Access Rights->Comments->Allow comments' in JoomGallery Configuration" -COM_JOOMGALLERY_CONFIG_ALLOW_RATING="Allow Rating" -COM_JOOMGALLERY_CONFIG_ALLOW_RATING_CAT_LONG="Here you can set whether the rating is allowed in this category.{tip}This can overwrite the setting in tab 'User Access Rights->Ratings->Allow Rating' in JoomGallery Configuration" COM_JOOMGALLERY_CONFIG_ALLOW_WATERMARK="Add Watermark" COM_JOOMGALLERY_CONFIG_ALLOW_WATERMARK_CAT_LONG="Here you can set whether the Watermark is shown in detail/original images in this category.{tip}This can overwrite the setting in tab 'Detail View->General Settings->Add Watermark?' in JoomGallery Configuration" COM_JOOMGALLERY_CONFIG_ALLOW_WATERMARK_DOWNLOAD="Download with Watermark" @@ -539,6 +535,7 @@ COM_JOOMGALLERY_CONFIG_DOWNLOADWITHWATERMARK="Download with Watermark?" COM_JOOMGALLERY_CONFIG_DOWNLOADWITHWATERMARK_LONG="Do you want to add a watermark to the downloaded image?{tip}Choosing \"Yes\" will add a watermark to the downloaded image without regard whether or not a watermark is displayed in detail view." COM_JOOMGALLERY_CONFIG_RATING="Allow Rating" COM_JOOMGALLERY_CONFIG_RATING_LONG="This option displays a rating field in the detail view of the gallery and allows users to rate images.{tip}Enabling this, will display additional rating options below." +COM_JOOMGALLERY_CONFIG_RATING_CAT_LONG="Here you can set whether the rating is allowed in this category.{tip}This can overwrite the setting in tab 'User Access Rights->Ratings->Allow Rating' in JoomGallery Configuration" COM_JOOMGALLERY_CONFIG_HIGHEST_RATING="Highest rating" COM_JOOMGALLERY_CONFIG_HIGHEST_RATING_LONG="If user ratings are activated (setting above) you can specify the range of numbers here.{tip}The value you enter is the highest possible value permitted." COM_JOOMGALLERY_CONFIG_CALC_TYPE="Rating calculation type" @@ -562,6 +559,9 @@ COM_JOOMGALLERY_CONFIG_IMAGETYPES="Supported image types" COM_JOOMGALLERY_CONFIG_IMAGETYPES_LONG="List here the supported image file types as a comma separated list of file extensions.{tip}Example: jpg,jpeg,png,gif,webp" COM_JOOMGALLERY_CONFIG_PARALLEL_PROCESS="Nmb parallel processes" COM_JOOMGALLERY_CONFIG_PARALLEL_PROCESS_LONG="Number of parallel image saving processes.{tip}High values speed up the upload process in the multiple upload method, as several images are saved at the same time. However, this requires high server performance. Only increase this value to more than one if you have a powerful server available." +COM_JOOMGALLERY_CONFIG_COMMENT="Allow Commenting" +COM_JOOMGALLERY_CONFIG_COMMENT_LONG="This option displays a comment field in the detail view of the gallery showing the given comments.{tip}The current JoomGallery version does not support commenting. But already given comments will be shown, if this option is enabled." +COM_JOOMGALLERY_CONFIG_COMMENT_CAT_LONG="Here you can set whether the commenting is allowed in this category.{tip}This can overwrite the setting in tab 'User Access Rights->Comments->Allow comments' in JoomGallery Configuration" ;Services COM_JOOMGALLERY_SERVICE_READING_ERROR="Read image failed. No further processing." @@ -698,6 +698,7 @@ COM_JOOMGALLERY_SERVICE_ERROR_MIGRATION_RESUME="Migration can not be resumed. Th COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_CONVERT_DATA="Data from source could not be converted to new data structure. See log file for more destails." COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_INSERT_RECORD="Data could not be inserted into destination database. See log file for more destails." COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_CREATE_IMGTYPE="Error in creating imagetypes. See log file for more destails." +COM_JOOMGALLERY_SERVICE_MIGRATION_FILENAME_DIFF="It was noticed that two different filenames were used for detail/original and thumbnail in the image with id: %s and alias: %s. The migration script will skip the migration of this image." ;Plugin events COM_JOOMGALLERY_PLUGIN_ERROR_RETURN_VALUE="Return value of the plugin event '%s' must be of type %s and contain : %s" diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini index d378bdf6..b71df952 100644 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini @@ -13,4 +13,7 @@ FILES_JOOMGALLERY_MIGRATION_JG3TOJG4_ORIGPATH_DESC="Path to original image" FILES_JOOMGALLERY_MIGRATION_JG3TOJG4_DETAILPATH_DESC="Path to detail image" FILES_JOOMGALLERY_MIGRATION_JG3TOJG4_THUMBPATH_DESC="Path to thumbnail image" FILES_JOOMGALLERY_MIGRATION_IMAGE_TITLE="Migration: Images" -FILES_JOOMGALLERY_MIGRATION_CATEGORY_TITLE="Migration: Categories" \ No newline at end of file +FILES_JOOMGALLERY_MIGRATION_CATEGORY_TITLE="Migration: Categories" +FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_TITLE="Image filenames" +FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_DESC="There are different filenames for thumbnail and detail image defined in the database of your JG3 tables. Number of affected images: %s" +FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_HELP="This script is currently not able to solve this issue automatically. You have to make the adjustments by yourself before you can use the script. For all the affected Images you have to do the following:
  • Rename the thumbnail image in the filesystem to be the one set in the old JoomGallery image table at row 'imgfilename'.
  • In the old JoomGallery image table, set the value of the row 'imgthumbname' to be the same as in row 'imgfilename'.

ID's of affected images: (%s)" \ No newline at end of file diff --git a/administrator/com_joomgallery/sql/install.mysql.utf8.sql b/administrator/com_joomgallery/sql/install.mysql.utf8.sql index 418548a3..517e0886 100644 --- a/administrator/com_joomgallery/sql/install.mysql.utf8.sql +++ b/administrator/com_joomgallery/sql/install.mysql.utf8.sql @@ -167,6 +167,7 @@ CREATE TABLE IF NOT EXISTS `#__joomgallery_configs` ( `jg_votingonlyonce` TINYINT(1) NOT NULL DEFAULT 1, `jg_report_images` TINYINT(1) NOT NULL DEFAULT 1, `jg_report_hint` TINYINT(1) NOT NULL DEFAULT 1, +`jg_showcomments` TINYINT(1) NOT NULL DEFAULT 1, PRIMARY KEY (`id`), KEY `idx_checkout` (`checked_out`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci; diff --git a/administrator/com_joomgallery/src/Controller/MigrationController.php b/administrator/com_joomgallery/src/Controller/MigrationController.php index 87fc4b0d..eb4485c5 100644 --- a/administrator/com_joomgallery/src/Controller/MigrationController.php +++ b/administrator/com_joomgallery/src/Controller/MigrationController.php @@ -513,7 +513,7 @@ public function start() //------------------ // Attempt to load migration record from database - $item = $model->getItem($json->id); + $item = $model->getItem($json['id']); if(\is_null($item->id)) { @@ -535,13 +535,10 @@ public function start() { // Check out record $model->checkout($item->id); - - // Attempt ot load migrateable from database - $item = $model->getItem($item->id); } // Perform the migration - $table = $model->migrate($type, $id, $item); + $table = $model->migrate($type, $id); // Check for errors if(!empty($this->component->getError())) diff --git a/administrator/com_joomgallery/src/Extension/MessageTrait.php b/administrator/com_joomgallery/src/Extension/MessageTrait.php index a0f19857..34453e33 100644 --- a/administrator/com_joomgallery/src/Extension/MessageTrait.php +++ b/administrator/com_joomgallery/src/Extension/MessageTrait.php @@ -187,13 +187,13 @@ protected function addLog(string $txt, int $priority = 8, string $name = null) /** * Set a default logger to be used from now on * - * @param string $name Name of the logger. Set null to use the default JoomGallery logger + * @param string $name Name of the logger. Empty to use the default JoomGallery logger * * @return void * * @since 4.0.0 */ - public function setLogger(string $name) + public function setLogger(string $name = null) { $this->addLogger($name); $this->logName = $name; diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index 2f0a384e..4e937cd0 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -577,22 +577,20 @@ public function postcheck($params) * * @param string $type Name of the content type to migrate. * @param integer $pk The primary key of the source record. - * @param object $mig The MigrationTable object. * * @return object The object containing the migration results. * * @since 4.0.0 */ - public function migrate(string $type, int $pk, object $mig): object + public function migrate(string $type, int $pk): object { // Initialise variables - $info = $this->getScript(); $new_pk = 0; $success = true; $error_msg = ''; - // Set the migration parameters - $this->setParams($mig->params); + // Prepare migration service and return migrateable object + $mig = $this->component->getMigration()->prepareMigration($type); // Get record data from source if($data = $this->component->getMigration()->getData($type, $pk)) @@ -608,7 +606,7 @@ public function migrate(string $type, int $pk, object $mig): object else { // Create new record based on data array - $sameIDs = \boolval($mig->params->get('source_ids', '0')); + $sameIDs = \boolval($mig->params->get('source_ids', 0)); $record = $this->insertRecord($type, $data, $sameIDs); // Set primary key value of new created record @@ -738,7 +736,7 @@ protected function insertRecord(string $type, array $data, bool $newID=true): bo // Check the data. if(!$table->check()) { - $this->setError($table->getError()); + $this->component->setError($table->getError()); return false; } @@ -746,7 +744,7 @@ protected function insertRecord(string $type, array $data, bool $newID=true): bo // Store the data. if(!$table->store()) { - $this->setError($table->getError()); + $this->component->setError($table->getError()); return false; } diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index 588cdc4b..1a816333 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -27,6 +27,7 @@ use \Joomgallery\Component\Joomgallery\Administrator\Extension\ServiceTrait; use \Joomgallery\Component\Joomgallery\Administrator\Service\Migration\Checks; use \Joomgallery\Component\Joomgallery\Administrator\Service\Migration\MigrationInterface; +use stdClass; /** * Migration Base Class @@ -130,8 +131,8 @@ public function __construct() */ public function __destruct() { - // Set logger - $this->component->setLogger(null); + // Reset logger to default + $this->component->setLogger(); } /** @@ -155,6 +156,24 @@ public function getMigrateables(): array return $this->migrateables; } + /** + * Prepare the migration. + * + * @return MigrationTable The currently processed migrateable + * + * @since 4.0.0 + */ + public function prepareMigration(string $type) + { + // Load migrateables to migration service + $this->getMigrateables(); + + // Set the migration parameters + $this->params = $this->migrateables[$type]->params; + + return $this->migrateables[$type]; + } + /** * Step 2 * Perform pre migration checks. @@ -199,6 +218,9 @@ public function precheck(): array $this->checkImageMapping($checks, 'destination'); } + // Perform some script specific checks + $this->scriptSpecificChecks($checks, 'general'); + return $checks->getAll(); } @@ -879,4 +901,113 @@ protected function checkImageMapping(Checks &$checks, string $category) $checks->addCheck($category, 'mapping_dest_types', false, Text::_('COM_JOOMGALLERY_FIELDS_IMAGEMAPPING_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_MAPPING_IMAGETYPE_NOT_USED', \implode(', ', $tmp_dest_imagetypes))); } } + + /** + * Precheck: Perform script specific checks + * + * @param Checks $checks The checks object + * @param string $category The checks-category into which to add the new check + * + * @return void + * + * @since 4.0.0 + */ + protected function scriptSpecificChecks(Checks &$checks, string $category) + { + return; + } + + /** + * Converts a data array based on a mapping. + * + * @param array $data Data received from getData() method. + * @param array $mapping Mapping array telling how to convert data. + * + * @return array Converted data array + * + * @since 4.0.0 + */ + protected function applyConvertData(array $data, array $mapping): array + { + // Loop through the data provided + foreach($data as $key => $value) + { + // Key not in the mapping array --> Nothing to do. + if(!\key_exists($key, $mapping)) + { + continue; + } + + // Mapping from an old to a new key ('old key' => 'new key') + if(\is_string($mapping[$key]) && !empty($mapping[$key])) + { + $data[$mapping[$key]] = $value; + unset($data[$key]); + + continue; + } + + // Remove content from data array element ('old key' => false) + if($mapping[$key] === false) + { + $data[$mapping[$key]] = null; + + continue; + } + + + // Content gets merged into anothter data array element ('old key' => array()) + if(\is_array($mapping[$key])) + { + $destFieldName = $mapping[$key][0]; + + // Prepare destField + if(!\key_exists($destFieldName, $data) || empty($data[$destFieldName])) + { + // Field does not exist or is empty + $data[$destFieldName] = new Registry(); + } + elseif(!($data[$destFieldName] instanceof Registry)) + { + // Field exists and is already of type Registry + $data[$destFieldName] = new Registry($data[$destFieldName]); + } + + // Prepare srcField + if(!($value instanceof Registry)) + { + $value = new Registry($value); + } + + // Apply merge + if(\count($mapping[$key]) < 2 || empty($mapping[$key][1])) + { + // Merge the two Registry objects (merge into root node) + $data[$destFieldName]->merge($value); + } + else + { + if(!$data[$destFieldName]->exists($key)) + { + // Attach value as a child element to destField (add as a child node) + $data[$destFieldName]->set($key, $value); + } + else + { + // Merge value into a child element of destField (merge into child node) + $data[$destFieldName]->append($key, $value); + } + } + + if($key != $destFieldName) + { + unset($data[$key]); + } + + continue; + } + } + + return $data; + } } diff --git a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php index 694b370d..14f0c1b4 100644 --- a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php +++ b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php @@ -147,13 +147,14 @@ public function getData(string $type, int $pk): array; /** * Converts data from source into the structure needed for JoomGallery. * + * @param string $type Name of the content type * @param array $data Data received from getData() method. * * @return array Converted data to save into JoomGallery * * @since 4.0.0 */ - public function convertData(array $data): array; + public function convertData(string $type, array $data): array; /** * Fetches an array of images from source to be used for creating the imagetypes diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index 3b1fd70f..7a3bb175 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -15,6 +15,8 @@ use \Joomla\CMS\Language\Text; use \Joomla\CMS\Filesystem\Path; +use \Joomla\CMS\User\UserFactoryInterface; +use \Joomgallery\Component\Joomgallery\Administrator\Service\Migration\Checks; use \Joomgallery\Component\Joomgallery\Administrator\Service\Migration\Migration; use \Joomgallery\Component\Joomgallery\Administrator\Service\Migration\Targetinfo; use \Joomgallery\Component\Joomgallery\Administrator\Service\Migration\MigrationInterface; @@ -127,38 +129,6 @@ public function getSourceDirs(): array return $dirs; } - /** - * Returns tablename and primarykey name of the source table - * - * @param string $type The content type name - * - * @return array The corresponding source table info - * list(tablename, primarykey) - * - * @since 4.0.0 - */ - public function getSourceTableInfo(string $type): array - { - $tables = array( 'image' => array('#__joomgallery', 'id'), - 'category' => array('#__joomgallery_catg', 'cid') - ); - - if(!\in_array($type, \array_keys($tables))) - { - throw new \Exception('There is no migration source table associated with the given content type. Given: ' . $type, 1); - } - - if($this->params->get('same_db')) - { - foreach($tables as $key => $value) - { - $tables[$key][0] = $value[0] . '_old'; - } - } - - return $tables[$type]; - } - /** * Returns a list of involved source tables. * @@ -194,42 +164,157 @@ public function getSourceTables(): array return $tables; } + /** + * Returns tablename and primarykey name of the source table + * + * @param string $type The content type name + * + * @return array The corresponding source table info + * list(tablename, primarykey) + * + * @since 4.0.0 + */ + public function getSourceTableInfo(string $type): array + { + $tables = array( 'image' => array('#__joomgallery', 'id'), + 'category' => array('#__joomgallery_catg', 'cid') + ); + + if(!\in_array($type, \array_keys($tables))) + { + throw new \Exception('There is no migration source table associated with the given content type. Given: ' . $type, 1); + } + + if($this->params->get('same_db')) + { + foreach($tables as $key => $value) + { + $tables[$key][0] = $value[0] . '_old'; + } + } + + return $tables[$type]; + } + /** * Returns an associative array containing the record data from source. * * @param string $type Name of the content type * @param int $pk The primary key of the content type * - * @return array Record data + * @return array Associated array of a record data * * @since 4.0.0 */ public function getData(string $type, int $pk): array { - switch ($type) + // Get source table info + list($tablename, $primarykey) = $this->getSourceTableInfo($type); + + // Get db object + list($db, $prefix) = $this->getDB('source'); + $query = $db->getQuery(true); + + // Create the query + $query->select('*') + ->from($db->quoteName($tablename)) + ->where($db->quoteName($primarykey) . ' = ' . $db->quote($pk)); + + // Attempt to load the array + try { - case 'category': - return $this->getCategoryData($pk); - break; - - default: - return array(); - break; + return $db->loadAssoc(); + } + catch(\Exception $e) + { + $this->component->setError($e->getMessage()); + + return array(); } } /** * Converts data from source into the structure needed for JoomGallery. * + * @param string $type Name of the content type * @param array $data Data received from getData() method. * * @return array Converted data to save into JoomGallery * * @since 4.0.0 */ - public function convertData(array $data): array + public function convertData(string $type, array $data): array { - return array(); + /* How mappings work: + - Key not in the mapping array: Nothing changes. Field value can be magrated as it is. + - 'old key' => 'new key': Field name has changed. Old values will be inserted in field with the provided new key. + - 'old key' => false: Field does not exist anymore or value has to be emptied to create new record in the new table. + - 'old key' => array(): Field was merget into another field of type json. array('dest. field', 'child-field name within dest. field'). + If the second element of the array is 'false' means that it will be merged directly into dest. field without creating a child field in it. + */ + + // The fieldname of owner (created_by) + $ownerFieldName = 'owner'; + + // Parameter dependet mapping fields + $id = \boolval($this->params->get('source_ids', 0)) ? 'id' : false; + $owner = \boolval($this->params->get('check_owner', 0)) ? 'created_by' : false; + + // Configure mapping for each content type + switch($type) + { + case 'category': + // Apply mapping for category table + $mapping = array( 'cid' => $id, 'asset_id' => false, 'name' => 'title', 'alias' => false, 'lft' => false, 'rgt' => false, 'level' => false, + 'owner' => $owner, 'img_position' => false, 'catpath' => 'path', 'params' => array('params', false), + 'allow_download' => array('params', 'jg_download'), 'allow_comment' => array('params', 'jg_showcomment'), 'allow_rating' => array('params', 'jg_showrating'), + 'allow_watermark' => array('params', 'jg_dynamic_watermark'), 'allow_watermark_download' => array('params', 'jg_downloadwithwatermark') + ); + + break; + + case 'image': + // Apply mapping for image table + $mapping = array( 'id' => $id, 'asset_id' => false, 'alias' => false, 'imgfilename' => 'filename', 'imgthumbname' => false, + 'owner' => $owner, 'params' => array('params', false) + ); + + // Check difference between imgfilename and imgthumbname + if($data['imgfilename'] !== $data['imgthumbname']) + { + $this->component->setError(Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_FILENAME_DIFF', $data['id'], $data['alias'])); + + return false; + } + + // Adjust catid with new created categories + if(!\boolval($this->params->get('source_ids', 0))) + { + $data['catid'] = $this->migrateables['category']->successful->get($data['catid']); + } + + break; + + default: + // The table structure is the same + $mapping = array('id' => $id, 'owner' => $owner); + + break; + } + + // Check owner + if(\boolval($this->params->get('check_owner', 0))) + { + // Check if user with the provided userid exists + $user = Factory::getContainer()->get(UserFactoryInterface::class)->loadUserById($data[$ownerFieldName]); + if(!$user || !$user->id) + { + $data[$ownerFieldName] = 0; + } + } + + // Apply mapping + return $this->applyConvertData($data, $mapping); } /** @@ -250,16 +335,36 @@ public function getImageSource(array $data): array } /** - * Returns an associative array containing the category data from source. - * - * @param int $pk The primary key of the category - * - * @return array Category data array + * Precheck: Perform script specific checks * + * @param Checks $checks The checks object + * @param string $category The checks-category into which to add the new check + * + * @return void + * * @since 4.0.0 - */ - public function getCategoryData(int $pk): array + */ + protected function scriptSpecificChecks(Checks &$checks, string $category) { - return array(); + // Check if imgfilename and imgthumbname are the same + list($db, $dbPrefix) = $this->getDB('source'); + list($tablename, $pkname) = $this->getSourceTableInfo('image'); + + // Create the query + $query = $db->getQuery(true) + ->select($db->quoteName(array('id'))) + ->from($tablename) + ->where($db->quoteName('imgfilename') . ' != ' . $db->quoteName('imgthumbname')); + $db->setQuery($query); + + // Load a list of ids that have different values for imgfilename and imgthumbname + $res = $db->loadColumn(); + + if(!empty(\count($res))) + { + $checks->addCheck($category, 'src_table_image_filename', false, Text::_('FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_TITLE'), Text::sprintf('FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_DESC', \count($res)), Text::sprintf('FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_HELP', \implode(', ', $res))); + } + + return; } } \ No newline at end of file diff --git a/administrator/com_joomgallery/tmpl/migration/step2.php b/administrator/com_joomgallery/tmpl/migration/step2.php index 43f86b94..ae53d8af 100644 --- a/administrator/com_joomgallery/tmpl/migration/step2.php +++ b/administrator/com_joomgallery/tmpl/migration/step2.php @@ -90,7 +90,12 @@ desc; ?> result ? 'success' : 'failed'; ?> - + + + @@ -111,5 +116,29 @@ - -
\ No newline at end of file + true, + 'title' => 'Test Title', + 'footer' => '', + ); + + echo HTMLHelper::_('bootstrap.renderModal', 'help-modal-box', $options, ''); + ?> +
+ + \ No newline at end of file From 26465cc1b07e5ebb6766c8bea5b041aadbc99c5e Mon Sep 17 00:00:00 2001 From: Elfangor Date: Tue, 7 Nov 2023 19:35:31 +0100 Subject: [PATCH 028/121] buttons fix --- .../com_joomgallery/src/Model/MigrationModel.php | 2 +- .../com_joomgallery/src/View/Migration/HtmlView.php | 5 +++-- administrator/com_joomgallery/tmpl/migration/default.php | 9 ++++++--- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index 4e937cd0..abadcb3e 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -382,7 +382,7 @@ public function getIdList(): array $query = $db->getQuery(true); // Select the required fields from the table. - $query->select(array('a.id', 'a.script', 'a.type')); + $query->select(array('a.id', 'a.script', 'a.type', 'a.checked_out')); $query->from($db->quoteName(_JOOM_TABLE_MIGRATION, 'a')); $db->setQuery($query); diff --git a/administrator/com_joomgallery/src/View/Migration/HtmlView.php b/administrator/com_joomgallery/src/View/Migration/HtmlView.php index 05aba02f..e8dd8332 100644 --- a/administrator/com_joomgallery/src/View/Migration/HtmlView.php +++ b/administrator/com_joomgallery/src/View/Migration/HtmlView.php @@ -99,8 +99,6 @@ public function display($tpl = null) break; default: - // ID list of open migrations - $this->openMigrations = $this->get('IdList'); break; } } @@ -111,6 +109,9 @@ public function display($tpl = null) { $this->app->getLanguage()->load('com_joomgallery.migration.'.$script['name'], _JOOM_PATH_ADMIN); } + + // ID list of open migrations + $this->openMigrations = $this->get('IdList'); } // Check for errors. diff --git a/administrator/com_joomgallery/tmpl/migration/default.php b/administrator/com_joomgallery/tmpl/migration/default.php index 1d0eae04..d73cbfd2 100644 --- a/administrator/com_joomgallery/tmpl/migration/default.php +++ b/administrator/com_joomgallery/tmpl/migration/default.php @@ -28,6 +28,9 @@
scripts as $name => $script) : ?> + openMigrations[$name] ? true : false; + ?>
@@ -40,16 +43,16 @@

- + - + - + From 46c4001d33ba57fa04dce759b0e7002d9e47d234 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Tue, 7 Nov 2023 22:07:07 +0100 Subject: [PATCH 029/121] border style: active migration --- .../language/en-GB/com_joomgallery.ini | 3 ++- .../tmpl/migration/default.php | 25 ++++++++++++++----- media/com_joomgallery/css/admin.css | 10 ++++++++ 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index 615b403a..e39a6b48 100755 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -194,7 +194,7 @@ COM_JOOMGALLERY_FILE_DESCRIPTION_HINT="Description of this file" COM_JOOMGALLERY_FILE_AUTHOR_HINT="Author of this file" COM_JOOMGALLERY_MIGRATION_START_SCRIPT="Start script" COM_JOOMGALLERY_MIGRATION_AVAILABLE_SCRIPTS="Available migration scripts" -COM_JOOMGALLERY_MIGRATION_ABORT_MIGRATION="Abort open migrations" +COM_JOOMGALLERY_MIGRATION_ABORT_MIGRATION="Abort migration" COM_JOOMGALLERY_MIGRATION_SCRIPT_NOT_EXIST="The requested migration script does not exist. Please provide an available script name in the request." COM_JOOMGALLERY_MIGRATION_STEP1_BTN_TXT="Check migration capability" COM_JOOMGALLERY_MIGRATION_STEP2_BTN_TXT="Start migration manager" @@ -699,6 +699,7 @@ COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_CONVERT_DATA="Data from source could no COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_INSERT_RECORD="Data could not be inserted into destination database. See log file for more destails." COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_CREATE_IMGTYPE="Error in creating imagetypes. See log file for more destails." COM_JOOMGALLERY_SERVICE_MIGRATION_FILENAME_DIFF="It was noticed that two different filenames were used for detail/original and thumbnail in the image with id: %s and alias: %s. The migration script will skip the migration of this image." +COM_JOOMGALLERY_SERVICE_MIGRATION_ACTIVE="Currently in migration" ;Plugin events COM_JOOMGALLERY_PLUGIN_ERROR_RETURN_VALUE="Return value of the plugin event '%s' must be of type %s and contain : %s" diff --git a/administrator/com_joomgallery/tmpl/migration/default.php b/administrator/com_joomgallery/tmpl/migration/default.php index d73cbfd2..a8de35cd 100644 --- a/administrator/com_joomgallery/tmpl/migration/default.php +++ b/administrator/com_joomgallery/tmpl/migration/default.php @@ -29,22 +29,31 @@ scripts as $name => $script) : ?> openMigrations[$name] ? true : false; + $openMigrations = false; + $openMigrationsIDs = ''; + + if(key_exists($name, $this->openMigrations)) + { + $openMigrations = true; + $openMigrationsIDs = $this->openMigrations[$name]->id; + } ?>
-
+

<?php echo $name; ?> logo
+ +
+ +

+

- - - @@ -52,7 +61,11 @@ - + + + + + diff --git a/media/com_joomgallery/css/admin.css b/media/com_joomgallery/css/admin.css index 306df0c6..abd7d713 100644 --- a/media/com_joomgallery/css/admin.css +++ b/media/com_joomgallery/css/admin.css @@ -70,6 +70,16 @@ img.jg-controlpanel-logo { .modal .modal-body { margin: 1rem 2rem; } +.card.border { + border: 2px solid var(--dark) !important; +} +.card.border.active, +.card.border.success { + border-color: var(--success) !important; +} +.card.border.error { + border-color: var(--danger) !important; +} .controls .choices__inner.is-valid { border-color: #457d54; padding-right: calc(1.5em + 1rem); From 8a83678d330b6d942f8b599dd8af120898fb58ec Mon Sep 17 00:00:00 2001 From: Elfangor Date: Sat, 11 Nov 2023 12:47:22 +0100 Subject: [PATCH 030/121] update resume method --- .../src/Controller/MigrationController.php | 71 ++++++++++--------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/administrator/com_joomgallery/src/Controller/MigrationController.php b/administrator/com_joomgallery/src/Controller/MigrationController.php index eb4485c5..7a186ec5 100644 --- a/administrator/com_joomgallery/src/Controller/MigrationController.php +++ b/administrator/com_joomgallery/src/Controller/MigrationController.php @@ -219,8 +219,8 @@ public function resume() // Set params data to user state $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.params', $item->params); - // Redirect to the from screen (step 1). - $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration&layout=step1', false)); + // Redirect to the from screen (step 2). + $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&task=migration.precheck&isNew=0', false)); return true; } @@ -324,44 +324,51 @@ public function precheck() return false; } - // Validate the posted data. - $form = $model->getForm($data, false); - - // Send an object which can be modified through the plugin event - $objData = (object) $data; - $this->app->triggerEvent('onContentNormaliseRequestData', [$context, $objData, $form]); - $data = (array) $objData; + if($isNew = $this->input->post->get('isNew', false,'bool')) + { + // Validate the posted data. + $form = $model->getForm($data, false); - // Test whether the data is valid. - $validData = $model->validate($form, $data); + // Send an object which can be modified through the plugin event + $objData = (object) $data; + $this->app->triggerEvent('onContentNormaliseRequestData', [$context, $objData, $form]); + $data = (array) $objData; - // Check for validation errors. - if($validData === false) - { - // Get the validation messages. - $errors = $model->getErrors(); + // Test whether the data is valid. + $validData = $model->validate($form, $data); - // Push up to three validation messages out to the user. - for($i = 0, $n = \count($errors); $i < $n && $i < 3; $i++) + // Check for validation errors. + if($validData === false) { - if($errors[$i] instanceof \Exception) - { - $this->app->enqueueMessage($errors[$i]->getMessage(), 'warning'); - } - else - { - $this->app->enqueueMessage($errors[$i], 'warning'); - } - } + // Get the validation messages. + $errors = $model->getErrors(); - // Save the form data in the session. - $this->app->setUserState($context . '.data', $data); + // Push up to three validation messages out to the user. + for($i = 0, $n = \count($errors); $i < $n && $i < 3; $i++) + { + if($errors[$i] instanceof \Exception) + { + $this->component->setWarning($errors[$i]->getMessage()); + } + else + { + $this->component->setWarning($errors[$i]); + } + } - // Redirect back to the edit screen. - $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration', false)); + // Save the form data in the session. + $this->app->setUserState($context . '.data', $data); - return false; + // Redirect back to the edit screen. + $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration', false)); + + return false; + } } + else + { + $validData = $this->app->getUserState(_JOOM_OPTION.'.migration.'.$script.'.params', array()); + } // Save the script name in the session. $this->app->setUserState(_JOOM_OPTION.'.migration.script', $script); From b8142b820ada7a86936c9f507bf4b7891a3000e8 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Sat, 11 Nov 2023 17:38:19 +0100 Subject: [PATCH 031/121] fix getItems method --- .../language/en-GB/com_joomgallery.ini | 8 +- .../com_joomgallery.migration.Jg3ToJg4.ini | 2 +- .../src/Controller/MigrationController.php | 84 +++++++++++-- .../src/Model/MigrationModel.php | 117 +++++++++++------- .../src/Service/Migration/Checks.php | 35 ++++-- .../src/Service/Migration/Migration.php | 104 ++++++++++------ .../Service/Migration/MigrationInterface.php | 13 ++ .../Service/Migration/Scripts/Jg3ToJg4.php | 30 ++++- .../src/Table/MigrationTable.php | 7 +- .../com_joomgallery/tmpl/migration/step2.php | 29 ++++- .../com_joomgallery/tmpl/migration/step3.php | 6 +- 11 files changed, 315 insertions(+), 120 deletions(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index e39a6b48..e7d2606a 100755 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -93,6 +93,10 @@ COM_JOOMGALLERY_ROOT_CAT_ASSET="Root Category Asset" COM_JOOMGALLERY_PATH="Path" COM_JOOMGALLERY_TABLE="Table" COM_JOOMGALLERY_RESUME="Resume" +COM_JOOMGALLERY_SUCCESSFUL="Successful" +COM_JOOMGALLERY_FAILED="Failed" +COM_JOOMGALLERY_WARNING="Warning" +COM_JOOMGALLERY_PENDING="Pending" ;Control panel COM_JOOMGALLERY_CONTROL_PANEL="Control Panel" @@ -294,6 +298,7 @@ COM_JOOMGALLERY_ERROR_IMGTYPE_TABLE_NOT_EXISTING="Table for content type '%s' do COM_JOOMGALLERY_ERROR_TABLE_NOT_EXISTING="Table does not exist." COM_JOOMGALLERY_ERROR_PATH_NOT_EXISTING="Path does not exist." COM_JOOMGALLERY_ERROR_TASK_NOT_PERMITTED="You are not permitted to perform this task. Requested task: %s" +COM_JOOMGALLERY_ERROR_CHECKED_OUT_BY_ANOTHER_USER="This is item is currently checked out by another user (name: %s). You are not allowed to view or modify this item as long as it is checked out." ;Mail Templates COM_JOOMGALLERY_MAIL_NEWIMAGE_TITLE="JoomGallery: New Image" @@ -659,7 +664,7 @@ COM_JOOMGALLERY_SERVICE_ERROR_CREATE_FILE="File could not be created." COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_NOT_A_DIRECTORY="Requested path is not a directory." COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_NOT_FOUND="No filesystem plugin available for the specified adapter (%s)." COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_ERROR="Filesystem plugin is reporting the following error: %s" -COM_JOOMGALLERY_SERVICE_MIGRATION_PRECHECK_TITLE="Results of the pre migration checks" +COM_JOOMGALLERY_SERVICE_MIGRATION_PRECHECK_TITLE="Check results" COM_JOOMGALLERY_SERVICE_MIGRATION_GENERAL_CHECK_DESC="General checks like log file, site state, ..." COM_JOOMGALLERY_SERVICE_MIGRATION_SOURCE_CHECK_DESC="Check source extension, directories and tables if they are compatible and existent." COM_JOOMGALLERY_SERVICE_MIGRATION_DESTINATION_CHECK_DESC="Check destination extension, directories and tables if they are compatible, existent and writeable." @@ -677,6 +682,7 @@ COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_SUCCESS="Extension is compatible (Ex COM_JOOMGALLERY_SERVICE_MIGRATION_PHP_WRONG_VERSION="The current PHP version is not supported. Current version: %s. Minimum requirement: %s" COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP2="Step 2: Migration pre-check failed. Reason: %s" COM_JOOMGALLERY_SERVICE_MIGRATION_SUCCESS_MIGRATION_STEP2="Step 2: Migration pre-check successful." +COM_JOOMGALLERY_SERVICE_MIGRATION_WARNING_MIGRATION_STEP2="Step 2: Warning appeared during migration pre-check. Reason: %s" COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP2_CHECKS_FAILED="Some of the checks failed." COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES="There are already %s records in this table." COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES_EMPTY="This table is empty." diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini index b71df952..07359612 100644 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini @@ -16,4 +16,4 @@ FILES_JOOMGALLERY_MIGRATION_IMAGE_TITLE="Migration: Images" FILES_JOOMGALLERY_MIGRATION_CATEGORY_TITLE="Migration: Categories" FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_TITLE="Image filenames" FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_DESC="There are different filenames for thumbnail and detail image defined in the database of your JG3 tables. Number of affected images: %s" -FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_HELP="This script is currently not able to solve this issue automatically. You have to make the adjustments by yourself before you can use the script. For all the affected Images you have to do the following:
  • Rename the thumbnail image in the filesystem to be the one set in the old JoomGallery image table at row 'imgfilename'.
  • In the old JoomGallery image table, set the value of the row 'imgthumbname' to be the same as in row 'imgfilename'.

ID's of affected images: (%s)" \ No newline at end of file +FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_HELP="It was detected that for some images in the database table of the JG3 (source, #__com_joomgallery) different filenames are stored in the rows 'imgfilename' and 'imgthumbname'. This script could result in errorous migration results. We strongly advise to clean up the database manually before starting the migration. For all the affected Images you have to do the following:
  • Rename the thumbnail image in the filesystem to be the one set in the old JoomGallery image table at row 'imgfilename'.
  • In the old JoomGallery image table, set the value of the row 'imgthumbname' to be the same as in row 'imgfilename'.

ID's of affected images: (%s)

If you face any problems, call for help in our forum at https://www.forum.en.joomgalleryfriends.net" \ No newline at end of file diff --git a/administrator/com_joomgallery/src/Controller/MigrationController.php b/administrator/com_joomgallery/src/Controller/MigrationController.php index 7a186ec5..129b7fd6 100644 --- a/administrator/com_joomgallery/src/Controller/MigrationController.php +++ b/administrator/com_joomgallery/src/Controller/MigrationController.php @@ -15,16 +15,17 @@ use \Joomla\CMS\Factory; use \Joomla\CMS\Uri\Uri; use \Joomla\Input\Input; +use \Joomla\CMS\Log\Log; use \Joomla\CMS\Language\Text; use \Joomla\CMS\Router\Route; -use \Joomla\CMS\Log\Log; use \Joomla\Registry\Registry; +use Joomla\CMS\Session\Session; use \Joomla\CMS\Response\JsonResponse; -use \Joomla\CMS\MVC\Controller\BaseController; use \Joomla\CMS\Application\CMSApplication; -use \Joomla\CMS\Form\FormFactoryAwareInterface; +use \Joomla\CMS\MVC\Controller\BaseController; use \Joomla\CMS\Form\FormFactoryAwareTrait; use \Joomla\CMS\Form\FormFactoryInterface; +use \Joomla\CMS\Form\FormFactoryAwareInterface; use \Joomla\CMS\MVC\Factory\MVCFactoryInterface; use \Joomgallery\Component\Joomgallery\Administrator\Extension\JoomgalleryComponent; use stdClass; @@ -147,6 +148,7 @@ public function cancel() // Clean the session data and redirect. $this->app->setUserState(_JOOM_OPTION.'.migration.script', null); $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.params', null); + $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.noToken', null); $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.step2.data', null); $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.step2.results', null); $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.step2.success', null); @@ -187,7 +189,7 @@ public function resume() $acl = $this->component->getAccess(); if(!$acl->checkACL('admin', 'com_joomgallery')) { - $this->setMessage(Text::sprintf('COM_JOOMGALLERY_ERROR_TASK_NOT_PERMITTED', 'migration.start'), 'error'); + $this->setMessage(Text::sprintf('COM_JOOMGALLERY_ERROR_TASK_NOT_PERMITTED', 'migration.start', 'error')); $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration', false)); return false; @@ -200,17 +202,28 @@ public function resume() if($id < 1) { - $this->setMessage(Text::_('COM_JOOMGALLERY_SERVICE_ERROR_MIGRATION_RESUME'), 'error'); + $this->setMessage(Text::_('COM_JOOMGALLERY_SERVICE_ERROR_MIGRATION_RESUME', 'error')); $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration', false)); return false; } - // Attempt to load the migration items + // Attempt to load the migration item $item = $model->getItem($id); if(!$item || $item->script != $script) { - $this->setMessage(Text::_('COM_JOOMGALLERY_SERVICE_ERROR_MIGRATION_RESUME'), 'error'); + $this->setMessage(Text::_('COM_JOOMGALLERY_SERVICE_ERROR_MIGRATION_RESUME', 'error')); + $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration', false)); + + return false; + } + + // Check if migration item is checked out + $user = Factory::getUser(); + if(isset($item->checked_out) && !($item->checked_out == 0 || $item->checked_out == $user->get('id'))) + { + // You are not allowed to resume the migration, since it is checked out by another user + $this->setMessage(Text::sprintf('COM_JOOMGALLERY_ERROR_CHECKED_OUT_BY_ANOTHER_USER', $user->get('name')), 'error'); $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration', false)); return false; @@ -219,6 +232,9 @@ public function resume() // Set params data to user state $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.params', $item->params); + // Set no token check to user state + $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.noToken', true); + // Redirect to the from screen (step 2). $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&task=migration.precheck&isNew=0', false)); @@ -268,6 +284,27 @@ public function delete() // Get the model. $model = $this->getModel(); + // Attempt to load the migration item + $item = $model->getItem($cid[0]); + if(!$item || $item->script != $script) + { + $this->setMessage(Text::_('COM_JOOMGALLERY_SERVICE_ERROR_MIGRATION_RESUME', 'error')); + $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration', false)); + + return false; + } + + // Check if migration item is checked out + $user = Factory::getUser(); + if(isset($item->checked_out) && !($item->checked_out == 0 || $item->checked_out == $user->get('id'))) + { + // You are not allowed to resume the migration, since it is checked out by another user + $this->setMessage(Text::sprintf('COM_JOOMGALLERY_ERROR_CHECKED_OUT_BY_ANOTHER_USER', $user->get('name')), 'error'); + $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration', false)); + + return false; + } + // Remove the items. if($model->delete($cid)) { @@ -296,11 +333,19 @@ public function delete() */ public function precheck() { + // Get script + $script = $this->app->getUserStateFromRequest(_JOOM_OPTION.'.migration.script', 'script', '', 'cmd'); + + // No token (When precheck is called on reume, no token check is needed) + $noToken = $this->app->getUserState(_JOOM_OPTION.'.migration.'.$script.'.noToken', false); + // Check for request forgeries - $this->checkToken(); + if(\is_null($noToken) && !$noToken) + { + $this->checkToken(); + } $model = $this->getModel(); - $script = $this->app->getUserStateFromRequest(_JOOM_OPTION.'.migration.script', 'script', '', 'cmd'); $scripts = $model->getScripts(); // Check if requested script exists @@ -324,7 +369,7 @@ public function precheck() return false; } - if($isNew = $this->input->post->get('isNew', false,'bool')) + if($isNew = $this->input->get('isNew', true,'bool')) { // Validate the posted data. $form = $model->getForm($data, false); @@ -387,6 +432,12 @@ public function precheck() { // Pre-checks successful. Show success message. $this->setMessage(Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_SUCCESS_MIGRATION_STEP2')); + + if(!empty($msg)) + { + // Warnings appeared. Show warning message. + $this->app->enqueueMessage(Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_WARNING_MIGRATION_STEP2', $msg), 'warning'); + } } // Save the results of the pre migration checks in the session. @@ -569,13 +620,13 @@ public function start() * * @param mixed $data The data returned to the frontend * @param bool $success True if everything was good, false otherwise - * @param array $error An array of error messages to be printed in the frontend + * @param mixed $error One or multiple error messages to be printed in the frontend * * @return string Response json string * * @since 4.0.0 */ - protected function createRespond($data, bool $success = true, array $error = null): string + protected function createRespond($data, bool $success = true, $error = null): string { $obj = new stdClass; @@ -600,7 +651,14 @@ protected function createRespond($data, bool $success = true, array $error = nul // Get error output if(!empty($error)) { - $obj->error = $error; + if(\is_array($error)) + { + $obj->error = $error; + } + else + { + \array_push($obj->error, $error); + } } return \json_encode($obj, JSON_UNESCAPED_UNICODE); diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index abadcb3e..cf65a805 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -268,29 +268,37 @@ public function getItem($pk = null) $item->type = $type; } - // Add source and destination table info if empty + // Add destination table info if empty + if(empty($item->dst_table)) + { + $item->dst_table = JoomHelper::$content_types[$type]; + $item->dst_pk = 'id'; + } + + // We can not go further without a properly loaded migration service + if(\is_null($this->component->getMigration()) || \is_null($this->component->getMigration()->get('params'))) + { + return $item; + } + + // Add source table info if empty if(empty($item->src_table)) { // Get table information list($src_table, $src_pk) = $this->component->getMigration()->getSourceTableInfo($type); $item->src_table = $src_table; $item->src_pk = $src_pk; - $item->dst_table = JoomHelper::$content_types[$type]; - $item->dst_pk = 'id'; } // Add queue if empty - if(\is_null($item->queue) || empty($item->queue)) + if((\is_null($item->queue) || empty($item->queue))) { // Load queue $item->queue = $this->getQueue($type, $item); } // Add params - if($this->component->getMigration()->get('params')) - { - $item->params = $this->component->getMigration()->get('params'); - } + $item->params = $this->component->getMigration()->get('params'); // Empty type storage $this->tmp_type = null; @@ -332,6 +340,15 @@ public function getItems(): array return array(); } + $table = $this->getTable(); + $tmp_pk = null; + if($this->app->input->exists($table->getKeyName())) + { + // Remove id from the input data + $tmp_pk = $this->app->input->get($table->getKeyName(), 'int'); + $this->app->input->set($table->getKeyName(), null); + } + $items = array(); foreach($types as $key => $type) { @@ -363,6 +380,12 @@ public function getItems(): array array_push($items, $item); } + // Reset id to input data + if(!\is_null($tmp_pk)) + { + $this->app->input->set($table->getKeyName(), $tmp_pk); + } + return $items; } @@ -590,55 +613,59 @@ public function migrate(string $type, int $pk): object $error_msg = ''; // Prepare migration service and return migrateable object + $this->component->createMigration($this->app->getUserStateFromRequest(_JOOM_OPTION.'.migration.script', 'script', '', 'cmd')); $mig = $this->component->getMigration()->prepareMigration($type); - // Get record data from source - if($data = $this->component->getMigration()->getData($type, $pk)) + if($this->component->getMigration()->needsMigration($type, $pk)) { - // Convert record data into structure needed for JoomGallery v4+ - $data = $this->component->getMigration()->convertData($data); - - if(!$data) - { - $success = false; - $error_msg = Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_CONVERT_DATA'); - } - else + // Get record data from source + if($data = $this->component->getMigration()->getData($type, $pk)) { - // Create new record based on data array - $sameIDs = \boolval($mig->params->get('source_ids', 0)); - $record = $this->insertRecord($type, $data, $sameIDs); + // Convert record data into structure needed for JoomGallery v4+ + $data = $this->component->getMigration()->convertData($type, $data); - // Set primary key value of new created record - $new_pk = $record->id; - - if(!$record) + if(!$data) { $success = false; - $error_msg = Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_INSERT_RECORD'); + $error_msg = Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_CONVERT_DATA'); } else { - // Recreate images - if($type === 'image') - { - $img_source = $this->component->getMigration()->getImageSource($data); - if(\array_key_first($img_source) === 0) - { - // Create imagetypes based on given image and mapping - $res = $this->createImages($record, $img_source[0]); - } - else - { - // Reuse images from source as imagetypes (no image creation) - $res = $this->reuseImages($record, $img_source); - } + // Create new record based on data array + $sameIDs = \boolval($mig->params->get('source_ids', 0)); + $record = $this->insertRecord($type, $data, $sameIDs); - if(!$res) + // Set primary key value of new created record + $new_pk = $record->id; + + if(!$record) + { + $success = false; + $error_msg = Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_INSERT_RECORD'); + } + else + { + // Recreate images + if($type === 'image') { - $record = $this->deleteRecord($type, $new_pk); - $success = false; - $error_msg = Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_CREATE_IMGTYPE'); + $img_source = $this->component->getMigration()->getImageSource($data); + if(\array_key_first($img_source) === 0) + { + // Create imagetypes based on given image and mapping + $res = $this->createImages($record, $img_source[0]); + } + else + { + // Reuse images from source as imagetypes (no image creation) + $res = $this->reuseImages($record, $img_source); + } + + if(!$res) + { + $record = $this->deleteRecord($type, $new_pk); + $success = false; + $error_msg = Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_CREATE_IMGTYPE'); + } } } } diff --git a/administrator/com_joomgallery/src/Service/Migration/Checks.php b/administrator/com_joomgallery/src/Service/Migration/Checks.php index 2a3a054d..8bf3f21d 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Checks.php +++ b/administrator/com_joomgallery/src/Service/Migration/Checks.php @@ -148,6 +148,7 @@ public function modCategory(string $name, $title = null, $desc = null) * @param string $category The category of the check * @param string $name The name of the check * @param bool $result True if the check was successful, false otherwise + * @param bool $warning True if the check should be displayed as a warning * @param string $title Optional: Title of the check * @param string $desc Optional: Description of the check * @param string $help Optional: URL to a help-site or help-text @@ -157,7 +158,7 @@ public function modCategory(string $name, $title = null, $desc = null) * @since 4.0.0 * @throws \Exception */ - public function addCheck(string $category, string $name, bool $result, string $title = '', string $desc = '', string $help = '') + public function addCheck(string $category, string $name, bool $result, bool $warning = false, string $title = '', string $desc = '', string $help = '') { // Make category and check name lowercase $category = \strtolower(\trim($category)); @@ -178,11 +179,12 @@ public function addCheck(string $category, string $name, bool $result, string $t // Asset not yet existing, create a new one $check = new \stdClass(); - $check->name = $name; - $check->result = $result; - $check->title = $title; - $check->desc = $desc; - $check->help = $help; + $check->name = $name; + $check->result = $result; + $check->warning = $warning; + $check->title = $title; + $check->desc = $desc; + $check->help = $help; // Add check to check-objects array $key = $this->array_push($this->objects[$catKey]->checks, $check); @@ -200,6 +202,14 @@ public function addCheck(string $category, string $name, bool $result, string $t $this->message = $title . ' (' . $desc . ')'; } } + else + { + if($warning && $this->message === '') + { + // Add message if there is a warning + $this->message = $title . ' (' . $desc . ')'; + } + } } else { @@ -214,6 +224,7 @@ public function addCheck(string $category, string $name, bool $result, string $t * @param string $category The category of the check * @param string $name The name of the check * @param bool $result True if the check was successful, false otherwise + * @param bool $warning True if the check should be displayed as a warning * @param string $title Optional: Title of the check * @param string $desc Optional: Description of the check * @param string $help Optional: URL to a help-site or help-text @@ -223,7 +234,7 @@ public function addCheck(string $category, string $name, bool $result, string $t * @since 4.0.0 * @throws \Exception */ - public function modCheck(string $category, string $name, $result = null, $title = null, $desc = null, $help = null) + public function modCheck(string $category, string $name, $result = null, $warning = null, $title = null, $desc = null, $help = null) { // Make category and check name lowercase $category = \strtolower(\trim($category)); @@ -252,13 +263,19 @@ public function modCheck(string $category, string $name, $result = null, $title { $this->objects[$catKey]->checks[$checkKey]->result = \boolval($result); - // Modify the oversall success if needed + // Modify the overall success if needed if(\boolval($result) === false) { $this->success = false; } } + // Modify the warning status + if(!\is_null($warning)) + { + $this->objects[$catKey]->checks[$checkKey]->warning = \boolval($warning); + } + // Modify the title if(!\is_null($title)) { @@ -306,7 +323,7 @@ public function getSuccess(): bool /** * Returns the registered checks and the overall success * - * @return array array($this->success, $this->objects) + * @return array array($this->success, $this->objects, $this->message) * * @since 4.0.0 */ diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index 1a816333..a2aa6b40 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -169,9 +169,19 @@ public function prepareMigration(string $type) $this->getMigrateables(); // Set the migration parameters - $this->params = $this->migrateables[$type]->params; + $migrateableKey = 0; + foreach($this->migrateables as $key => $migrateable) + { + if($migrateable->type == $type) + { + $this->setParams($migrateable->params); + $migrateableKey = $key; + + continue; + } + } - return $this->migrateables[$type]; + return $this->migrateables[$migrateableKey]; } /** @@ -283,6 +293,18 @@ public function getDB(string $target): array return array($db, $dbPrefix); } + /** + * Set params to object + * + * @param mixed $params Array or object of params + * + * @since 4.0.0 + */ + public function setParams($params) + { + $this->params = new Registry($params); + } + /** * Returns the Joomla root path of the source. * @@ -331,28 +353,28 @@ protected function checkLogFile(Checks &$checks, string $category) { if(\is_writable($log_dir)) { - $checks->addCheck($category, 'log_file', true, Text::_('COM_JOOMGALLERY_LOGFILE'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_LOGFILE_SUCCESS', $log_file)); + $checks->addCheck($category, 'log_file', true, false, Text::_('COM_JOOMGALLERY_LOGFILE'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_LOGFILE_SUCCESS', $log_file)); } else { - $checks->addCheck($category, 'log_file', false, Text::_('COM_JOOMGALLERY_LOGFILE'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_LOGFILE_ERROR', $log_file)); + $checks->addCheck($category, 'log_file', false, false, Text::_('COM_JOOMGALLERY_LOGFILE'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_LOGFILE_ERROR', $log_file)); } } else { if(\is_writable($log_dir)) { - $checks->addCheck($category, 'log_dir', true, Text::_('COM_JOOMGALLERY_LOGDIRECTORY'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_LOGDIR_SUCCESS', $log_dir)); + $checks->addCheck($category, 'log_dir', true, false, Text::_('COM_JOOMGALLERY_LOGDIRECTORY'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_LOGDIR_SUCCESS', $log_dir)); } else { - $checks->addCheck($category, 'log_dir', false, Text::_('COM_JOOMGALLERY_LOGDIRECTORY'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_LOGDIR_ERROR', $log_dir)); + $checks->addCheck($category, 'log_dir', false, false, Text::_('COM_JOOMGALLERY_LOGDIRECTORY'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_LOGDIR_ERROR', $log_dir)); } } } else { - $checks->addCheck($category, 'log_dir', false, Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_LOG_DIR_LABEL'), Text::_('Logging directory not existent.')); + $checks->addCheck($category, 'log_dir', false, false, Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_LOG_DIR_LABEL'), Text::_('Logging directory not existent.')); } } @@ -375,22 +397,22 @@ protected function checkSourceExtension(Checks &$checks, string $category) if(\version_compare(PHP_VERSION, $src_info->get('php_min'), '<')) { // PHP version not supported - $checks->addCheck($category, 'src_extension', false, Text::_('COM_JOOMGALLERY_FIELDS_SRC_EXTENSION_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_PHP_WRONG_VERSION', PHP_VERSION, $src_info->get('php_min'))); + $checks->addCheck($category, 'src_extension', false, false, Text::_('COM_JOOMGALLERY_FIELDS_SRC_EXTENSION_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_PHP_WRONG_VERSION', PHP_VERSION, $src_info->get('php_min'))); } elseif(\strval($src_xml->name) !== $src_info->get('extension')) { // Wrong source extension - $checks->addCheck($category, 'src_extension', false, Text::_('COM_JOOMGALLERY_FIELDS_SRC_EXTENSION_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_NOT_SUPPORTED', \strval($src_xml->name))); + $checks->addCheck($category, 'src_extension', false, false, Text::_('COM_JOOMGALLERY_FIELDS_SRC_EXTENSION_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_NOT_SUPPORTED', \strval($src_xml->name))); } elseif(\version_compare($src_xml->version, $src_info->get('min'), '<') || \version_compare($src_xml->version, $src_info->get('max'), '>')) { // Version not correct - $checks->addCheck($category, 'src_extension', false, Text::_('COM_JOOMGALLERY_FIELDS_SRC_EXTENSION_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_WRONG_VERSION', $src_xml->version, $src_info->get('min') . ' - ' . $src_info->get('max'))); + $checks->addCheck($category, 'src_extension', false, false, Text::_('COM_JOOMGALLERY_FIELDS_SRC_EXTENSION_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_WRONG_VERSION', $src_xml->version, $src_info->get('min') . ' - ' . $src_info->get('max'))); } else { // Check successful - $checks->addCheck($category, 'src_extension', true, Text::_('COM_JOOMGALLERY_FIELDS_SRC_EXTENSION_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_SUCCESS', \strval($src_xml->name), $src_xml->version)); + $checks->addCheck($category, 'src_extension', true, false, Text::_('COM_JOOMGALLERY_FIELDS_SRC_EXTENSION_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_SUCCESS', \strval($src_xml->name), $src_xml->version)); } } @@ -412,22 +434,22 @@ protected function checkDestExtension(Checks &$checks, string $category) if(\version_compare(PHP_VERSION, $dest_info->get('php_min'), '<')) { // PHP version not supported - $checks->addCheck($category, 'dest_extension', false, Text::_('COM_JOOMGALLERY_FIELDS_SRC_EXTENSION_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_PHP_WRONG_VERSION', PHP_VERSION, $dest_info->get('php_min'))); + $checks->addCheck($category, 'dest_extension', false, false, Text::_('COM_JOOMGALLERY_FIELDS_SRC_EXTENSION_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_PHP_WRONG_VERSION', PHP_VERSION, $dest_info->get('php_min'))); } elseif(\strval($this->component->xml->name) !== $dest_info->get('extension')) { // Wrong destination extension - $checks->addCheck($category, 'dest_extension', false, Text::_('COM_JOOMGALLERY_FIELDS_DEST_EXTENSION_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_NOT_SUPPORTED', \strval($this->component->xml->name))); + $checks->addCheck($category, 'dest_extension', false, false, Text::_('COM_JOOMGALLERY_FIELDS_DEST_EXTENSION_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_NOT_SUPPORTED', \strval($this->component->xml->name))); } elseif(\version_compare($version, $dest_info->get('min'), '<') || \version_compare($version, $dest_info->get('max'), '>')) { // Version not correct - $checks->addCheck($category, 'dest_extension', false, Text::_('COM_JOOMGALLERY_FIELDS_DEST_EXTENSION_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_WRONG_VERSION', $this->component->version, $dest_info->get('min') . ' - ' . $dest_info->get('max'))); + $checks->addCheck($category, 'dest_extension', false, false, Text::_('COM_JOOMGALLERY_FIELDS_DEST_EXTENSION_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_WRONG_VERSION', $this->component->version, $dest_info->get('min') . ' - ' . $dest_info->get('max'))); } else { // Check successful - $checks->addCheck($category, 'dest_extension', true, Text::_('COM_JOOMGALLERY_FIELDS_DEST_EXTENSION_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_SUCCESS', \strval($this->component->xml->name), $this->component->version)); + $checks->addCheck($category, 'dest_extension', true, false, Text::_('COM_JOOMGALLERY_FIELDS_DEST_EXTENSION_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_SUCCESS', \strval($this->component->xml->name), $this->component->version)); } } @@ -445,11 +467,11 @@ protected function checkSiteState(Checks &$checks, string $category) { if($this->app->get('offline')) { - $checks->addCheck($category, 'offline', true, Text::_('COM_JOOMGALLERY_SITE_OFFLINE'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_OFFLINE_SUCCESS')); + $checks->addCheck($category, 'offline', true, false, Text::_('COM_JOOMGALLERY_SITE_OFFLINE'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_OFFLINE_SUCCESS')); } else { - $checks->addCheck($category, 'offline', false, Text::_('COM_JOOMGALLERY_SITE_OFFLINE'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_OFFLINE_ERROR')); + $checks->addCheck($category, 'offline', false, false, Text::_('COM_JOOMGALLERY_SITE_OFFLINE'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_OFFLINE_ERROR')); } } @@ -476,11 +498,11 @@ protected function checkSourceDir(Checks &$checks, string $category) if(!\is_dir($root . $dir)) { // Path is not a directory - $checks->addCheck($category, $check_name, false, Text::_('COM_JOOMGALLERY_DIRECTORY') . ': ' . $dir, Text::_('COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_NOT_A_DIRECTORY')); + $checks->addCheck($category, $check_name, false, false, Text::_('COM_JOOMGALLERY_DIRECTORY') . ': ' . $dir, Text::_('COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_NOT_A_DIRECTORY')); } else { - $checks->addCheck($category, $check_name, true, Text::_('COM_JOOMGALLERY_DIRECTORY') . ': ' . $dir, Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_DIRECTORY_SUCCESS')); + $checks->addCheck($category, $check_name, true, false, Text::_('COM_JOOMGALLERY_DIRECTORY') . ': ' . $dir, Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_DIRECTORY_SUCCESS')); } } } @@ -515,13 +537,13 @@ protected function checkDestDir(Checks &$checks, string $category) catch(FileNotFoundException $msg) { // Path doesn't exist - $checks->addCheck($category, $check_name, false, Text::_('COM_JOOMGALLERY_DIRECTORY') . ': ' . $imagetype->path, Text::_('COM_JOOMGALLERY_ERROR_PATH_NOT_EXISTING')); + $checks->addCheck($category, $check_name, false, false, Text::_('COM_JOOMGALLERY_DIRECTORY') . ': ' . $imagetype->path, Text::_('COM_JOOMGALLERY_ERROR_PATH_NOT_EXISTING')); $error = true; } catch(\Exception $msg) { // Error in filesystem - $checks->addCheck($category, $check_name, false, Text::_('COM_JOOMGALLERY_DIRECTORY') . ': ' . $imagetype->path, Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_ERROR', $msg)); + $checks->addCheck($category, $check_name, false, false, Text::_('COM_JOOMGALLERY_DIRECTORY') . ': ' . $imagetype->path, Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_ERROR', $msg)); $error = true; } @@ -530,11 +552,11 @@ protected function checkDestDir(Checks &$checks, string $category) if($dir_info->type !== 'dir') { // Path is not a directory - $checks->addCheck($category, $check_name, false, Text::_('COM_JOOMGALLERY_DIRECTORY') . ': ' . $imagetype->path, Text::_('COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_NOT_A_DIRECTORY')); + $checks->addCheck($category, $check_name, false, false, Text::_('COM_JOOMGALLERY_DIRECTORY') . ': ' . $imagetype->path, Text::_('COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_NOT_A_DIRECTORY')); } else { - $checks->addCheck($category, $check_name, true, Text::_('COM_JOOMGALLERY_DIRECTORY') . ': ' . $imagetype->path, Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_DIRECTORY_SUCCESS')); + $checks->addCheck($category, $check_name, true, false, Text::_('COM_JOOMGALLERY_DIRECTORY') . ': ' . $imagetype->path, Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_DIRECTORY_SUCCESS')); } } } @@ -573,7 +595,7 @@ protected function checkSourceTable(Checks &$checks, string $category) // Check if required tables exists if(!\in_array(\str_replace('#__', $dbPrefix, $tablename), $tableList)) { - $checks->addCheck($category, $check_name, false, Text::_('COM_JOOMGALLERY_TABLE') . ': ' . $tablename, Text::_('COM_JOOMGALLERY_ERROR_TABLE_NOT_EXISTING')); + $checks->addCheck($category, $check_name, false, false, Text::_('COM_JOOMGALLERY_TABLE') . ': ' . $tablename, Text::_('COM_JOOMGALLERY_ERROR_TABLE_NOT_EXISTING')); continue; } @@ -588,11 +610,11 @@ protected function checkSourceTable(Checks &$checks, string $category) $check_name = 'dest_table_' . $tablename . '_count'; if($count == 0) { - $checks->addCheck($category, $check_name, true, Text::_('COM_JOOMGALLERY_TABLE') . ': ' . $tablename, Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES_EMPTY')); + $checks->addCheck($category, $check_name, true, false, Text::_('COM_JOOMGALLERY_TABLE') . ': ' . $tablename, Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES_EMPTY')); } else { - $checks->addCheck($category, $check_name, true, Text::_('COM_JOOMGALLERY_TABLE') . ': ' . $tablename, Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES', $count)); + $checks->addCheck($category, $check_name, true, false, Text::_('COM_JOOMGALLERY_TABLE') . ': ' . $tablename, Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES', $count)); } } } @@ -626,12 +648,12 @@ protected function checkDestTable(Checks &$checks, string $category) if($db->loadResult()) { - $checks->addCheck($category, 'dest_root_cat', true, Text::_('COM_JOOMGALLERY_ROOT_CATEGORY'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_CAT_SUCCESS')); + $checks->addCheck($category, 'dest_root_cat', true, false, Text::_('COM_JOOMGALLERY_ROOT_CATEGORY'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_CAT_SUCCESS')); $rootCat = true; } else { - $checks->addCheck($category, 'dest_root_cat', false, Text::_('COM_JOOMGALLERY_ROOT_CATEGORY'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_CAT_ERROR')); + $checks->addCheck($category, 'dest_root_cat', false, false, Text::_('COM_JOOMGALLERY_ROOT_CATEGORY'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_CAT_ERROR')); } // Check whether root asset exists @@ -644,11 +666,11 @@ protected function checkDestTable(Checks &$checks, string $category) if($rootAssetID = $db->loadResult()) { - $checks->addCheck($category, 'dest_root_asset', true, Text::_('COM_JOOMGALLERY_ROOT_ASSET'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_ASSET_SUCCESS')); + $checks->addCheck($category, 'dest_root_asset', true, false, Text::_('COM_JOOMGALLERY_ROOT_ASSET'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_ASSET_SUCCESS')); } else { - $checks->addCheck($category, 'dest_root_asset', false, Text::_('COM_JOOMGALLERY_ROOT_ASSET'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_ASSET_ERROR')); + $checks->addCheck($category, 'dest_root_asset', false, false, Text::_('COM_JOOMGALLERY_ROOT_ASSET'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_ASSET_ERROR')); } // Check whether root category asset exists @@ -661,11 +683,11 @@ protected function checkDestTable(Checks &$checks, string $category) if($db->loadResult()) { - $checks->addCheck($category, 'dest_root_cat_asset', true, Text::_('COM_JOOMGALLERY_ROOT_CAT_ASSET'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_CAT_ASSET_SUCCESS')); + $checks->addCheck($category, 'dest_root_cat_asset', true, false, Text::_('COM_JOOMGALLERY_ROOT_CAT_ASSET'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_CAT_ASSET_SUCCESS')); } else { - $checks->addCheck($category, 'dest_root_cat_asset', false, Text::_('COM_JOOMGALLERY_ROOT_CAT_ASSET'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_CAT_ASSET_ERROR')); + $checks->addCheck($category, 'dest_root_cat_asset', false, false, Text::_('COM_JOOMGALLERY_ROOT_CAT_ASSET'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ROOT_CAT_ASSET_ERROR')); } // Check required tables @@ -676,7 +698,7 @@ protected function checkDestTable(Checks &$checks, string $category) // Check if required tables exists if(!\in_array( \str_replace('#__', $dbPrefix, $tablename), $tableList)) { - $checks->addCheck($category, $check_name, false, Text::_('COM_JOOMGALLERY_TABLE') . ': ' . $tablename, Text::_('COM_JOOMGALLERY_ERROR_TABLE_NOT_EXISTING')); + $checks->addCheck($category, $check_name, false, false, Text::_('COM_JOOMGALLERY_TABLE') . ': ' . $tablename, Text::_('COM_JOOMGALLERY_ERROR_TABLE_NOT_EXISTING')); continue; } @@ -696,16 +718,16 @@ protected function checkDestTable(Checks &$checks, string $category) $check_name = 'dest_table_' . $tablename . '_count'; if($count == 0) { - $checks->addCheck($category, $check_name, true, Text::_('COM_JOOMGALLERY_TABLE') . ': ' . $tablename, Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES_EMPTY')); + $checks->addCheck($category, $check_name, true, false, Text::_('COM_JOOMGALLERY_TABLE') . ': ' . $tablename, Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES_EMPTY')); } elseif($this->params->get('source_ids', 0) > 0 && $count > 0) { - $checks->addCheck($category, $check_name, true, Text::_('COM_JOOMGALLERY_TABLE') . ': ' . $tablename, Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES', $count)); + $checks->addCheck($category, $check_name, true, false, Text::_('COM_JOOMGALLERY_TABLE') . ': ' . $tablename, Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES', $count)); $this->checkDestTableIdAvailability($checks, $category, $tablename); } else { - $checks->addCheck($category, $check_name, true, Text::_('COM_JOOMGALLERY_TABLE') . ': ' . $tablename, Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES', $count)); + $checks->addCheck($category, $check_name, true, false, Text::_('COM_JOOMGALLERY_TABLE') . ': ' . $tablename, Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES', $count)); } } } @@ -829,7 +851,7 @@ protected function checkDestTableIdAvailability(Checks &$checks, string $categor if(!empty($list)) { - $checks->addCheck($category, 'dest_table_' . $tablename . '_ids', false, Text::_('COM_JOOMGALLERY_TABLE') . ': ' . $tablename, Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES_USE_IDS_HINT', \implode(',', $list))); + $checks->addCheck($category, 'dest_table_' . $tablename . '_ids', false, false, Text::_('COM_JOOMGALLERY_TABLE') . ': ' . $tablename, Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES_USE_IDS_HINT', \implode(',', $list))); } } @@ -852,7 +874,7 @@ protected function checkImageMapping(Checks &$checks, string $category) // Check if mapping contains enough elements if(\count((array)$mapping) != \count($dest_imagetypes)) { - $checks->addCheck($category, 'mapping_count', false, Text::_('COM_JOOMGALLERY_FIELDS_IMAGEMAPPING_LABEL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_MAPPING_ERROR')); + $checks->addCheck($category, 'mapping_count', false, false, Text::_('COM_JOOMGALLERY_FIELDS_IMAGEMAPPING_LABEL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_MAPPING_ERROR')); return; } @@ -883,14 +905,14 @@ protected function checkImageMapping(Checks &$checks, string $category) else { // Destination imagetype in mapping does not exist - $checks->addCheck($category, 'mapping_dest_types_'.$mapVal->destination, false, Text::_('COM_JOOMGALLERY_FIELDS_IMAGEMAPPING_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_MAPPING_DEST_IMAGETYPE_NOT_EXIST', Text::_('COM_JOOMGALLERY_' . \strtoupper($mapVal->destination)))); + $checks->addCheck($category, 'mapping_dest_types_'.$mapVal->destination, false, false, Text::_('COM_JOOMGALLERY_FIELDS_IMAGEMAPPING_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_MAPPING_DEST_IMAGETYPE_NOT_EXIST', Text::_('COM_JOOMGALLERY_' . \strtoupper($mapVal->destination)))); return; } if(!\in_array($mapVal->source, $src_imagetypes)) { // Source imagetype in mapping does not exist - $checks->addCheck($category, 'mapping_src_types_'.$mapVal->source, false, Text::_('COM_JOOMGALLERY_FIELDS_IMAGEMAPPING_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_MAPPING_IMAGETYPE_NOT_EXIST', Text::_('COM_JOOMGALLERY_' . \strtoupper($mapVal->source)))); + $checks->addCheck($category, 'mapping_src_types_'.$mapVal->source, false, false, Text::_('COM_JOOMGALLERY_FIELDS_IMAGEMAPPING_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_MAPPING_IMAGETYPE_NOT_EXIST', Text::_('COM_JOOMGALLERY_' . \strtoupper($mapVal->source)))); return; } } @@ -898,7 +920,7 @@ protected function checkImageMapping(Checks &$checks, string $category) if(!empty($tmp_dest_imagetypes)) { // Destination imagetype not used in the mapping - $checks->addCheck($category, 'mapping_dest_types', false, Text::_('COM_JOOMGALLERY_FIELDS_IMAGEMAPPING_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_MAPPING_IMAGETYPE_NOT_USED', \implode(', ', $tmp_dest_imagetypes))); + $checks->addCheck($category, 'mapping_dest_types', false, false, Text::_('COM_JOOMGALLERY_FIELDS_IMAGEMAPPING_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_MAPPING_IMAGETYPE_NOT_USED', \implode(', ', $tmp_dest_imagetypes))); } } diff --git a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php index 14f0c1b4..5e156a47 100644 --- a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php +++ b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php @@ -132,6 +132,19 @@ public function getSourceTableInfo(string $type): array; */ public function getSourceTables(): array; + /** + * True if the given record has to be migrated + * False to skip the migration for this record + * + * @param string $type Name of the content type + * @param int $pk The primary key of the content type + * + * @return bool True to continue migration, false to skip it + * + * @since 4.0.0 + */ + public function needsMigration(string $type, int $pk): bool; + /** * Returns an associative array containing the record data from source. * diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index 7a3bb175..c79b81e2 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -220,6 +220,9 @@ public function getData(string $type, int $pk): array ->from($db->quoteName($tablename)) ->where($db->quoteName($primarykey) . ' = ' . $db->quote($pk)); + // Reset the query using our newly populated query object. + $db->setQuery($query); + // Attempt to load the array try { @@ -233,6 +236,31 @@ public function getData(string $type, int $pk): array } } + /** + * True if the given record has to be migrated + * False to skip the migration for this record + * + * @param string $type Name of the content type + * @param int $pk The primary key of the content type + * + * @return bool True to continue migration, false to skip it + * + * @since 4.0.0 + */ + public function needsMigration(string $type, int $pk): bool + { + $skip_records = array('category' => array(0, 1), 'image' => array(0)); + + if(\in_array($pk, $skip_records[$type])) + { + return false; + } + else + { + return true; + } + } + /** * Converts data from source into the structure needed for JoomGallery. * @@ -362,7 +390,7 @@ protected function scriptSpecificChecks(Checks &$checks, string $category) if(!empty(\count($res))) { - $checks->addCheck($category, 'src_table_image_filename', false, Text::_('FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_TITLE'), Text::sprintf('FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_DESC', \count($res)), Text::sprintf('FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_HELP', \implode(', ', $res))); + $checks->addCheck($category, 'src_table_image_filename', true, true, Text::_('FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_TITLE'), Text::sprintf('FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_DESC', \count($res)), Text::sprintf('FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_HELP', \implode(', ', $res))); } return; diff --git a/administrator/com_joomgallery/src/Table/MigrationTable.php b/administrator/com_joomgallery/src/Table/MigrationTable.php index 736f5a9a..17341bc4 100644 --- a/administrator/com_joomgallery/src/Table/MigrationTable.php +++ b/administrator/com_joomgallery/src/Table/MigrationTable.php @@ -217,10 +217,13 @@ public function load($keys = null, $reset = true) public function clcProgress() { // Calculate progress property - $this->progress = (int) \round((100 / \count($this->queue)) * ($this->successful->count() + $this->failed->count())); + $total = \count($this->queue) + $this->successful->count() + $this->failed->count(); + $finished = $this->successful->count() + $this->failed->count(); + + $this->progress = (int) \round((100 / $total) * ($finished)); // Update completed property - if(\count($this->queue) === $this->successful->count()) + if($total === $finished) { $this->completed = true; } diff --git a/administrator/com_joomgallery/tmpl/migration/step2.php b/administrator/com_joomgallery/tmpl/migration/step2.php index ae53d8af..99166f24 100644 --- a/administrator/com_joomgallery/tmpl/migration/step2.php +++ b/administrator/com_joomgallery/tmpl/migration/step2.php @@ -56,15 +56,13 @@ name="adminForm" id="migration-form" class="form-validate" aria-label="COM_JOOMGALLERY_MIGRATION_STEP2_TITLE"> error)) : ?> -

- precheck as $cat) : ?>
title): ?>
-

title; ?>

+

title; ?>

desc): ?> desc; ?> @@ -84,12 +82,35 @@ checks as $check) : ?> + result) + { + if($check->warning) + { + // Check successful, but marked as warning + $badgeClass = 'warning'; + $badgeText = Text::_('COM_JOOMGALLERY_WARNING'); + } + else + { + // Check successful + $badgeClass = 'success'; + $badgeText = Text::_('COM_JOOMGALLERY_SUCCESSFUL'); + } + } + else + { + // Check failed + $badgeClass = 'danger'; + $badgeText = Text::_('COM_JOOMGALLERY_FAILED'); + } + ?> title; ?>
desc; ?> - result ? 'success' : 'failed'; ?> + From bac76a251c71c22ef550cb0c2a6fbdcdda3031b6 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 13 Nov 2023 21:00:10 +0100 Subject: [PATCH 032/121] update migrator.js --- .../language/en-GB/com_joomgallery.ini | 4 + .../com_joomgallery.migration.Jg3ToJg4.ini | 3 +- .../src/Controller/MigrationController.php | 19 +- .../src/Model/MigrationModel.php | 1 + .../src/Service/Migration/Migration.php | 10 + .../Service/Migration/Scripts/Jg3ToJg4.php | 26 +- .../tmpl/migration/default.php | 2 +- .../com_joomgallery/tmpl/migration/step3.php | 27 +- media/com_joomgallery/css/admin.css | 20 +- .../js/migrator/dist/migrator.js | 261 +++++++++++++++++- .../js/migrator/dist/migrator.js.map | 2 +- .../com_joomgallery/js/migrator/src/index.js | 261 +++++++++++++++++- 12 files changed, 591 insertions(+), 45 deletions(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index e7d2606a..1b9a0082 100755 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -11,6 +11,7 @@ ; ;Common JOOMGALLERY="JoomGallery" +SUCCESS="Success" COM_JOOMGALLERY="JoomGallery" COM_JOOMGALLERY_CONFIGURATION="JoomGallery: Options" COM_JOOMGALLERY_LOGO="JoomGallery Logo" @@ -94,6 +95,7 @@ COM_JOOMGALLERY_PATH="Path" COM_JOOMGALLERY_TABLE="Table" COM_JOOMGALLERY_RESUME="Resume" COM_JOOMGALLERY_SUCCESSFUL="Successful" +COM_JOOMGALLERY_SUCCESS="Success" COM_JOOMGALLERY_FAILED="Failed" COM_JOOMGALLERY_WARNING="Warning" COM_JOOMGALLERY_PENDING="Pending" @@ -203,6 +205,7 @@ COM_JOOMGALLERY_MIGRATION_SCRIPT_NOT_EXIST="The requested migration script does COM_JOOMGALLERY_MIGRATION_STEP1_BTN_TXT="Check migration capability" COM_JOOMGALLERY_MIGRATION_STEP2_BTN_TXT="Start migration manager" COM_JOOMGALLERY_MIGRATION_STEP3_BTN_TXT="Check migration success" +COM_JOOMGALLERY_SHOWLOG="Show log output" ;Messages @@ -299,6 +302,7 @@ COM_JOOMGALLERY_ERROR_TABLE_NOT_EXISTING="Table does not exist." COM_JOOMGALLERY_ERROR_PATH_NOT_EXISTING="Path does not exist." COM_JOOMGALLERY_ERROR_TASK_NOT_PERMITTED="You are not permitted to perform this task. Requested task: %s" COM_JOOMGALLERY_ERROR_CHECKED_OUT_BY_ANOTHER_USER="This is item is currently checked out by another user (name: %s). You are not allowed to view or modify this item as long as it is checked out." +COM_JOOMGALLERY_ERROR_NETWORK_PROBLEM="Network problem occured. No connection to the server endpoint established or the server responds with an error." ;Mail Templates COM_JOOMGALLERY_MAIL_NEWIMAGE_TITLE="JoomGallery: New Image" diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini index 07359612..9601a6d2 100644 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini @@ -16,4 +16,5 @@ FILES_JOOMGALLERY_MIGRATION_IMAGE_TITLE="Migration: Images" FILES_JOOMGALLERY_MIGRATION_CATEGORY_TITLE="Migration: Categories" FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_TITLE="Image filenames" FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_DESC="There are different filenames for thumbnail and detail image defined in the database of your JG3 tables. Number of affected images: %s" -FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_HELP="It was detected that for some images in the database table of the JG3 (source, #__com_joomgallery) different filenames are stored in the rows 'imgfilename' and 'imgthumbname'. This script could result in errorous migration results. We strongly advise to clean up the database manually before starting the migration. For all the affected Images you have to do the following:
  • Rename the thumbnail image in the filesystem to be the one set in the old JoomGallery image table at row 'imgfilename'.
  • In the old JoomGallery image table, set the value of the row 'imgthumbname' to be the same as in row 'imgfilename'.

ID's of affected images: (%s)

If you face any problems, call for help in our forum at https://www.forum.en.joomgalleryfriends.net" \ No newline at end of file +FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_HELP="It was detected that for some images in the database table of the JG3 (source, #__com_joomgallery) different filenames are stored in the rows 'imgfilename' and 'imgthumbname'. This script could result in errorous migration results. We strongly advise to clean up the database manually before starting the migration. For all the affected Images you have to do the following:
  • Rename the thumbnail image in the filesystem to be the one set in the old JoomGallery image table at row 'imgfilename'.
  • In the old JoomGallery image table, set the value of the row 'imgthumbname' to be the same as in row 'imgfilename'.

ID's of affected images: (%s)

If you face any problems, call for help in our forum at https://www.forum.en.joomgalleryfriends.net" +FILES_JOOMGALLERY_MIGRATION_ERROR_TYPE_PREREQUIREMENT="Prerequirement for this content type is not fulfilled. Please make shure the following content types are completely migrated and none of them failed." \ No newline at end of file diff --git a/administrator/com_joomgallery/src/Controller/MigrationController.php b/administrator/com_joomgallery/src/Controller/MigrationController.php index 129b7fd6..75fffcb1 100644 --- a/administrator/com_joomgallery/src/Controller/MigrationController.php +++ b/administrator/com_joomgallery/src/Controller/MigrationController.php @@ -616,7 +616,7 @@ public function start() /** * Create a response object - * {success: bool, message: string|array, data: mixed} + * {success: bool, data: mixed, continue: bool, error: string|array, debug: string|array, warning: string|array} * * @param mixed $data The data returned to the frontend * @param bool $success True if everything was good, false otherwise @@ -630,11 +630,18 @@ protected function createRespond($data, bool $success = true, $error = null): st { $obj = new stdClass; - $obj->success = $success; - $obj->data = $data; - $obj->error = array(); - $obj->debug = array(); - $obj->warning = array(); + $obj->success = $success; + $obj->data = $data; + $obj->continue = true; + $obj->error = array(); + $obj->debug = array(); + $obj->warning = array(); + + // Get value for continue + if(!\is_null($this->component->getMigration())) + { + $obj->continue = $this->component->getMigration()->get('continue', true); + } // Get debug output if(!empty($debug = $this->component->getDebug())) diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index cf65a805..56369d4b 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -616,6 +616,7 @@ public function migrate(string $type, int $pk): object $this->component->createMigration($this->app->getUserStateFromRequest(_JOOM_OPTION.'.migration.script', 'script', '', 'cmd')); $mig = $this->component->getMigration()->prepareMigration($type); + // Perform the migration of the element if($this->component->getMigration()->needsMigration($type, $pk)) { // Get record data from source diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index a2aa6b40..aa94190e 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -94,6 +94,16 @@ abstract class Migration implements MigrationInterface */ protected $migrateables = array(); + /** + * True, if the migration process of the current content type should be continued + * False to stop the automatic migration process. + * + * @var boolean + * + * @since 4.0.0 + */ + protected $continue = true; + /** * Constructor * diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index c79b81e2..5525a3a4 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -249,16 +249,30 @@ public function getData(string $type, int $pk): array */ public function needsMigration(string $type, int $pk): bool { - $skip_records = array('category' => array(0, 1), 'image' => array(0)); + // Content types that require another type beeing migrated completely + $prerequirements = array('category' => array(), 'image' => array('category')); + if(!empty($prerequirements[$type])) + { + foreach($prerequirements[$type] as $key => $req) + { + if(!$this->migrateables[$req] || !$this->migrateables[$req]->completed || $this->migrateables[$req]->failed->count() > 0) + { + $this->continue = false; + $this->component->setError(Text::sprintf('FILES_JOOMGALLERY_MIGRATION_PREREQUIREMENT_ERROR', \implode(', ', $prerequirements[$type]))); + return false; + } + } + } + + // Specific record ids which can be skiped + $skip_records = array('category' => array(0, 1), 'image' => array(0)); if(\in_array($pk, $skip_records[$type])) { return false; - } - else - { - return true; - } + } + + return true; } /** diff --git a/administrator/com_joomgallery/tmpl/migration/default.php b/administrator/com_joomgallery/tmpl/migration/default.php index a8de35cd..377daa86 100644 --- a/administrator/com_joomgallery/tmpl/migration/default.php +++ b/administrator/com_joomgallery/tmpl/migration/default.php @@ -41,7 +41,7 @@
-
+

diff --git a/administrator/com_joomgallery/tmpl/migration/step3.php b/administrator/com_joomgallery/tmpl/migration/step3.php index 757ffdab..d089d075 100644 --- a/administrator/com_joomgallery/tmpl/migration/step3.php +++ b/administrator/com_joomgallery/tmpl/migration/step3.php @@ -21,6 +21,13 @@ $wa->useStyle('com_joomgallery.admin') ->useScript('com_joomgallery.admin') ->useScript('com_joomgallery.migrator'); + +// Add language strings to JavaScript +Text::script('COM_JOOMGALLERY_ERROR_NETWORK_PROBLEM'); +Text::script('ERROR'); +Text::script('WARNING'); +Text::script('INFO'); +Text::script('SUCCESS'); ?>
@@ -69,20 +76,26 @@

- : queue); ?> - : successful); ?> - : failed); ?> + : queue); ?> + : successful); ?> + : failed); ?>
- + -
-
+
+
+
+ +
+
+
-
diff --git a/media/com_joomgallery/css/admin.css b/media/com_joomgallery/css/admin.css index abd7d713..a06398be 100644 --- a/media/com_joomgallery/css/admin.css +++ b/media/com_joomgallery/css/admin.css @@ -70,9 +70,6 @@ img.jg-controlpanel-logo { .modal .modal-body { margin: 1rem 2rem; } -.card.border { - border: 2px solid var(--dark) !important; -} .card.border.active, .card.border.success { border-color: var(--success) !important; @@ -96,6 +93,11 @@ img.jg-controlpanel-logo { background-position: right calc(0.375em + 0.25rem) center; background-size: calc(0.75em + 0.5rem) calc(0.75em + 0.5rem); } +.log-area { + font-size: 0.85rem; + max-height: 250px; + overflow-y: scroll; +} .jcc-color-box { float: left; width: 15px; @@ -222,3 +224,15 @@ joomla-field-image .jg_minithumb { .jg-migration .navigation { margin: 0 0 2rem; } +.log-area p { + margin-bottom: 0.5rem; +} +.log-area .color-success { + color: var(--success) +} +.log-area .color-error { + color: var(--danger) +} +.log-area .color-warning { + color: var(--warning) +} diff --git a/media/com_joomgallery/js/migrator/dist/migrator.js b/media/com_joomgallery/js/migrator/dist/migrator.js index a332b218..4320bdf2 100644 --- a/media/com_joomgallery/js/migrator/dist/migrator.js +++ b/media/com_joomgallery/js/migrator/dist/migrator.js @@ -45,15 +45,30 @@ __webpack_require__.r(__webpack_exports__); // Selectors used by this script let typeSelector = 'data-type'; let formIdTmpl = 'migrationForm'; +let button = 'migrationBtn'; +let tryLimit = 3; /** * Storage for migrateables * @var {Object} migrateablesList */ -let migrateablesList = {}; +var migrateablesList = {}; /** - * Submit a migration task + * Counter of how many times the same migration was tried to perfrom + * @var {Integer} tryCounter + */ +var tryCounter = 0; + +/** + * State. As long as this state is set to true, the migration will be + * continued automatically regarding the pernding queue in the migrateablesList. + * @var {Boolean} contiue + */ +var continueState = true; + +/** + * Submit the migration task by pressing the button * * @param {Object} event Event object * @param {Object} element DOM element object @@ -65,14 +80,25 @@ let submitTask = function(event, element) { let formId = formIdTmpl + '-' + type; let task = element.parentNode.querySelector('[name="task"]').value; + tryCounter++; + ajax(formId, task) .then(res => { // Handle the successful result here - responseHandler(res); + responseHandler(type, res); + + if(tryCounter >= tryLimit) { + // We reached the limit of tries --> looks like we have a network problem + updateMigrateables(type, {'success': false, 'message': Joomla.JText._('COM_JOOMGALLERY_ERROR_NETWORK_PROBLEM'), 'data': false}); + } else if(continueState) { + // Kick off the next task + submitTask(event, element); + } }) .catch(error => { // Handle any errors here console.error(error); + addLog(error, type, 'error'); }); }; @@ -83,7 +109,7 @@ let submitTask = function(event, element) { * @param {String} task Name of the task * * @returns {Object} Result object - * {success: true, status: 200, message: '', messages: {}, data: { {success: bool, message: string, data: mixed} }} + * {success: true, status: 200, message: '', messages: {}, data: { { {success, data, continue, error, debug, warning} }} */ let ajax = async function(formId, task) { @@ -117,8 +143,7 @@ let ajax = async function(formId, task) { if (!response.ok) { // Catch network error - console.log(txt); - return {success: false, status: response.status, message: response.message, messages: {}, data: {message: txt}}; + return {success: false, status: response.status, message: response.message, messages: {}, data: {error: txt, data:null}}; } if(txt.startsWith('{"success"')) { @@ -128,7 +153,7 @@ let ajax = async function(formId, task) { res.data = JSON.parse(res.data); } else if (txt.includes('Fatal error')) { // PHP fatal error occurred - res = {success: false, status: response.status, message: response.statusText, messages: {}, data: {message: txt}}; + res = {success: false, status: response.status, message: response.statusText, messages: {}, data: {error: txt, data:null}}; } else { // Response is not of type json --> probably some php warnings/notices let split = txt.split('\n{"'); @@ -137,6 +162,13 @@ let ajax = async function(formId, task) { res = {success: true, status: response.status, message: split[0], messages: temp.messages, data: data}; } + // Make sure res.data.data.queue is of type array + if(typeof res.data.data != "undefined" && res.data.data != null && 'queue' in res.data.data) { + if(res.data.data.queue.constructor !== Array) { + res.data.data.queue = Object.values(res.data.data.queue); + } + } + return res; } @@ -171,12 +203,221 @@ let getNextMigrationID = function(formId) { * Handle migration response * * @param {Object} response The response object in the form of - * {success: true, status: 200, message: '', messages: {}, data: { {success: bool, message: string, data: mixed} }} + * {success: true, status: 200, message: '', messages: {}, data: { {success, data, continue, error, debug, warning} }} + * + * @returns void + */ +let responseHandler = function(type, response) { + if(response.success == false) { + // Ajax request failed + addLog(response.message, type, 'error'); + addLog(response.messages, type, 'error'); + + // Try again... + } + else { + // Ajax request successful + if(!response.data.success) + { + // Migration failed + addLog('[Migrator.js] Migration of '+type+' with id = '+migrateablesList[type]['currentID']+' failed.', type, 'error'); + logMessages(type, response.data); + + // Stop autimatic continuation if requested from backend + if(!response.data.continue || response.data.continue == null || response.data.continue == false) { + continueState = false; + } + + // Update migrateables + updateMigrateables(type, response.data); + } + else + { + // Save record successful + logMessages(type, response.data); + addLog('[Migrator.js] Migration of '+type+' with id = '+migrateablesList[type]['currentID']+' successful.', type, 'success'); + + // Stop autimatic continuation if requested from backend + if(!response.data.continue || response.data.continue == null || response.data.continue == false) { + continueState = false; + } + + // Update migrateables + updateMigrateables(type, response.data); + + // Reset tryCounter + tryCounter = 0; + } + } +} + +/** + * Add a message to the logging output and the console + * + * @param {Mixed} msg One or multiple messages to be added to the log + * @param {String} type The type defining the logging output to use + * @param {String} msgType The type of message (available: error, warning, success, info) + * @param {Boolean} console True to add the message also to the console + * @param {Boolean} newLine True to add the message on a new line + * @param {Integer} marginTop Number of how much margin you want on the top of the message + * + * @returns void + */ +let addLog = function(msg, type, msgType, console=false, newLine=true, marginTop=0) { + if(!Boolean(msg) || msg == null || msg == '') { + // Message is empty. Do nothing + return; + } else if(typeof msg === 'string') { + // Your message is a simple string + let tmp_msg = ''; + + // Test if your string a json string + try { + tmp_msg = JSON.parse(msg); + } catch (e) { + } + + // Convert string to array + if(tmp_msg !== '') { + msg = Object.values(tmp_msg); + } else { + msg = [msg]; + } + } else if(typeof msg === 'object') { + // Your message is an object. Convert to array + msg = Object.values(msg); + } + + // Get logging output element + let logOutput = document.getElementById('logOutput-'+type); + + // Loop through all messages + msg.forEach((message, i) => { + + // Print in console + if(console) { + console.log(message); + } + + // Create element + let line = null; + if(newLine) { + line = document.createElement('p'); + } else { + line = document.createElement('span'); + } + + // Top margin to element + marginTop = parseInt(marginTop); + if(marginTop > 0) { + line.classList.add('mt-'+String(marginTop)); + } + + // Add text color + line.classList.add('color-'+msgType); + + // Add message to element + let msgType_txt = msgType.toLocaleUpperCase(); + line.textContent = '['+Joomla.JText._(msgType_txt)+'] '+String(message); + + // Print into logging output + logOutput.appendChild(line); + }); +} + +/** + * Clear the logging output + * + * @param {String} type The type defining the logging output to clear * * @returns void */ -let responseHandler = function(response) { - console.log(response); +let clearLog = function(type) { + // Get logging output element + let logOutput = document.getElementById('logOutput-'+type); + + // clear + logOutput.innerHTML = ''; +} + +/** + * Output all available messages from the result object + * + * @param {String} type The type defining the content type to be updated + * @param {Object} res The result object in the form of + * {success: bool, data: mixed, continue: bool, error: string|array, debug: string|array, warning: string|array} + * + * @returns void + */ +let logMessages = function(type, res) { + // Available message types: error, debug, warning + let available = ['error', 'debug', 'warning']; + let msgTypes = {'error': 'error', 'debug': 'info', 'warning': 'warning'}; + + available.forEach((value, index) => { + if(!res[value] || !Boolean(res.data) || res.data == null) { + return; + } + + addLog(res[value], type, msgTypes[value]); + }); +} + +/** + * Update migrateable input field, progress bar and badges + * + * @param {String} type The type defining the content type to be updated + * @param {Object} res The result object in the form of + * {success: bool, data: mixed, continue: bool, error: string|array, debug: string|array, warning: string|array} + * + * @returns void + */ +let updateMigrateables = function(type, res) { + let formId = formIdTmpl + '-' + type; + let form = document.getElementById(formId); + + if(!res.success && (!Boolean(res.data) || res.data == null || res.data == '')) { + // Migration failed, but no data available in result + + // Create result data based on input field + let migrateable = atob(form.querySelector('[name="migrateable"]').value); + res.data = JSON.parse(migrateable); + + // See: Joomgallery\Component\Joomgallery\Administrator\Model\MigrationModel::migrate + // Remove migrated primary key from queue + res.data.queue = res.data.queue.filter(function(e) { return e !== migrateablesList[type]['currentID'] }) + + // Add migrated primary key to failed object + res.data.failed[migrateablesList[type]['currentID']] = res.message; + } + + if(!Boolean(res.data.progress) || res.data.progress == null || res.data.progress == '') { + // Update progress if not delivered with result object + let total = res.data.queue.lenght + Object.keys(res.data.successful).length + Object.keys(res.data.failed).length; + let finished = Object.keys(res.data.successful).length + Object.keys(res.data.failed).length; + res.data.progress = Math.round((100 / total) * (finished)); + } + + // Get badges + let queueBadge = document.getElementById('badgeQueue-'+type); + let resBadge = document.getElementById('badgeSuccessful-'+type); + if(!res.success) { + resBadge = document.getElementById('badgeFailed-'+type); + } + + // Update migrateable input field + let field = form.querySelector('[name="migrateable"]'); + field.value = btoa(JSON.stringify(res.data)); + + // Update badges + queueBadge.innerHTML = parseInt(queueBadge.innerHTML) - 1; + resBadge.innerHTML = parseInt(resBadge.innerHTML) + 1; + + // Update progress bar + let bar = document.getElementById('progress-'+type); + bar.setAttribute('aria-valuenow', res.data.progress); + bar.style.width = res.data.progress + '%'; + bar.innerText = res.data.progress + '%'; } Migrator = __webpack_exports__; /******/ })() diff --git a/media/com_joomgallery/js/migrator/dist/migrator.js.map b/media/com_joomgallery/js/migrator/dist/migrator.js.map index 21b02350..f3fdddb8 100644 --- a/media/com_joomgallery/js/migrator/dist/migrator.js.map +++ b/media/com_joomgallery/js/migrator/dist/migrator.js.map @@ -1 +1 @@ -{"version":3,"file":"migrator.js","mappings":";;;UAAA;UACA;;;;;WCDA;WACA;WACA;WACA;WACA,yCAAyC,wCAAwC;WACjF;WACA;WACA;;;;;WCPA;;;;;WCAA;WACA;WACA;WACA,uDAAuD,iBAAiB;WACxE;WACA,gDAAgD,aAAa;WAC7D;;;;;;;;;;;;ACNA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,SAAS;AAClB;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB;AACA,aAAa,UAAU;AACvB,aAAa,qDAAqD,WAAW;AAC7E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,gFAAgF,SAAS;AACrG;AACA;AACA,sBAAsB;AACtB;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA,WAAW,mFAAmF,SAAS;AACvG,IAAI;AACJ;AACA,8BAA8B;AAC9B,6BAA6B;AAC7B;AACA,WAAW;AACX;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB;AACA,aAAa,UAAU;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,qDAAqD,WAAW;AAC7E;AACA;AACA;AACA;AACA;AACA,C","sources":["webpack://Migrator/webpack/bootstrap","webpack://Migrator/webpack/runtime/define property getters","webpack://Migrator/webpack/runtime/hasOwnProperty shorthand","webpack://Migrator/webpack/runtime/make namespace object","webpack://Migrator/./src/index.js"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","// Selectors used by this script\r\nlet typeSelector = 'data-type';\r\nlet formIdTmpl = 'migrationForm';\r\n\r\n/**\r\n * Storage for migrateables\r\n * @var {Object} migrateablesList\r\n */\r\nlet migrateablesList = {};\r\n\r\n/**\r\n * Submit a migration task\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let submitTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n let type = element.getAttribute(typeSelector);\r\n let formId = formIdTmpl + '-' + type;\r\n let task = element.parentNode.querySelector('[name=\"task\"]').value;\r\n\r\n ajax(formId, task)\r\n .then(res => {\r\n // Handle the successful result here\r\n responseHandler(res);\r\n })\r\n .catch(error => {\r\n // Handle any errors here\r\n console.error(error);\r\n });\r\n};\r\n\r\n/**\r\n * Perform an ajax request in json format\r\n * \r\n * @param {String} formId Id of the form element\r\n * @param {String} task Name of the task\r\n * \r\n * @returns {Object} Result object\r\n * {success: true, status: 200, message: '', messages: {}, data: { {success: bool, message: string, data: mixed} }}\r\n */\r\nlet ajax = async function(formId, task) {\r\n\r\n // Catch form and data\r\n let formData = new FormData(document.getElementById(formId));\r\n formData.append('format', 'json');\r\n\r\n if(task == 'migration.start') {\r\n formData.append('id', getNextMigrationID(formId));\r\n }\r\n\r\n // Set request parameters\r\n let parameters = {\r\n method: 'POST',\r\n mode: 'same-origin',\r\n cache: 'default',\r\n redirect: 'follow',\r\n referrerPolicy: 'no-referrer-when-downgrade',\r\n body: formData,\r\n };\r\n\r\n // Set the url\r\n let url = document.getElementById(formId).getAttribute('action');\r\n\r\n // Perform the fetch request\r\n let response = await fetch(url, parameters);\r\n\r\n // Resolve promise as text string\r\n let txt = await response.text();\r\n let res = null;\r\n\r\n if (!response.ok) {\r\n // Catch network error\r\n console.log(txt);\r\n return {success: false, status: response.status, message: response.message, messages: {}, data: {message: txt}};\r\n }\r\n\r\n if(txt.startsWith('{\"success\"')) {\r\n // Response is of type json --> everything fine\r\n res = JSON.parse(txt);\r\n res.status = response.status;\r\n res.data = JSON.parse(res.data);\r\n } else if (txt.includes('Fatal error')) {\r\n // PHP fatal error occurred\r\n res = {success: false, status: response.status, message: response.statusText, messages: {}, data: {message: txt}};\r\n } else {\r\n // Response is not of type json --> probably some php warnings/notices\r\n let split = txt.split('\\n{\"');\r\n let temp = JSON.parse('{\"'+split[1]);\r\n let data = JSON.parse(temp.data);\r\n res = {success: true, status: response.status, message: split[0], messages: temp.messages, data: data};\r\n }\r\n\r\n return res;\r\n}\r\n\r\n/**\r\n * Perform a migration task\r\n * @param {String} formId Id of the form element\r\n * \r\n * @returns {String} Id of the database record to be migrated\r\n */\r\nlet getNextMigrationID = function(formId) {\r\n let type = formId.replace(formIdTmpl + '-', '');\r\n let form = document.getElementById(formId);\r\n\r\n let migrateable = atob(form.querySelector('[name=\"migrateable\"]').value);\r\n migrateable = JSON.parse(migrateable);\r\n\r\n // Overwrite migrateable in list\r\n migrateablesList[type] = migrateable;\r\n\r\n // Loop through queue\r\n for (let id of migrateable.queue) {\r\n if (!(id in migrateable.successful) && !(id in migrateable.failed)) {\r\n migrateablesList[type]['currentID'] = id;\r\n break;\r\n }\r\n }\r\n\r\n return migrateablesList[type]['currentID'];\r\n}\r\n\r\n/**\r\n * Handle migration response\r\n * \r\n * @param {Object} response The response object in the form of\r\n * {success: true, status: 200, message: '', messages: {}, data: { {success: bool, message: string, data: mixed} }}\r\n * \r\n * @returns void\r\n */\r\nlet responseHandler = function(response) {\r\n console.log(response);\r\n}"],"names":[],"sourceRoot":""} \ No newline at end of file +{"version":3,"file":"migrator.js","mappings":";;;UAAA;UACA;;;;;WCDA;WACA;WACA;WACA;WACA,yCAAyC,wCAAwC;WACjF;WACA;WACA;;;;;WCPA;;;;;WCAA;WACA;WACA;WACA,uDAAuD,iBAAiB;WACxE;WACA,gDAAgD,aAAa;WAC7D;;;;;;;;;;;;ACNA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,SAAS;AAClB;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kCAAkC,oGAAoG;AACtI,QAAQ;AACR;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB;AACA,aAAa,UAAU;AACvB,aAAa,qDAAqD,aAAa;AAC/E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,gFAAgF,SAAS;AACrG;AACA;AACA,sBAAsB;AACtB;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA,WAAW,mFAAmF,SAAS;AACvG,IAAI;AACJ;AACA,8BAA8B;AAC9B,6BAA6B;AAC7B;AACA,WAAW;AACX;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB;AACA,aAAa,UAAU;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,qDAAqD,WAAW;AAC7E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB,YAAY,UAAU;AACtB,cAAc;AACd;AACA;AACA;AACA;AACA;AACA;AACA,kBAAkB;AAClB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB,YAAY,UAAU;AACtB,cAAc;AACd;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,yDAAyD,kDAAkD;AAC3G;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,C","sources":["webpack://Migrator/webpack/bootstrap","webpack://Migrator/webpack/runtime/define property getters","webpack://Migrator/webpack/runtime/hasOwnProperty shorthand","webpack://Migrator/webpack/runtime/make namespace object","webpack://Migrator/./src/index.js"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","// Selectors used by this script\r\nlet typeSelector = 'data-type';\r\nlet formIdTmpl = 'migrationForm';\r\nlet button = 'migrationBtn';\r\nlet tryLimit = 3;\r\n\r\n/**\r\n * Storage for migrateables\r\n * @var {Object} migrateablesList\r\n */\r\nvar migrateablesList = {};\r\n\r\n/**\r\n * Counter of how many times the same migration was tried to perfrom\r\n * @var {Integer} tryCounter\r\n */\r\nvar tryCounter = 0;\r\n\r\n/**\r\n * State. As long as this state is set to true, the migration will be\r\n * continued automatically regarding the pernding queue in the migrateablesList.\r\n * @var {Boolean} contiue\r\n */\r\nvar continueState = true;\r\n\r\n/**\r\n * Submit the migration task by pressing the button\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let submitTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n let type = element.getAttribute(typeSelector);\r\n let formId = formIdTmpl + '-' + type;\r\n let task = element.parentNode.querySelector('[name=\"task\"]').value;\r\n\r\n tryCounter++;\r\n\r\n ajax(formId, task)\r\n .then(res => {\r\n // Handle the successful result here\r\n responseHandler(type, res);\r\n\r\n if(tryCounter >= tryLimit) {\r\n // We reached the limit of tries --> looks like we have a network problem\r\n updateMigrateables(type, {'success': false, 'message': Joomla.JText._('COM_JOOMGALLERY_ERROR_NETWORK_PROBLEM'), 'data': false});\r\n } else if(continueState) {\r\n // Kick off the next task\r\n submitTask(event, element);\r\n }\r\n })\r\n .catch(error => {\r\n // Handle any errors here\r\n console.error(error);\r\n addLog(error, type, 'error');\r\n });\r\n};\r\n\r\n/**\r\n * Perform an ajax request in json format\r\n * \r\n * @param {String} formId Id of the form element\r\n * @param {String} task Name of the task\r\n * \r\n * @returns {Object} Result object\r\n * {success: true, status: 200, message: '', messages: {}, data: { { {success, data, continue, error, debug, warning} }}\r\n */\r\nlet ajax = async function(formId, task) {\r\n\r\n // Catch form and data\r\n let formData = new FormData(document.getElementById(formId));\r\n formData.append('format', 'json');\r\n\r\n if(task == 'migration.start') {\r\n formData.append('id', getNextMigrationID(formId));\r\n }\r\n\r\n // Set request parameters\r\n let parameters = {\r\n method: 'POST',\r\n mode: 'same-origin',\r\n cache: 'default',\r\n redirect: 'follow',\r\n referrerPolicy: 'no-referrer-when-downgrade',\r\n body: formData,\r\n };\r\n\r\n // Set the url\r\n let url = document.getElementById(formId).getAttribute('action');\r\n\r\n // Perform the fetch request\r\n let response = await fetch(url, parameters);\r\n\r\n // Resolve promise as text string\r\n let txt = await response.text();\r\n let res = null;\r\n\r\n if (!response.ok) {\r\n // Catch network error\r\n return {success: false, status: response.status, message: response.message, messages: {}, data: {error: txt, data:null}};\r\n }\r\n\r\n if(txt.startsWith('{\"success\"')) {\r\n // Response is of type json --> everything fine\r\n res = JSON.parse(txt);\r\n res.status = response.status;\r\n res.data = JSON.parse(res.data);\r\n } else if (txt.includes('Fatal error')) {\r\n // PHP fatal error occurred\r\n res = {success: false, status: response.status, message: response.statusText, messages: {}, data: {error: txt, data:null}};\r\n } else {\r\n // Response is not of type json --> probably some php warnings/notices\r\n let split = txt.split('\\n{\"');\r\n let temp = JSON.parse('{\"'+split[1]);\r\n let data = JSON.parse(temp.data);\r\n res = {success: true, status: response.status, message: split[0], messages: temp.messages, data: data};\r\n }\r\n\r\n // Make sure res.data.data.queue is of type array\r\n if(typeof res.data.data != \"undefined\" && res.data.data != null && 'queue' in res.data.data) {\r\n if(res.data.data.queue.constructor !== Array) {\r\n res.data.data.queue = Object.values(res.data.data.queue);\r\n }\r\n }\r\n\r\n return res;\r\n}\r\n\r\n/**\r\n * Perform a migration task\r\n * @param {String} formId Id of the form element\r\n * \r\n * @returns {String} Id of the database record to be migrated\r\n */\r\nlet getNextMigrationID = function(formId) {\r\n let type = formId.replace(formIdTmpl + '-', '');\r\n let form = document.getElementById(formId);\r\n\r\n let migrateable = atob(form.querySelector('[name=\"migrateable\"]').value);\r\n migrateable = JSON.parse(migrateable);\r\n\r\n // Overwrite migrateable in list\r\n migrateablesList[type] = migrateable;\r\n\r\n // Loop through queue\r\n for (let id of migrateable.queue) {\r\n if (!(id in migrateable.successful) && !(id in migrateable.failed)) {\r\n migrateablesList[type]['currentID'] = id;\r\n break;\r\n }\r\n }\r\n\r\n return migrateablesList[type]['currentID'];\r\n}\r\n\r\n/**\r\n * Handle migration response\r\n * \r\n * @param {Object} response The response object in the form of\r\n * {success: true, status: 200, message: '', messages: {}, data: { {success, data, continue, error, debug, warning} }}\r\n * \r\n * @returns void\r\n */\r\nlet responseHandler = function(type, response) {\r\n if(response.success == false) {\r\n // Ajax request failed\r\n addLog(response.message, type, 'error');\r\n addLog(response.messages, type, 'error');\r\n\r\n // Try again...\r\n }\r\n else {\r\n // Ajax request successful\r\n if(!response.data.success)\r\n {\r\n // Migration failed\r\n addLog('[Migrator.js] Migration of '+type+' with id = '+migrateablesList[type]['currentID']+' failed.', type, 'error');\r\n logMessages(type, response.data);\r\n\r\n // Stop autimatic continuation if requested from backend\r\n if(!response.data.continue || response.data.continue == null || response.data.continue == false) {\r\n continueState = false;\r\n }\r\n\r\n // Update migrateables\r\n updateMigrateables(type, response.data);\r\n }\r\n else\r\n {\r\n // Save record successful\r\n logMessages(type, response.data);\r\n addLog('[Migrator.js] Migration of '+type+' with id = '+migrateablesList[type]['currentID']+' successful.', type, 'success');\r\n\r\n // Stop autimatic continuation if requested from backend\r\n if(!response.data.continue || response.data.continue == null || response.data.continue == false) {\r\n continueState = false;\r\n }\r\n\r\n // Update migrateables\r\n updateMigrateables(type, response.data);\r\n\r\n // Reset tryCounter\r\n tryCounter = 0;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Add a message to the logging output and the console\r\n * \r\n * @param {Mixed} msg One or multiple messages to be added to the log\r\n * @param {String} type The type defining the logging output to use\r\n * @param {String} msgType The type of message (available: error, warning, success, info)\r\n * @param {Boolean} console True to add the message also to the console\r\n * @param {Boolean} newLine True to add the message on a new line\r\n * @param {Integer} marginTop Number of how much margin you want on the top of the message\r\n * \r\n * @returns void\r\n */\r\nlet addLog = function(msg, type, msgType, console=false, newLine=true, marginTop=0) {\r\n if(!Boolean(msg) || msg == null || msg == '') {\r\n // Message is empty. Do nothing\r\n return;\r\n } else if(typeof msg === 'string') {\r\n // Your message is a simple string\r\n let tmp_msg = '';\r\n\r\n // Test if your string a json string\r\n try {\r\n tmp_msg = JSON.parse(msg);\r\n } catch (e) {\r\n }\r\n\r\n // Convert string to array\r\n if(tmp_msg !== '') {\r\n msg = Object.values(tmp_msg);\r\n } else {\r\n msg = [msg];\r\n }\r\n } else if(typeof msg === 'object') {\r\n // Your message is an object. Convert to array\r\n msg = Object.values(msg);\r\n }\r\n\r\n // Get logging output element\r\n let logOutput = document.getElementById('logOutput-'+type);\r\n\r\n // Loop through all messages\r\n msg.forEach((message, i) => {\r\n\r\n // Print in console\r\n if(console) {\r\n console.log(message);\r\n }\r\n\r\n // Create element\r\n let line = null;\r\n if(newLine) {\r\n line = document.createElement('p');\r\n } else {\r\n line = document.createElement('span');\r\n }\r\n\r\n // Top margin to element\r\n marginTop = parseInt(marginTop);\r\n if(marginTop > 0) {\r\n line.classList.add('mt-'+String(marginTop));\r\n }\r\n\r\n // Add text color\r\n line.classList.add('color-'+msgType);\r\n \r\n // Add message to element\r\n let msgType_txt = msgType.toLocaleUpperCase();\r\n line.textContent = '['+Joomla.JText._(msgType_txt)+'] '+String(message);\r\n\r\n // Print into logging output\r\n logOutput.appendChild(line);\r\n });\r\n}\r\n\r\n/**\r\n * Clear the logging output\r\n *\r\n * @param {String} type The type defining the logging output to clear\r\n * \r\n * @returns void\r\n */\r\nlet clearLog = function(type) {\r\n // Get logging output element\r\n let logOutput = document.getElementById('logOutput-'+type);\r\n\r\n // clear\r\n logOutput.innerHTML = '';\r\n}\r\n\r\n/**\r\n * Output all available messages from the result object\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {Object} res The result object in the form of\r\n * {success: bool, data: mixed, continue: bool, error: string|array, debug: string|array, warning: string|array}\r\n * \r\n * @returns void\r\n */\r\nlet logMessages = function(type, res) {\r\n // Available message types: error, debug, warning\r\n let available = ['error', 'debug', 'warning'];\r\n let msgTypes = {'error': 'error', 'debug': 'info', 'warning': 'warning'};\r\n\r\n available.forEach((value, index) => {\r\n if(!res[value] || !Boolean(res.data) || res.data == null) {\r\n return;\r\n }\r\n\r\n addLog(res[value], type, msgTypes[value]);\r\n });\r\n}\r\n\r\n/**\r\n * Update migrateable input field, progress bar and badges\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {Object} res The result object in the form of\r\n * {success: bool, data: mixed, continue: bool, error: string|array, debug: string|array, warning: string|array}\r\n * \r\n * @returns void\r\n */\r\nlet updateMigrateables = function(type, res) {\r\n let formId = formIdTmpl + '-' + type;\r\n let form = document.getElementById(formId);\r\n\r\n if(!res.success && (!Boolean(res.data) || res.data == null || res.data == '')) {\r\n // Migration failed, but no data available in result\r\n\r\n // Create result data based on input field\r\n let migrateable = atob(form.querySelector('[name=\"migrateable\"]').value);\r\n res.data = JSON.parse(migrateable);\r\n\r\n // See: Joomgallery\\Component\\Joomgallery\\Administrator\\Model\\MigrationModel::migrate\r\n // Remove migrated primary key from queue\r\n res.data.queue = res.data.queue.filter(function(e) { return e !== migrateablesList[type]['currentID'] })\r\n\r\n // Add migrated primary key to failed object\r\n res.data.failed[migrateablesList[type]['currentID']] = res.message;\r\n }\r\n\r\n if(!Boolean(res.data.progress) || res.data.progress == null || res.data.progress == '') {\r\n // Update progress if not delivered with result object\r\n let total = res.data.queue.lenght + Object.keys(res.data.successful).length + Object.keys(res.data.failed).length;\r\n let finished = Object.keys(res.data.successful).length + Object.keys(res.data.failed).length;\r\n res.data.progress = Math.round((100 / total) * (finished));\r\n }\r\n\r\n // Get badges\r\n let queueBadge = document.getElementById('badgeQueue-'+type);\r\n let resBadge = document.getElementById('badgeSuccessful-'+type);\r\n if(!res.success) {\r\n resBadge = document.getElementById('badgeFailed-'+type);\r\n }\r\n\r\n // Update migrateable input field\r\n let field = form.querySelector('[name=\"migrateable\"]');\r\n field.value = btoa(JSON.stringify(res.data));\r\n\r\n // Update badges\r\n queueBadge.innerHTML = parseInt(queueBadge.innerHTML) - 1;\r\n resBadge.innerHTML = parseInt(resBadge.innerHTML) + 1;\r\n\r\n // Update progress bar\r\n let bar = document.getElementById('progress-'+type);\r\n bar.setAttribute('aria-valuenow', res.data.progress);\r\n bar.style.width = res.data.progress + '%';\r\n bar.innerText = res.data.progress + '%';\r\n}"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/media/com_joomgallery/js/migrator/src/index.js b/media/com_joomgallery/js/migrator/src/index.js index 8cf704c2..2fa42036 100644 --- a/media/com_joomgallery/js/migrator/src/index.js +++ b/media/com_joomgallery/js/migrator/src/index.js @@ -1,15 +1,30 @@ // Selectors used by this script let typeSelector = 'data-type'; let formIdTmpl = 'migrationForm'; +let button = 'migrationBtn'; +let tryLimit = 3; /** * Storage for migrateables * @var {Object} migrateablesList */ -let migrateablesList = {}; +var migrateablesList = {}; /** - * Submit a migration task + * Counter of how many times the same migration was tried to perfrom + * @var {Integer} tryCounter + */ +var tryCounter = 0; + +/** + * State. As long as this state is set to true, the migration will be + * continued automatically regarding the pernding queue in the migrateablesList. + * @var {Boolean} contiue + */ +var continueState = true; + +/** + * Submit the migration task by pressing the button * * @param {Object} event Event object * @param {Object} element DOM element object @@ -21,14 +36,25 @@ export let submitTask = function(event, element) { let formId = formIdTmpl + '-' + type; let task = element.parentNode.querySelector('[name="task"]').value; + tryCounter++; + ajax(formId, task) .then(res => { // Handle the successful result here - responseHandler(res); + responseHandler(type, res); + + if(tryCounter >= tryLimit) { + // We reached the limit of tries --> looks like we have a network problem + updateMigrateables(type, {'success': false, 'message': Joomla.JText._('COM_JOOMGALLERY_ERROR_NETWORK_PROBLEM'), 'data': false}); + } else if(continueState) { + // Kick off the next task + submitTask(event, element); + } }) .catch(error => { // Handle any errors here console.error(error); + addLog(error, type, 'error'); }); }; @@ -39,7 +65,7 @@ export let submitTask = function(event, element) { * @param {String} task Name of the task * * @returns {Object} Result object - * {success: true, status: 200, message: '', messages: {}, data: { {success: bool, message: string, data: mixed} }} + * {success: true, status: 200, message: '', messages: {}, data: { { {success, data, continue, error, debug, warning} }} */ let ajax = async function(formId, task) { @@ -73,8 +99,7 @@ let ajax = async function(formId, task) { if (!response.ok) { // Catch network error - console.log(txt); - return {success: false, status: response.status, message: response.message, messages: {}, data: {message: txt}}; + return {success: false, status: response.status, message: response.message, messages: {}, data: {error: txt, data:null}}; } if(txt.startsWith('{"success"')) { @@ -84,7 +109,7 @@ let ajax = async function(formId, task) { res.data = JSON.parse(res.data); } else if (txt.includes('Fatal error')) { // PHP fatal error occurred - res = {success: false, status: response.status, message: response.statusText, messages: {}, data: {message: txt}}; + res = {success: false, status: response.status, message: response.statusText, messages: {}, data: {error: txt, data:null}}; } else { // Response is not of type json --> probably some php warnings/notices let split = txt.split('\n{"'); @@ -93,6 +118,13 @@ let ajax = async function(formId, task) { res = {success: true, status: response.status, message: split[0], messages: temp.messages, data: data}; } + // Make sure res.data.data.queue is of type array + if(typeof res.data.data != "undefined" && res.data.data != null && 'queue' in res.data.data) { + if(res.data.data.queue.constructor !== Array) { + res.data.data.queue = Object.values(res.data.data.queue); + } + } + return res; } @@ -127,10 +159,219 @@ let getNextMigrationID = function(formId) { * Handle migration response * * @param {Object} response The response object in the form of - * {success: true, status: 200, message: '', messages: {}, data: { {success: bool, message: string, data: mixed} }} + * {success: true, status: 200, message: '', messages: {}, data: { {success, data, continue, error, debug, warning} }} + * + * @returns void + */ +let responseHandler = function(type, response) { + if(response.success == false) { + // Ajax request failed + addLog(response.message, type, 'error'); + addLog(response.messages, type, 'error'); + + // Try again... + } + else { + // Ajax request successful + if(!response.data.success) + { + // Migration failed + addLog('[Migrator.js] Migration of '+type+' with id = '+migrateablesList[type]['currentID']+' failed.', type, 'error'); + logMessages(type, response.data); + + // Stop autimatic continuation if requested from backend + if(!response.data.continue || response.data.continue == null || response.data.continue == false) { + continueState = false; + } + + // Update migrateables + updateMigrateables(type, response.data); + } + else + { + // Save record successful + logMessages(type, response.data); + addLog('[Migrator.js] Migration of '+type+' with id = '+migrateablesList[type]['currentID']+' successful.', type, 'success'); + + // Stop autimatic continuation if requested from backend + if(!response.data.continue || response.data.continue == null || response.data.continue == false) { + continueState = false; + } + + // Update migrateables + updateMigrateables(type, response.data); + + // Reset tryCounter + tryCounter = 0; + } + } +} + +/** + * Add a message to the logging output and the console + * + * @param {Mixed} msg One or multiple messages to be added to the log + * @param {String} type The type defining the logging output to use + * @param {String} msgType The type of message (available: error, warning, success, info) + * @param {Boolean} console True to add the message also to the console + * @param {Boolean} newLine True to add the message on a new line + * @param {Integer} marginTop Number of how much margin you want on the top of the message + * + * @returns void + */ +let addLog = function(msg, type, msgType, console=false, newLine=true, marginTop=0) { + if(!Boolean(msg) || msg == null || msg == '') { + // Message is empty. Do nothing + return; + } else if(typeof msg === 'string') { + // Your message is a simple string + let tmp_msg = ''; + + // Test if your string a json string + try { + tmp_msg = JSON.parse(msg); + } catch (e) { + } + + // Convert string to array + if(tmp_msg !== '') { + msg = Object.values(tmp_msg); + } else { + msg = [msg]; + } + } else if(typeof msg === 'object') { + // Your message is an object. Convert to array + msg = Object.values(msg); + } + + // Get logging output element + let logOutput = document.getElementById('logOutput-'+type); + + // Loop through all messages + msg.forEach((message, i) => { + + // Print in console + if(console) { + console.log(message); + } + + // Create element + let line = null; + if(newLine) { + line = document.createElement('p'); + } else { + line = document.createElement('span'); + } + + // Top margin to element + marginTop = parseInt(marginTop); + if(marginTop > 0) { + line.classList.add('mt-'+String(marginTop)); + } + + // Add text color + line.classList.add('color-'+msgType); + + // Add message to element + let msgType_txt = msgType.toLocaleUpperCase(); + line.textContent = '['+Joomla.JText._(msgType_txt)+'] '+String(message); + + // Print into logging output + logOutput.appendChild(line); + }); +} + +/** + * Clear the logging output + * + * @param {String} type The type defining the logging output to clear * * @returns void */ -let responseHandler = function(response) { - console.log(response); +let clearLog = function(type) { + // Get logging output element + let logOutput = document.getElementById('logOutput-'+type); + + // clear + logOutput.innerHTML = ''; +} + +/** + * Output all available messages from the result object + * + * @param {String} type The type defining the content type to be updated + * @param {Object} res The result object in the form of + * {success: bool, data: mixed, continue: bool, error: string|array, debug: string|array, warning: string|array} + * + * @returns void + */ +let logMessages = function(type, res) { + // Available message types: error, debug, warning + let available = ['error', 'debug', 'warning']; + let msgTypes = {'error': 'error', 'debug': 'info', 'warning': 'warning'}; + + available.forEach((value, index) => { + if(!res[value] || !Boolean(res.data) || res.data == null) { + return; + } + + addLog(res[value], type, msgTypes[value]); + }); +} + +/** + * Update migrateable input field, progress bar and badges + * + * @param {String} type The type defining the content type to be updated + * @param {Object} res The result object in the form of + * {success: bool, data: mixed, continue: bool, error: string|array, debug: string|array, warning: string|array} + * + * @returns void + */ +let updateMigrateables = function(type, res) { + let formId = formIdTmpl + '-' + type; + let form = document.getElementById(formId); + + if(!res.success && (!Boolean(res.data) || res.data == null || res.data == '')) { + // Migration failed, but no data available in result + + // Create result data based on input field + let migrateable = atob(form.querySelector('[name="migrateable"]').value); + res.data = JSON.parse(migrateable); + + // See: Joomgallery\Component\Joomgallery\Administrator\Model\MigrationModel::migrate + // Remove migrated primary key from queue + res.data.queue = res.data.queue.filter(function(e) { return e !== migrateablesList[type]['currentID'] }) + + // Add migrated primary key to failed object + res.data.failed[migrateablesList[type]['currentID']] = res.message; + } + + if(!Boolean(res.data.progress) || res.data.progress == null || res.data.progress == '') { + // Update progress if not delivered with result object + let total = res.data.queue.lenght + Object.keys(res.data.successful).length + Object.keys(res.data.failed).length; + let finished = Object.keys(res.data.successful).length + Object.keys(res.data.failed).length; + res.data.progress = Math.round((100 / total) * (finished)); + } + + // Get badges + let queueBadge = document.getElementById('badgeQueue-'+type); + let resBadge = document.getElementById('badgeSuccessful-'+type); + if(!res.success) { + resBadge = document.getElementById('badgeFailed-'+type); + } + + // Update migrateable input field + let field = form.querySelector('[name="migrateable"]'); + field.value = btoa(JSON.stringify(res.data)); + + // Update badges + queueBadge.innerHTML = parseInt(queueBadge.innerHTML) - 1; + resBadge.innerHTML = parseInt(resBadge.innerHTML) + 1; + + // Update progress bar + let bar = document.getElementById('progress-'+type); + bar.setAttribute('aria-valuenow', res.data.progress); + bar.style.width = res.data.progress + '%'; + bar.innerText = res.data.progress + '%'; } \ No newline at end of file From beb25fc922400e3e747c91d0c259099659590d79 Mon Sep 17 00:00:00 2001 From: Elfangor Date: Mon, 20 Nov 2023 08:41:34 +0100 Subject: [PATCH 033/121] update Migration service --- .../src/Model/MigrationModel.php | 53 +++++-- .../src/Service/Migration/Migration.php | 2 +- .../src/Table/MigrationTable.php | 132 +++++++++++++----- media/com_joomgallery/css/admin.css | 1 + 4 files changed, 140 insertions(+), 48 deletions(-) diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index 56369d4b..c352927a 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -18,6 +18,7 @@ use \Joomla\CMS\Form\Form; use \Joomla\CMS\Language\Text; use \Joomla\Registry\Registry; +use \Joomla\Utilities\ArrayHelper; use \Joomla\CMS\Filesystem\Folder; use \Joomla\CMS\MVC\Model\AdminModel; use \Joomla\CMS\Language\Multilanguage; @@ -226,24 +227,35 @@ public function getItem($pk = null) { $item = parent::getItem($pk); - if(\property_exists($item, 'queue')) + if(!$item) + { + $item = parent::getItem(null); + } + + // Support for queue field + if(isset($item->queue)) { $registry = new Registry($item->queue); $item->queue = $registry->toArray(); + $item->queue = ArrayHelper::toInteger($item->queue); } - if(\property_exists($item, 'successful')) + // Support for successful field + if(isset($item->successful)) { - $registry = new Registry($item->successful); - $item->successful = $registry; - //$item->successful = $registry->toArray(); + $item->successful = new Registry($item->successful); } - if(\property_exists($item, 'failed')) + // Support for failed field + if(isset($item->failed)) { - $registry = new Registry($item->failed); - $item->failed = $registry; - //$item->failed = $registry->toArray(); + $item->failed = new Registry($item->failed); + } + + // Support for params field + if(isset($item->params)) + { + $item->params = new Registry($item->params); } // Add script if empty @@ -708,7 +720,28 @@ public function migrate(string $type, int $pk): object // Calculate progress and completed state $table->clcProgress(); - return $table; + // Prepare the row for saving + $this->prepareTable($table); + + // Check the data. + if(!$table->check()) + { + $this->component->setError($table->getError()); + + return false; + } + + $ret_table = clone $table; + + // Save table + if(!$table->store()) + { + $this->component->setError($table->getError()); + + return $mig; + } + + return $ret_table; } /** diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index aa94190e..c1134fde 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -982,7 +982,7 @@ protected function applyConvertData(array $data, array $mapping): array // Remove content from data array element ('old key' => false) if($mapping[$key] === false) { - $data[$mapping[$key]] = null; + $data[$key] = null; continue; } diff --git a/administrator/com_joomgallery/src/Table/MigrationTable.php b/administrator/com_joomgallery/src/Table/MigrationTable.php index 17341bc4..05648616 100644 --- a/administrator/com_joomgallery/src/Table/MigrationTable.php +++ b/administrator/com_joomgallery/src/Table/MigrationTable.php @@ -16,6 +16,7 @@ use \Joomla\CMS\Factory; use \Joomla\CMS\Table\Table; use \Joomla\Registry\Registry; +use \Joomla\Utilities\ArrayHelper; use \Joomla\Database\DatabaseDriver; /** @@ -88,23 +89,35 @@ public function getTypeAlias() */ public function store($updateNulls = true) { - return parent::store($updateNulls); - } + // Support for queue field + if(isset($this->queue) && !\is_string($this->queue)) + { + $this->queue = \json_encode(array_values($this->queue), JSON_UNESCAPED_UNICODE); + } - /** - * Delete a record by id - * - * @param mixed $pk Primary key value to delete. Optional - * - * @return bool - */ - public function delete($pk = null) - { - $this->load($pk); - $result = parent::delete($pk); + // Support for successful field + if(isset($this->successful) && !\is_string($this->successful)) + { + $registry = new Registry($this->successful); + $this->successful = (string) $registry; + } - return $result; - } + // Support for failed field + if(isset($this->failed) && !is_string($this->failed)) + { + $registry = new Registry($this->failed); + $this->failed = (string) $registry; + } + + // Support for params field + if(isset($this->params) && !is_string($this->params)) + { + $registry = new Registry($this->params); + $this->params = (string) $registry; + } + + return parent::store($updateNulls); + } /** * Overloaded bind function to pre-process the params. @@ -159,47 +172,92 @@ public function bind($array, $ignore = '') } /** - * Method to load a row from the database by primary key and bind the fields to the Table instance properties. + * Method to perform sanity checks on the Table instance properties to ensure they are safe to store in the database. * - * @param mixed $keys An optional primary key value to load the row by, or an array of fields to match. - * If not set the instance property value is used. - * @param boolean $reset True to reset the default values before loading the new row. + * Child classes should override this method to make sure the data they are storing in the database is safe and as expected before storage. * - * @return boolean True if successful. False if row not found. + * @return boolean True if the instance is sane and able to be stored in the database. * - * @see Table:bind * @since 4.0.0 */ - public function load($keys = null, $reset = true) + public function check() { - $success = parent::load($keys, $reset); - - if($success) + // Support for queue field + if(isset($this->queue)) { - // Support for queue field - if(isset($this->queue) && !is_array($this->queue)) + if(\is_string($this->queue)) { $this->queue = \json_decode($this->queue); } - - // Support for successful field - if(isset($this->successful) && !is_array($this->successful)) + elseif(\is_object($this->queue)) { - $this->successful = new Registry(\json_decode($this->successful)); + $this->queue = ArrayHelper::fromObject($this->queue); } - // Support for failed field - if(isset($this->failed) && !is_array($this->failed)) + $this->queue = ArrayHelper::toInteger($this->queue); + } + + // Support for successful field + if(isset($this->successful)) + { + if(\is_string($this->successful)) { - $this->failed = new Registry(\json_decode($this->failed)); + $this->successful = \json_decode($this->successful); } - // Support for params field - if(isset($this->params) && !is_array($this->params)) + if(\is_object($this->successful)) { - $this->params = new Registry(\json_decode($this->params)); + if($this->successful instanceof Registry) + { + $this->successful = (string) $this->successful; + } + else + { + $this->successful = ArrayHelper::fromObject($this->successful); + } } + // Convert values to integer + $this->successful = ArrayHelper::toInteger($this->successful); + $this->successful = new Registry($this->successful); + } + + // Support for failed field + if(isset($this->failed)) + { + $this->failed = new Registry($this->failed); + } + + // Support for params field + if(isset($this->params)) + { + $this->params = new Registry($this->params); + } + + return parent::check(); + } + + /** + * Method to load a row from the database by primary key and bind the fields to the Table instance properties. + * + * @param mixed $keys An optional primary key value to load the row by, or an array of fields to match. + * If not set the instance property value is used. + * @param boolean $reset True to reset the default values before loading the new row. + * + * @return boolean True if successful. False if row not found. + * + * @see Table:bind + * @since 4.0.0 + */ + public function load($keys = null, $reset = true) + { + $success = parent::load($keys, $reset); + + if($success) + { + // Bring table to the correct form + $this->check(); + // Calculate progress and completed state $this->clcProgress(); } diff --git a/media/com_joomgallery/css/admin.css b/media/com_joomgallery/css/admin.css index a06398be..849011c4 100644 --- a/media/com_joomgallery/css/admin.css +++ b/media/com_joomgallery/css/admin.css @@ -95,6 +95,7 @@ img.jg-controlpanel-logo { } .log-area { font-size: 0.85rem; + min-height: 100px; max-height: 250px; overflow-y: scroll; } From a83b2f4e79317324df8b2d5f13f3f66df2239bb6 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 20 Nov 2023 15:58:57 +0100 Subject: [PATCH 034/121] add defineTypes() method --- .../src/Model/MigrationModel.php | 26 +- .../src/Service/Migration/Migration.php | 227 ++++++++++++++++-- .../Service/Migration/MigrationInterface.php | 24 ++ .../Service/Migration/Scripts/Jg3ToJg4.php | 143 +++-------- .../src/Service/Migration/Type.php | 133 ++++++++++ .../src/Table/CategoryTable.php | 17 +- .../com_joomgallery/src/Table/ImageTable.php | 15 +- 7 files changed, 448 insertions(+), 137 deletions(-) create mode 100644 administrator/com_joomgallery/src/Service/Migration/Type.php diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index c352927a..5c5d3af9 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -328,7 +328,7 @@ public function getItem($pk = null) public function getItems(): array { // Get types from migration service - $types = $this->component->getMigration()->get('types'); + $types = $this->component->getMigration()->getTypes(); // Get available types from db try @@ -470,6 +470,13 @@ public function getQueue($type, $table=null): array $query->select($db->quoteName($migrateable->get('src_pk', 'id'))) ->from($db->quoteName($migrateable->get('src_table'))) ->order($db->quoteName($migrateable->get('src_pk', 'id')) . ' ASC'); + + // Apply ordering based on level if it is a nested type + if($this->component->getMigration()->get('types')[$type]->get('nested')) + { + $query->order($db->quoteName('level') . ' ASC'); + } + $db->setQuery($query); return $db->loadColumn(); @@ -646,10 +653,7 @@ public function migrate(string $type, int $pk): object { // Create new record based on data array $sameIDs = \boolval($mig->params->get('source_ids', 0)); - $record = $this->insertRecord($type, $data, $sameIDs); - - // Set primary key value of new created record - $new_pk = $record->id; + $record = $this->insertRecord($type, (array) $data, $sameIDs); if(!$record) { @@ -658,6 +662,9 @@ public function migrate(string $type, int $pk): object } else { + // Set primary key value of new created record + $new_pk = $record->id; + // Recreate images if($type === 'image') { @@ -755,7 +762,7 @@ public function migrate(string $type, int $pk): object * * @since 4.0.0 */ - protected function insertRecord(string $type, array $data, bool $newID=true): bool + protected function insertRecord(string $type, array $data, bool $newID=true) { // Check content type JoomHelper::isAvailable($type); @@ -783,6 +790,10 @@ protected function insertRecord(string $type, array $data, bool $newID=true): bo $data['language'] = '*'; } + // Reset task + $tmp_task = $this->app->input->get('task', '', 'cmd'); + $this->app->input->set('task', 'save'); + // Bind migrated data to table object if(!$table->bind($data)) { @@ -810,6 +821,9 @@ protected function insertRecord(string $type, array $data, bool $newID=true): bo return false; } + // Restore task + $this->app->input->set('task', $tmp_task); + return $table; } diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index c1134fde..c091daeb 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -79,7 +79,7 @@ abstract class Migration implements MigrationInterface * List of content types which can be migrated with this script * Use the singular form of the content type (e.g image, not images) * - * @var array + * @var Types[] * * @since 4.0.0 */ @@ -145,6 +145,33 @@ public function __destruct() $this->component->setLogger(); } + /** + * A list of content type definitions depending on migration source + * + * @param bool $names_only True to load type names only. No migration parameters required. + * + * @return array The source types info + * array(tablename, primarykey, isNested, isCategorized, prerequirements, pkstoskip) + * Needed: tablename, primarykey, isNested, isCategorized + * Optional: prerequirements, pkstoskip + * + * @since 4.0.0 + */ + public function defineTypes($names_only = false): array + { + // Content type definition array + // Order of the content types must correspond to the migration order + // Pay attention to the prerequirements when ordering here !!! + + /* Example: + $types = array( 'category' => array('#__joomgallery_catg', 'cid', true, false, array(), array(1)), + 'image' => array('#__joomgallery', 'id', false, true, array('category')) + ); + */ + + return array(); + } + /** * Returns a list of content types which can be migrated. * @@ -341,6 +368,129 @@ protected function getSourceRootPath(): string return $root; } + /** + * Loads all available content types to Migration object. + * Gets available with the function defineTypes() from migration script. + * + * @return void + * + * @since 4.0.0 + */ + protected function loadTypes() + { + if(empty($this->types)) + { + if(\is_null($this->params)) + { + throw new Exception('Migration parameters need to be set in order to load types.', 1); + } + + $types = $this->defineTypes(); + + foreach($types as $key => $list) + { + $type = new Type($key, $list); + + $this->types[$key] = $type; + } + } + } + + /** + * Returns a list of involved source tables. + * + * @return array List of table names (Joomla style, e.g #__joomgallery) + * array('image' => '#__joomgallery', ...) + * + * @since 4.0.0 + */ + public function getSourceTables(): array + { + $this->loadTypes(); + + $tables = array(); + foreach($this->types as $key => $type) + { + $tables[$key] = $this->types[$key]->get('tablename'); + } + + return $tables; + } + + /** + * Returns tablename and primarykey name of the source table + * + * @param string $type The content type name + * + * @return array The corresponding source table info + * list(tablename, primarykey) + * + * @since 4.0.0 + */ + public function getSourceTableInfo(string $type): array + { + $this->loadTypes(); + + return array($this->types[$type]->get('tablename'), $this->types[$type]->get('pk')); + } + + /** + * Returns a list of involved content types. + * + * @return array List of type names + * array('image', 'category', ...) + * + * @since 4.0.0 + */ + public function getTypes(): array + { + $types = $this->defineTypes(true); + + return $types; + } + + /** + * True if the given record has to be migrated + * False to skip the migration for this record + * + * @param string $type Name of the content type + * @param int $pk The primary key of the content type + * + * @return bool True to continue migration, false to skip it + * + * @since 4.0.0 + */ + public function needsMigration(string $type, int $pk): bool + { + $this->loadTypes(); + + // Content types that require another type beeing migrated completely + if(!empty($this->types[$type])) + { + foreach($this->types[$type]->get('needsMigrated') as $key => $req) + { + if(!$this->migrateables[$req] || !$this->migrateables[$req]->completed || $this->migrateables[$req]->failed->count() > 0) + { + $this->continue = false; + $this->component->setError(Text::sprintf('FILES_JOOMGALLERY_MIGRATION_PREREQUIREMENT_ERROR', \implode(', ', $this->types[$type]->get('needsMigrated')))); + + return false; + } + } + } + + // Specific record primary keys which can be skiped + foreach($this->types[$type]->get('skip') as $skip) + { + if($pk == $skip) + { + return false; + } + } + + return true; + } + /** * Precheck: Check logfile and add check to checks array. * @@ -987,8 +1137,8 @@ protected function applyConvertData(array $data, array $mapping): array continue; } - - // Content gets merged into anothter data array element ('old key' => array()) + // Content gets merged into anothter data array element ('old key' => array(string, string, bool)) + // array('destination field name', 'new field name', 'create child') if(\is_array($mapping[$key])) { $destFieldName = $mapping[$key][0]; @@ -1005,31 +1155,78 @@ protected function applyConvertData(array $data, array $mapping): array $data[$destFieldName] = new Registry($data[$destFieldName]); } - // Prepare srcField - if(!($value instanceof Registry)) + // Create new field name + $newKey = $key; + if(\count($mapping[$key]) > 1 && !empty($mapping[$key][1])) { - $value = new Registry($value); + $newKey = $mapping[$key][1]; } - // Apply merge - if(\count($mapping[$key]) < 2 || empty($mapping[$key][1])) + // Prepare srcField + if(\count($mapping[$key]) > 2 && !empty($mapping[$key][2])) { - // Merge the two Registry objects (merge into root node) - $data[$destFieldName]->merge($value); + // Add as a child node + $child = new Registry($value); + $value = new Registry(array($newKey=> $child)); } else { - if(!$data[$destFieldName]->exists($key)) + // Add directly + $srcLenght = 1; + $isJson = false; + + // Detect we have a json string + if(\is_string($value)) + { + \json_decode($value); + if(json_last_error() === JSON_ERROR_NONE) + { + $isJson = true; + } + } + + // Get source lenght + elseif(\is_array($value)) + { + $srcLenght = \count($value); + } + elseif(\is_object($value)) + { + if($value instanceof \Countable) + { + $srcLenght = $value->count(); + } + else + { + $srcLenght = \count(\get_object_vars($value)); + } + } + + if($srcLenght > 1 || $isJson) { - // Attach value as a child element to destField (add as a child node) - $data[$destFieldName]->set($key, $value); + // We are trying to add a json or an object directly without adding a child + // Here 'new field name' has no effect + $value = new Registry($value); } else { - // Merge value into a child element of destField (merge into child node) - $data[$destFieldName]->append($key, $value); + if(\is_array($value)) + { + $value = $value[0]; + } + elseif(\is_object($value)) + { + $keys = \array_keys(\get_object_vars($value)); + $value = $value[$keys[0]]; + } + + // Create registry with only one key value pair + $value = new Registry(array($newKey=> $value)); } } + + // Apply merge + $data[$destFieldName]->merge($value); if($key != $destFieldName) { diff --git a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php index 5e156a47..6e3dcce7 100644 --- a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php +++ b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php @@ -60,6 +60,20 @@ public function postcheck(); */ public function migrate($type, $source, $dest); + /** + * A list of content type definitions depending on migration source + * + * @param bool $names_only True to load type names only. No migration parameters required. + * + * @return array The source types info + * array(tablename, primarykey, isNested, isCategorized, prerequirements, pkstoskip) + * Needed: tablename, primarykey, isNested, isCategorized + * Optional: prerequirements, pkstoskip + * + * @since 4.0.0 + */ + public function defineTypes($names_only = false): array; + /** * Get a database object * @@ -132,6 +146,16 @@ public function getSourceTableInfo(string $type): array; */ public function getSourceTables(): array; + /** + * Returns a list of involved content types. + * + * @return array List of type names + * array('image', 'category', ...) + * + * @since 4.0.0 + */ + public function getTypes(): array; + /** * True if the given record has to be migrated * False to skip the migration for this record diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index 5525a3a4..1d641ae7 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -39,17 +39,6 @@ class Jg3ToJg4 extends Migration implements MigrationInterface */ protected $name = 'Jg3ToJg4'; - /** - * List of content types which can be migrated with this script - * Use the singular form of the content type (e.g image, not images) - * Order in the list corresponds to migration order! - * - * @var array - * - * @since 4.0.0 - */ - protected $types = array('category', 'image'); - /** * Constructor * @@ -130,70 +119,40 @@ public function getSourceDirs(): array } /** - * Returns a list of involved source tables. - * - * @return array List of table names (Joomla style, e.g #__joomgallery) - * array('image' => '#__joomgallery', ...) + * A list of content type definitions depending on migration source * - * @since 4.0.0 - */ - public function getSourceTables(): array - { - $tables = array( '#__joomgallery', - '#__joomgallery_image_details', - '#__joomgallery_catg', - '#__joomgallery_category_details', - '#__joomgallery_comments', - '#__joomgallery_config', - '#__joomgallery_countstop', - '#__joomgallery_maintenance', - '#__joomgallery_nameshields', - '#__joomgallery_orphans', - '#__joomgallery_users', - '#__joomgallery_votes' - ); - - if($this->params->get('same_db')) - { - foreach($tables as $key => $table) - { - $tables[$key] = $table . '_old'; - } - } - - return $tables; - } - - /** - * Returns tablename and primarykey name of the source table - * - * @param string $type The content type name + * @param bool $names_only True to load type names only. No migration parameters required. * - * @return array The corresponding source table info - * list(tablename, primarykey) + * @return array The source types info + * array(tablename, primarykey, isNested, isCategorized, prerequirements, pkstoskip) + * Needed: tablename, primarykey, isNested, isCategorized + * Optional: prerequirements, pkstoskip * * @since 4.0.0 */ - public function getSourceTableInfo(string $type): array + public function defineTypes($names_only = false): array { - $tables = array( 'image' => array('#__joomgallery', 'id'), - 'category' => array('#__joomgallery_catg', 'cid') - ); + // Content type definition array + // Order of the content types must correspond to the migration order + // Pay attention to the prerequirements when ordering here !!! + $types = array( 'category' => array('#__joomgallery_catg', 'cid', true, false, array(), array(1)), + 'image' => array('#__joomgallery', 'id', false, true, array('category')) + ); - if(!\in_array($type, \array_keys($tables))) + if($names_only) { - throw new \Exception('There is no migration source table associated with the given content type. Given: ' . $type, 1); + return \array_keys($types); } if($this->params->get('same_db')) { - foreach($tables as $key => $value) + foreach($types as $key => $value) { - $tables[$key][0] = $value[0] . '_old'; + $types[$key][0] = $value[0] . '_old'; } } - return $tables[$type]; + return $types; } /** @@ -236,45 +195,6 @@ public function getData(string $type, int $pk): array } } - /** - * True if the given record has to be migrated - * False to skip the migration for this record - * - * @param string $type Name of the content type - * @param int $pk The primary key of the content type - * - * @return bool True to continue migration, false to skip it - * - * @since 4.0.0 - */ - public function needsMigration(string $type, int $pk): bool - { - // Content types that require another type beeing migrated completely - $prerequirements = array('category' => array(), 'image' => array('category')); - if(!empty($prerequirements[$type])) - { - foreach($prerequirements[$type] as $key => $req) - { - if(!$this->migrateables[$req] || !$this->migrateables[$req]->completed || $this->migrateables[$req]->failed->count() > 0) - { - $this->continue = false; - $this->component->setError(Text::sprintf('FILES_JOOMGALLERY_MIGRATION_PREREQUIREMENT_ERROR', \implode(', ', $prerequirements[$type]))); - - return false; - } - } - } - - // Specific record ids which can be skiped - $skip_records = array('category' => array(0, 1), 'image' => array(0)); - if(\in_array($pk, $skip_records[$type])) - { - return false; - } - - return true; - } - /** * Converts data from source into the structure needed for JoomGallery. * @@ -288,11 +208,13 @@ public function needsMigration(string $type, int $pk): bool public function convertData(string $type, array $data): array { /* How mappings work: - - Key not in the mapping array: Nothing changes. Field value can be magrated as it is. - - 'old key' => 'new key': Field name has changed. Old values will be inserted in field with the provided new key. - - 'old key' => false: Field does not exist anymore or value has to be emptied to create new record in the new table. - - 'old key' => array(): Field was merget into another field of type json. array('dest. field', 'child-field name within dest. field'). - If the second element of the array is 'false' means that it will be merged directly into dest. field without creating a child field in it. + - Key not in the mapping array: Nothing changes. Field value can be magrated as it is. + - 'old key' => 'new key': Field name has changed. Old values will be inserted in field with the provided new key. + - 'old key' => false: Field does not exist anymore or value has to be emptied to create new record in the new table. + - 'old key' => array(string, string, bool): Field will be merget into another field of type json. + 1. ('destination field name'): Name of the field to be merged into. + 2. ('new field name'): New name of the field created in the destination field. (default: false / retain field name) + 3. ('create child'): True, if a child node shall be created in the destination field containing the field values. (default: false / no child) */ // The fieldname of owner (created_by) @@ -308,17 +230,24 @@ public function convertData(string $type, array $data): array case 'category': // Apply mapping for category table $mapping = array( 'cid' => $id, 'asset_id' => false, 'name' => 'title', 'alias' => false, 'lft' => false, 'rgt' => false, 'level' => false, - 'owner' => $owner, 'img_position' => false, 'catpath' => 'path', 'params' => array('params', false), - 'allow_download' => array('params', 'jg_download'), 'allow_comment' => array('params', 'jg_showcomment'), 'allow_rating' => array('params', 'jg_showrating'), - 'allow_watermark' => array('params', 'jg_dynamic_watermark'), 'allow_watermark_download' => array('params', 'jg_downloadwithwatermark') + 'owner' => $owner, 'img_position' => false, 'catpath' => 'path', 'params' => array('params', false, false), + 'allow_download' => array('params', 'jg_download', false), 'allow_comment' => array('params', 'jg_showcomment', false), + 'allow_rating' => array('params', 'jg_showrating', false), 'allow_watermark' => array('params', 'jg_dynamic_watermark', false), + 'allow_watermark_download' => array('params', 'jg_downloadwithwatermark', false) ); + // Adjust parent_id based on already created categories + if(!\boolval($this->params->get('source_ids', 0)) && $data['parent_id'] > 0) + { + $data['parent_id'] = $this->migrateables['category']->successful->get($data['parent_id']); + } + break; case 'image': // Apply mapping for image table $mapping = array( 'id' => $id, 'asset_id' => false, 'alias' => false, 'imgfilename' => 'filename', 'imgthumbname' => false, - 'owner' => $owner, 'params' => array('params', false) + 'owner' => $owner, 'params' => array('params', false, false) ); // Check difference between imgfilename and imgthumbname diff --git a/administrator/com_joomgallery/src/Service/Migration/Type.php b/administrator/com_joomgallery/src/Service/Migration/Type.php new file mode 100644 index 00000000..91b57fa4 --- /dev/null +++ b/administrator/com_joomgallery/src/Service/Migration/Type.php @@ -0,0 +1,133 @@ + ** +** @copyright 2008 - 2023 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 3 or later ** +*****************************************************************************************/ + +namespace Joomgallery\Component\Joomgallery\Administrator\Service\Migration; + +// No direct access +\defined('_JEXEC') or die; + +use \Joomgallery\Component\Joomgallery\Administrator\Extension\ServiceTrait; + +/** + * Type Class + * Providing information about a content type beeing migrated + * + * @package JoomGallery + * @since 4.0.0 + */ +class Type +{ + use ServiceTrait; + + /** + * Name of this content type + * + * @var string + * + * @since 4.0.0 + */ + public $name = 'image'; + + /** + * Source table name of this content type + * + * @var string + * + * @since 4.0.0 + */ + protected $tablename = '#__joomgallery'; + + /** + * Name of the primary key + * + * @var string + * + * @since 4.0.0 + */ + protected $pk = 'id'; + + /** + * True if this content type is nested + * + * @var boolean + * + * @since 4.0.0 + */ + protected $nested = false; + + /** + * True if this content type has categories + * + * @var boolean + * + * @since 4.0.0 + */ + protected $categorized = true; + + /** + * List of primary keys that dont need migration and can be skipped. + * + * @var array + * + * @since 4.0.0 + */ + protected $skip = array(0); + + /** + * List of content types that has to be migrated before this one. + * + * @var array + * + * @since 4.0.0 + */ + protected $needsMigrated = array(); + + /** + * Constructor + * + * @param string $name Name of this content type + * @param array $list Source types info created by Migration::defineTypes() + * + * @return void + * + * @since 4.0.0 + */ + public function __construct($name, $list) + { + $this->name = $name; + + if(\count($list) < 4) + { + throw new Exception('Type object needs a list of at least 4 entries as the second argument.', 1); + } + + $this->tablename = $list[0]; + $this->pk = $list[1]; + $this->nested = $list[2]; + $this->categorized = $list[3]; + + if(\count($list) > 4) + { + $this->needsMigrated = $list[4]; + } + + if(\count($list) > 5) + { + if(\is_array($list[5])) + { + $this->skip = \array_merge($this->skip, $list[5]); + } + else + { + \array_push($this->skip, $list[5]); + } + } + } +} \ No newline at end of file diff --git a/administrator/com_joomgallery/src/Table/CategoryTable.php b/administrator/com_joomgallery/src/Table/CategoryTable.php index d02ce1ba..52222078 100644 --- a/administrator/com_joomgallery/src/Table/CategoryTable.php +++ b/administrator/com_joomgallery/src/Table/CategoryTable.php @@ -285,6 +285,13 @@ public function store($updateNulls = true) { $this->setPathWithLocation(); + // Support for params field + if(isset($this->params) && !is_string($this->params)) + { + $registry = new Registry($this->params); + $this->params = (string) $registry; + } + return parent::store($updateNulls); } @@ -351,11 +358,11 @@ public function check() $manager = JoomHelper::getService('FileManager'); $this->path = $manager->getCatPath(0, false, $this->parent_id, $this->alias); - // Support for subform field params - if(is_array($this->params)) - { - $this->params = json_encode($this->params, JSON_UNESCAPED_UNICODE); - } + // Support for params field + if(isset($this->params)) + { + $this->params = new Registry($this->params); + } return parent::check(); } diff --git a/administrator/com_joomgallery/src/Table/ImageTable.php b/administrator/com_joomgallery/src/Table/ImageTable.php index c55ac1c5..3f68c505 100644 --- a/administrator/com_joomgallery/src/Table/ImageTable.php +++ b/administrator/com_joomgallery/src/Table/ImageTable.php @@ -296,6 +296,13 @@ public function bind($array, $ignore = '') */ public function store($updateNulls = true) { + // Support for params field + if(isset($this->params) && !is_string($this->params)) + { + $registry = new Registry($this->params); + $this->params = (string) $registry; + } + $success = parent::store($updateNulls); if($success) @@ -366,10 +373,10 @@ public function check() { $this->params = $this->loadDefaultField('params'); } - elseif(\is_array($this->params)) - { - $this->params = json_encode($this->params, JSON_UNESCAPED_UNICODE); - } + if(isset($this->params)) + { + $this->params = new Registry($this->params); + } // Support for field metadesc if(empty($this->metadesc)) From a77699265d093937f0a1268bd5e27cc6a8245251 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 20 Nov 2023 18:22:36 +0100 Subject: [PATCH 035/121] fix issues while category migration --- .../src/Model/MigrationModel.php | 13 ++- .../src/Service/Migration/Migration.php | 36 ++++++++- .../Service/Migration/Scripts/Jg3ToJg4.php | 80 +++++++++---------- .../src/Table/MigrationTable.php | 2 +- 4 files changed, 85 insertions(+), 46 deletions(-) diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index 5c5d3af9..64cc257f 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -389,7 +389,8 @@ public function getItems(): array return array(); } - array_push($items, $item); + //array_push($items, $item); + $items[$type] = $item; } // Reset id to input data @@ -627,7 +628,7 @@ public function postcheck($params) public function migrate(string $type, int $pk): object { // Initialise variables - $new_pk = 0; + $new_pk = $pk; $success = true; $error_msg = ''; @@ -762,7 +763,7 @@ public function migrate(string $type, int $pk): object * * @since 4.0.0 */ - protected function insertRecord(string $type, array $data, bool $newID=true) + protected function insertRecord(string $type, array $data, bool $newID = true) { // Check content type JoomHelper::isAvailable($type); @@ -794,6 +795,12 @@ protected function insertRecord(string $type, array $data, bool $newID=true) $tmp_task = $this->app->input->get('task', '', 'cmd'); $this->app->input->set('task', 'save'); + if($this->component->getMigration()->get('types')[$type]->get('nested')) + { + // Assumption: parent primary key name for all nested types at destination is 'parent_id' + $table->setLocation($data['parent_id'], 'last-child'); + } + // Bind migrated data to table object if(!$table->bind($data)) { diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index c091daeb..510f5458 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -168,10 +168,35 @@ public function defineTypes($names_only = false): array 'image' => array('#__joomgallery', 'id', false, true, array('category')) ); */ - + return array(); } + /** + * Converts data from source into the structure needed for JoomGallery. + * + * @param string $type Name of the content type + * @param array $data Data received from getData() method. + * + * @return array Converted data to save into JoomGallery + * + * @since 4.0.0 + */ + public function convertData(string $type, array $data): array + { + /* How mappings work: + - Key not in the mapping array: Nothing changes. Field value can be magrated as it is. + - 'old key' => 'new key': Field name has changed. Old values will be inserted in field with the provided new key. + - 'old key' => false: Field does not exist anymore or value has to be emptied to create new record in the new table. + - 'old key' => array(string, string, bool): Field will be merget into another field of type json. + 1. ('destination field name'): Name of the field to be merged into. + 2. ('new field name'): New name of the field created in the destination field. (default: false / retain field name) + 3. ('create child'): True, if a child node shall be created in the destination field containing the field values. (default: false / no child) + */ + + return $data; + } + /** * Returns a list of content types which can be migrated. * @@ -1104,12 +1129,13 @@ protected function scriptSpecificChecks(Checks &$checks, string $category) * * @param array $data Data received from getData() method. * @param array $mapping Mapping array telling how to convert data. + * @param string $pk_name Name of the destination primary key. (default: 'id') * * @return array Converted data array * * @since 4.0.0 */ - protected function applyConvertData(array $data, array $mapping): array + protected function applyConvertData(array $data, array $mapping, string $pk_name = 'id'): array { // Loop through the data provided foreach($data as $key => $value) @@ -1237,6 +1263,12 @@ protected function applyConvertData(array $data, array $mapping): array } } + // Make sure the primary key field is available in the data array + if(!\array_key_exists($pk_name, $data)) + { + $data[$pk_name] = null; + } + return $data; } } diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index 1d641ae7..80ba5c8c 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -155,46 +155,6 @@ public function defineTypes($names_only = false): array return $types; } - /** - * Returns an associative array containing the record data from source. - * - * @param string $type Name of the content type - * @param int $pk The primary key of the content type - * - * @return array Associated array of a record data - * - * @since 4.0.0 - */ - public function getData(string $type, int $pk): array - { - // Get source table info - list($tablename, $primarykey) = $this->getSourceTableInfo($type); - - // Get db object - list($db, $prefix) = $this->getDB('source'); - $query = $db->getQuery(true); - - // Create the query - $query->select('*') - ->from($db->quoteName($tablename)) - ->where($db->quoteName($primarykey) . ' = ' . $db->quote($pk)); - - // Reset the query using our newly populated query object. - $db->setQuery($query); - - // Attempt to load the array - try - { - return $db->loadAssoc(); - } - catch(\Exception $e) - { - $this->component->setError($e->getMessage()); - - return array(); - } - } - /** * Converts data from source into the structure needed for JoomGallery. * @@ -288,6 +248,46 @@ public function convertData(string $type, array $data): array return $this->applyConvertData($data, $mapping); } + /** + * Returns an associative array containing the record data from source. + * + * @param string $type Name of the content type + * @param int $pk The primary key of the content type + * + * @return array Associated array of a record data + * + * @since 4.0.0 + */ + public function getData(string $type, int $pk): array + { + // Get source table info + list($tablename, $primarykey) = $this->getSourceTableInfo($type); + + // Get db object + list($db, $prefix) = $this->getDB('source'); + $query = $db->getQuery(true); + + // Create the query + $query->select('*') + ->from($db->quoteName($tablename)) + ->where($db->quoteName($primarykey) . ' = ' . $db->quote($pk)); + + // Reset the query using our newly populated query object. + $db->setQuery($query); + + // Attempt to load the array + try + { + return $db->loadAssoc(); + } + catch(\Exception $e) + { + $this->component->setError($e->getMessage()); + + return array(); + } + } + /** * Fetches an array of images from source to be used for creating the imagetypes * for the current image. diff --git a/administrator/com_joomgallery/src/Table/MigrationTable.php b/administrator/com_joomgallery/src/Table/MigrationTable.php index 05648616..3da7f188 100644 --- a/administrator/com_joomgallery/src/Table/MigrationTable.php +++ b/administrator/com_joomgallery/src/Table/MigrationTable.php @@ -209,7 +209,7 @@ public function check() { if($this->successful instanceof Registry) { - $this->successful = (string) $this->successful; + $this->successful = $this->successful->toArray(); } else { From 2575dc53d2598b62b7e563e76e58cc800b908d6d Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 20 Nov 2023 21:07:49 +0100 Subject: [PATCH 036/121] first migration of categories successful --- .../language/en-GB/com_joomgallery.ini | 1 + .../com_joomgallery.migration.Jg3ToJg4.ini | 2 +- .../src/Controller/MigrationController.php | 9 +++ .../src/Model/MigrationModel.php | 75 +++++++++++++------ .../com_joomgallery/tmpl/migration/step3.php | 2 +- .../js/migrator/dist/migrator.js | 59 ++++++++++++++- .../js/migrator/dist/migrator.js.map | 2 +- .../com_joomgallery/js/migrator/src/index.js | 59 ++++++++++++++- 8 files changed, 179 insertions(+), 30 deletions(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index 1b9a0082..c3ae8a39 100755 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -708,6 +708,7 @@ COM_JOOMGALLERY_SERVICE_ERROR_MIGRATION_RESUME="Migration can not be resumed. Th COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_CONVERT_DATA="Data from source could not be converted to new data structure. See log file for more destails." COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_INSERT_RECORD="Data could not be inserted into destination database. See log file for more destails." COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_CREATE_IMGTYPE="Error in creating imagetypes. See log file for more destails." +COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_CREATE_FOLDER="Error in creating folders. See log file for more destails." COM_JOOMGALLERY_SERVICE_MIGRATION_FILENAME_DIFF="It was noticed that two different filenames were used for detail/original and thumbnail in the image with id: %s and alias: %s. The migration script will skip the migration of this image." COM_JOOMGALLERY_SERVICE_MIGRATION_ACTIVE="Currently in migration" diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini index 9601a6d2..cb9005f1 100644 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini @@ -8,7 +8,7 @@ ; FILES_JOOMGALLERY_MIGRATION_JG3TOJG4="JoomGallery 3.x to JoomGallery 4.x migration script" FILES_JOOMGALLERY_MIGRATION_JG3TOJG4_TITLE="JoomGallery 3.x to JoomGallery 4.x" -FILES_JOOMGALLERY_MIGRATION_JG3TOJG4_DESC="Migration of content types (images, categories, tags) from JoomGallery version 3.x to JoomGallery version 4.x." +FILES_JOOMGALLERY_MIGRATION_JG3TOJG4_DESC="Migration of content types (images, categories) from JoomGallery version 3.x to JoomGallery version 4.x." FILES_JOOMGALLERY_MIGRATION_JG3TOJG4_ORIGPATH_DESC="Path to original image" FILES_JOOMGALLERY_MIGRATION_JG3TOJG4_DETAILPATH_DESC="Path to detail image" FILES_JOOMGALLERY_MIGRATION_JG3TOJG4_THUMBPATH_DESC="Path to thumbnail image" diff --git a/administrator/com_joomgallery/src/Controller/MigrationController.php b/administrator/com_joomgallery/src/Controller/MigrationController.php index 75fffcb1..f2e4244f 100644 --- a/administrator/com_joomgallery/src/Controller/MigrationController.php +++ b/administrator/com_joomgallery/src/Controller/MigrationController.php @@ -598,6 +598,15 @@ public function start() // Perform the migration $table = $model->migrate($type, $id); + // Stop automatic execution if migrateable is complete + if($table->completed) + { + $this->component->getMigration()->set('continue', false); + + // Check in record + $model->checkin($table->id); + } + // Check for errors if(!empty($this->component->getError())) { diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index 64cc257f..0c3d9c0e 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -24,6 +24,7 @@ use \Joomla\CMS\Language\Multilanguage; use \Joomgallery\Component\Joomgallery\Administrator\Table\MigrationTable; use \Joomgallery\Component\Joomgallery\Administrator\Table\ImageTable; +use \Joomgallery\Component\Joomgallery\Administrator\Table\CategoryTable; use \Joomgallery\Component\Joomgallery\Administrator\Helper\JoomHelper; /** @@ -616,7 +617,7 @@ public function postcheck($params) } /** - * Method to perform one migration of one record. + * Method to perform the migration of one record. * * @param string $type Name of the content type to migrate. * @param integer $pk The primary key of the source record. @@ -666,27 +667,40 @@ public function migrate(string $type, int $pk): object // Set primary key value of new created record $new_pk = $record->id; - // Recreate images - if($type === 'image') + // Post processing steps + switch($type) { - $img_source = $this->component->getMigration()->getImageSource($data); - if(\array_key_first($img_source) === 0) - { - // Create imagetypes based on given image and mapping - $res = $this->createImages($record, $img_source[0]); - } - else - { - // Reuse images from source as imagetypes (no image creation) - $res = $this->reuseImages($record, $img_source); - } - - if(!$res) - { - $record = $this->deleteRecord($type, $new_pk); - $success = false; - $error_msg = Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_CREATE_IMGTYPE'); - } + case 'image': + $img_source = $this->component->getMigration()->getImageSource($data); + if(\array_key_first($img_source) === 0) + { + // Create imagetypes based on given image and mapping + $res = $this->createImages($record, $img_source[0]); + } + else + { + // Reuse images from source as imagetypes (no image creation) + $res = $this->reuseImages($record, $img_source); + } + + $error_msg_end = 'CREATE_IMGTYPE'; + break; + + case 'category': + $res = $this->createFolder($record); + + $error_msg_end = 'CREATE_FOLDER'; + + default: + $res = true; + break; + } + + if(!$res) + { + $record = $this->deleteRecord($type, $new_pk); + $success = false; + $error_msg = Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_'.$error_msg_end); } } } @@ -898,6 +912,7 @@ protected function createImages(ImageTable $img, string $source): bool // Create file manager service $this->component->createFileManager(); + // Create imagetypes return $this->component->getFileManager()->createImages($source, $img->filename, $img->catid); } @@ -984,4 +999,22 @@ protected function reuseImages(ImageTable $img, array $sources): bool return true; } } + + /** + * Creation of category folders based on one source file. + * + * @param CategoryTable $cat CategoryTable object, already stored + * + * @return bool True on success, false otherwise + * + * @since 4.0.0 + */ + protected function createFolder(CategoryTable $cat): bool + { + // Create file manager service + $this->component->createFileManager(); + + // Create folders + return $this->component->getFileManager()->createCategory($cat->alias, $cat->parent_id); + } } diff --git a/administrator/com_joomgallery/tmpl/migration/step3.php b/administrator/com_joomgallery/tmpl/migration/step3.php index d089d075..45965110 100644 --- a/administrator/com_joomgallery/tmpl/migration/step3.php +++ b/administrator/com_joomgallery/tmpl/migration/step3.php @@ -87,7 +87,7 @@
-
+
-

Step 1: Migration configuration

+

:


error)): ?> diff --git a/administrator/com_joomgallery/tmpl/migration/step2.php b/administrator/com_joomgallery/tmpl/migration/step2.php index 99166f24..6a4b0da7 100644 --- a/administrator/com_joomgallery/tmpl/migration/step2.php +++ b/administrator/com_joomgallery/tmpl/migration/step2.php @@ -26,14 +26,14 @@
-

Step 2: Migration pre-check

+

:


error)): ?> diff --git a/administrator/com_joomgallery/tmpl/migration/step3.php b/administrator/com_joomgallery/tmpl/migration/step3.php index 19dfb323..c539dc8d 100644 --- a/administrator/com_joomgallery/tmpl/migration/step3.php +++ b/administrator/com_joomgallery/tmpl/migration/step3.php @@ -36,14 +36,14 @@
-

Step 3: Perform migration

+

:


error)): ?> @@ -123,7 +123,7 @@ migrateables)))) + if(empty(array_diff_key($completed, array_keys($this->migrateables))) && empty(array_diff_key(array_keys($this->migrateables), $completed))) { $total_complete = true; } diff --git a/administrator/com_joomgallery/tmpl/migration/step4.php b/administrator/com_joomgallery/tmpl/migration/step4.php index 0936a74a..b8d46f9f 100644 --- a/administrator/com_joomgallery/tmpl/migration/step4.php +++ b/administrator/com_joomgallery/tmpl/migration/step4.php @@ -28,14 +28,14 @@
-

Step 4: Migration post-check

+

:


error)): ?> From 048a6b287e0f2e4e8f1238ca9d8ca2d48790255d Mon Sep 17 00:00:00 2001 From: Elfangor Date: Tue, 5 Dec 2023 12:23:39 +0100 Subject: [PATCH 045/121] add step 4 --- .../language/en-GB/com_joomgallery.ini | 35 +++- .../src/Controller/MigrationController.php | 151 +++++++++++++++++- .../src/Model/MigrationModel.php | 66 ++++++-- .../src/Service/Migration/Migration.php | 139 ++++++++++++++-- .../Service/Migration/MigrationInterface.php | 4 +- .../Service/Migration/Scripts/Jg3ToJg4.php | 58 ++++--- .../src/Service/Migration/Type.php | 17 +- .../src/View/Migration/HtmlView.php | 16 +- .../com_joomgallery/tmpl/migration/step2.php | 2 +- .../com_joomgallery/tmpl/migration/step4.php | 125 ++++++++++++++- 10 files changed, 552 insertions(+), 61 deletions(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index 62e13c3a..75b8f89e 100644 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -202,6 +202,7 @@ COM_JOOMGALLERY_GENERIC_UPLOAD_DATA="Data entered in this form is applied on all COM_JOOMGALLERY_FILE_TITLE_HINT="Title of this file" COM_JOOMGALLERY_FILE_DESCRIPTION_HINT="Description of this file" COM_JOOMGALLERY_FILE_AUTHOR_HINT="Author of this file" +COM_JOOMGALLERY_MIGRATION_INERRUPT_MIGRATION="Interrupt migration" COM_JOOMGALLERY_MIGRATION_START_SCRIPT="Start script" COM_JOOMGALLERY_MIGRATION_AVAILABLE_SCRIPTS="Available migration scripts" COM_JOOMGALLERY_MIGRATION_ABORT_MIGRATION="Abort migration" @@ -211,8 +212,13 @@ COM_JOOMGALLERY_MIGRATION_STEP1_BTN_TXT="Check migration capability" COM_JOOMGALLERY_MIGRATION_STEP2_TITLE="Migration pre-check" COM_JOOMGALLERY_MIGRATION_STEP2_BTN_TXT="Start migration manager" COM_JOOMGALLERY_MIGRATION_STEP3_TITLE="Perform migration" -COM_JOOMGALLERY_MIGRATION_STEP3_BTN_TXT="Finish migration" +COM_JOOMGALLERY_MIGRATION_STEP3_BTN_TXT="Check & finish migration" COM_JOOMGALLERY_MIGRATION_STEP4_TITLE="Finish migration" +COM_JOOMGALLERY_MIGRATION_STEP4_BTN_TXT="Back to step 3" +COM_JOOMGALLERY_MIGRATION_BTN_REMOVE_SOURCE="Remove source data" +COM_JOOMGALLERY_MIGRATION_BTN_END_MIGRATION="End migration and delete progress" +COM_JOOMGALLERY_MIGRATION_BTN_REMOVE_SOURCE_CONFIRM="Are you sure, you want to remove all source data? This is irreversible and prevents you from performing further migrations with these sources in the future." +COM_JOOMGALLERY_MIGRATION_REMOVE_SOURCE_DATA_DESC="If you are really sure that you have finished and completed all migrations using this migration script, you can use this functionality to remove all source data (databse & filesystem). If your source is another extension (component, module, plugin), deinstall this instead.

Attention!!
This is irreversible and prevents you from performing further migrations with these sources in the future." COM_JOOMGALLERY_SHOWLOG="Show log output" COM_JOOMGALLERY_MIGRATION_START="Start migration" COM_JOOMGALLERY_MIGRATION_STOP="Stop migration" @@ -688,10 +694,13 @@ COM_JOOMGALLERY_SERVICE_ERROR_CREATE_FILE="File could not be created." COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_NOT_A_DIRECTORY="Requested path is not a directory." COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_NOT_FOUND="No filesystem plugin available for the specified adapter (%s)." COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_ERROR="Filesystem plugin is reporting the following error: %s" -COM_JOOMGALLERY_SERVICE_MIGRATION_PRECHECK_TITLE="Check results" -COM_JOOMGALLERY_SERVICE_MIGRATION_GENERAL_CHECK_DESC="General checks like log file, site state, ..." -COM_JOOMGALLERY_SERVICE_MIGRATION_SOURCE_CHECK_DESC="Check source extension, directories and tables if they are compatible and existent." -COM_JOOMGALLERY_SERVICE_MIGRATION_DESTINATION_CHECK_DESC="Check destination extension, directories and tables if they are compatible, existent and writeable." +COM_JOOMGALLERY_SERVICE_MIGRATION_CHECK_TITLE="Check results" +COM_JOOMGALLERY_SERVICE_MIGRATION_GENERAL_PRECHECK_DESC="General checks like log file, site state, ..." +COM_JOOMGALLERY_SERVICE_MIGRATION_SOURCE_PRECHECK_DESC="Check source extension, directories and tables if they are compatible and existent." +COM_JOOMGALLERY_SERVICE_MIGRATION_DESTINATION_PRECHECK_DESC="Check destination extension, directories and tables if they are compatible, existent and writeable." +COM_JOOMGALLERY_SERVICE_MIGRATION_GENERAL_POSTCHECK_DESC="General checks to verify that migrations are completed." +COM_JOOMGALLERY_SERVICE_MIGRATION_DIRS_POSTCHECK_DESC="Check destination directory for orphans." +COM_JOOMGALLERY_SERVICE_MIGRATION_DB_POSTCHECK_DESC="Check that all needed files and folders exists." COM_JOOMGALLERY_SERVICE_MIGRATION_DIRECTORY_SUCCESS="Directory exists and is usable." COM_JOOMGALLERY_SERVICE_MIGRATION_LOG_DIR_LABEL="Logging directory" COM_JOOMGALLERY_SERVICE_MIGRATION_LOGFILE_ERROR="Log file '%s' is not writable. Please check permissions." @@ -707,7 +716,10 @@ COM_JOOMGALLERY_SERVICE_MIGRATION_PHP_WRONG_VERSION="The current PHP version is COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP2="Step 2: Migration pre-check failed. Reason: %s" COM_JOOMGALLERY_SERVICE_MIGRATION_SUCCESS_MIGRATION_STEP2="Step 2: Migration pre-check successful." COM_JOOMGALLERY_SERVICE_MIGRATION_WARNING_MIGRATION_STEP2="Step 2: Warning appeared during migration pre-check. Reason: %s" -COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP2_CHECKS_FAILED="Some of the checks failed." +COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_CHECKS_FAILED="Some of the checks failed." +COM_JOOMGALLERY_SERVICE_MIGRATION_SUCCESS_MIGRATION_STEP4="Step 4: Migration post-check successful." +COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP4="Step 4: Migration post-check failed. Reason: %s" +COM_JOOMGALLERY_SERVICE_MIGRATION_WARNING_MIGRATION_STEP4="Step 4: Warning appeared during migration post-check. Reason: %s" COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES="There are already %s records in this table." COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES_EMPTY="This table is empty." COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES_USE_IDS_HINT="You have set 'Use source IDs' to true in step 1. But the ID's from source are not free/available in the destination.
Please delete the records with the following ID's: %s." @@ -731,6 +743,17 @@ COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_CREATE_IMGTYPE="Error in creating image COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_CREATE_FOLDER="Error in creating folders. See log file for more destails." COM_JOOMGALLERY_SERVICE_MIGRATION_FILENAME_DIFF="It was noticed that two different filenames were used for detail/original and thumbnail in the image with id: %s and alias: %s. The migration script will skip the migration of this image." COM_JOOMGALLERY_SERVICE_MIGRATION_ACTIVE="Currently in migration" +COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_APPLYSTATE_FORMCHECK="Record migration state couldn\'t be modified. Please fill out form correctly." +COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_APPLYSTATE_SUCCESSFUL_0="Record of type %s with id=%s successfully marked as failed" +COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_APPLYSTATE_SUCCESSFUL_1="Record of type %s with id=%s successfully marked as successful" +COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_APPLYSTATE_SUCCESSFUL_2="Record of type %s with id=%s successfully marked as pending" +COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_APPLYSTATE_NOT_AVAILABLE="Record with id=%s not available. State can not be changed." +COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_MIGRATIONS_ERROR="It seems that not all queued migrations are processed. Please go back to step 3 and finish performing all migrations." +COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_MIGRATIONS_SUCCESS="All queued migrations are successfully processed." +COM_JOOMGALLERY_SERVICE_MIGRATION_QUEUE="Migration queue" +COM_JOOMGALLERY_SERVICE_MIGRATION_QUEUE_ERROR="The migration queue of %s have not yet been fully processed. Please go back to step 3 and finish performing all migrations." +COM_JOOMGALLERY_SERVICE_MIGRATION_ERRORS_ERROR="Error during migration of type '%s' and ID '%s'. Error message: %s." +COM_JOOMGALLERY_SERVICE_MIGRATION_ERRORS_SUCCESS="No errors detected from migration manager." ;Menu COM_JOOMGALLERY_MENU_CATEGORY_VIEW_OPTIONS="Category View" diff --git a/administrator/com_joomgallery/src/Controller/MigrationController.php b/administrator/com_joomgallery/src/Controller/MigrationController.php index 94f914d5..eb3ee90a 100644 --- a/administrator/com_joomgallery/src/Controller/MigrationController.php +++ b/administrator/com_joomgallery/src/Controller/MigrationController.php @@ -322,6 +322,84 @@ public function delete() return true; } + /** + * Method to remove migration source data (filesystem & database). + * + * @return boolean True on success, false otherwise + * + * @since 4.0.0 + */ + public function removesource() + { + $this->checkToken(); + + $model = $this->getModel(); + $script = $this->app->getUserStateFromRequest(_JOOM_OPTION.'.migration.script', 'script', '', 'cmd'); + $scripts = $model->getScripts(); + + // Check if requested script exists + if(!\in_array($script, \array_keys($scripts))) + { + // Requested script does not exists + throw new Exception('Requested migration script does not exist.', 1); + } + + // Access check. + $acl = $this->component->getAccess(); + if(!$acl->checkACL('admin', 'com_joomgallery')) + { + $this->setMessage(Text::sprintf('COM_JOOMGALLERY_ERROR_TASK_NOT_PERMITTED', 'migration.start'), 'error'); + $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration', false)); + + return false; + } + + // Check if script allows source data removal + if(!$model->sourceDeletion()) + { + $this->setMessage(Text::sprintf('COM_JOOMGALLERY_ERROR_TASK_NOT_PERMITTED', 'migration.removesource'), 'error'); + $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration&layout=step4', false)); + + return false; + } + + $postcheck = $this->app->getUserState(_JOOM_OPTION.'.migration.'.$script.'.step4.success', false); + + // Check if no errors detected in postcheck (step 4) + if(!$postcheck) + { + // Post-checks not successful. Show error message. + $msg = Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_CHECKS_FAILED'); + $this->setMessage(Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP4', $msg), 'error'); + // Redirect to the step 4 screen + $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration&layout=step4', false)); + } + + // Get items to remove from the request. + $cid = (array) $this->input->get('cid', [], 'int'); + + // Remove zero values resulting from input filter + $cid = \array_filter($cid); + + if(!empty($cid)) + { + // Remove the source data. + if($model->deleteSource($cid)) + { + $this->app->enqueueMessage(Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_SOURCE_DATA_DELETE_SUCCESSFUL')); + } + else + { + $this->app->enqueueMessage($model->getError(), 'error'); + } + } + + // Redirect to the step 4 screen. + $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration&layout=step4', false)); + + return true; + } + /** * Step 2 * Validate the form input data and perform the pre migration checks. @@ -491,7 +569,7 @@ public function migrate() if(!$precheck) { // Pre-checks not successful. Show error message. - $msg = Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP2_CHECKS_FAILED'); + $msg = Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_CHECKS_FAILED'); $this->setMessage(Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP2', $msg), 'error'); // Redirect to the step 2 screen $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration&layout=step2', false)); @@ -501,6 +579,73 @@ public function migrate() $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration&layout=step3', false)); } + /** + * Step 4 + * Perform the post migration checks. + * + * @return void + * + * @since 4.0.0 + * @throws \Exception + */ + public function postcheck() + { + // Check for request forgeries + $this->checkToken(); + + // Access check. + $acl = $this->component->getAccess(); + if(!$acl->checkACL('admin', 'com_joomgallery')) + { + $this->setMessage(Text::sprintf('COM_JOOMGALLERY_ERROR_TASK_NOT_PERMITTED', 'migration.start'), 'error'); + $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration', false)); + + return false; + } + + $model = $this->getModel(); + $script = $this->app->getUserStateFromRequest(_JOOM_OPTION.'.migration.script', 'script', '', 'cmd'); + $scripts = $model->getScripts(); + + // Check if requested script exists + if(!\in_array($script, \array_keys($scripts))) + { + // Requested script does not exists + throw new \Exception('Requested migration script does not exist.', 1); + } + + $context = _JOOM_OPTION.'.migration.'.$script.'.step4'; + $task = $this->getTask(); + + // Perform the post migration checks + list($success, $res, $msg) = $model->postcheck(); + if(!$success) + { + // Post-checks not successful. Show error message. + $this->setMessage(Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP4', $msg), 'error'); + } + else + { + // Pre-checks successful. Show success message. + $this->setMessage(Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_SUCCESS_MIGRATION_STEP4')); + + if(!empty($msg)) + { + // Warnings appeared. Show warning message. + $this->app->enqueueMessage(Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_WARNING_MIGRATION_STEP4', $msg), 'warning'); + } + } + + // Save the results of the post migration checks in the session. + $this->app->setUserState($context . '.results', $res); + $this->app->setUserState($context . '.success', $success); + + // Redirect to the screen to show the results (View of Step 4) + $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration&layout=step4', false)); + + return; + } + /** * Perform a migration * Called by Ajax requests @@ -546,7 +691,7 @@ public function start() if(!$precheck) { // Pre-checks not successful. Show error message. - $response = $this->createRespond(null, false, Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP2_CHECKS_FAILED')); + $response = $this->createRespond(null, false, Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_CHECKS_FAILED')); $this->ajaxRespond($response, $format); return false; @@ -661,7 +806,7 @@ public function applyState() if(!$precheck) { // Pre-checks not successful. Show error message. - $this->setMessage(Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP2_CHECKS_FAILED'), 'error'); + $this->setMessage(Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_CHECKS_FAILED'), 'error'); $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration&layout=step2', false)); return false; diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index b578a186..f797db47 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -456,6 +456,26 @@ public function getIdList(): array return $ids; } + /** + * Method to get the sourceDeletion flag from migration script + * + * @return bool True to offer the task migration.removesource + * + * @since 4.0.0 + */ + public function getSourceDeletion(): bool + { + // Retreive script + $script = $this->getScript(); + + if(!$script) + { + return false; + } + + return $this->component->getMigration()->get('sourceDeletion', false); + } + /** * Load the current queue of ids from table * @@ -620,21 +640,19 @@ public function precheck($params) /** * Method to perform the post migration checks. - * - * @param array $params The migration parameters entered in the migration form * * @return array|boolean An array containing the postcheck results on success. * * @since 4.0.0 */ - public function postcheck($params) - { - $info = $this->getScript(); - - // Set the migration parameters - $this->setParams($params); - - // Perform the prechecks + public function postcheck() + { + // Prepare the migration object + $migs = $this->getMigrateables(); + $keys = \array_keys($migs); + $this->setParams($migs[$keys[0]]->params); + + // Perform the postchecks return $this->component->getMigration()->postcheck(); } @@ -707,7 +725,7 @@ public function migrate(string $type, int $pk): object // Recreate imagetypes based on given image $res = $this->createImages($record, $img_source[0]); } - elseif($mig->params->get('image_usage') == 2 || $this->params->get('image_usage') == 3) + elseif($mig->params->get('image_usage') == 2 || $mig->params->get('image_usage') == 3) { $copy = false; if($mig->params->get('image_usage') == 2) @@ -951,6 +969,24 @@ public function applyState(string $type, int $state, int $src_pk, int $dest_pk = return $ret_table; } + /** + * Method to delete migration source data. + * + * @return boolean True if successful, false if an error occurs. + * + * @since 4.0.0 + */ + public function deleteSource() + { + // Prepare the migration object + $migs = $this->getMigrateables(); + $keys = \array_keys($migs); + $this->setParams($migs[$keys[0]]->params); + + // Perform the postchecks + return $this->component->getMigration()->deleteSource(); + } + /** * Method to insert a content type record from migration data. * @@ -964,11 +1000,13 @@ public function applyState(string $type, int $state, int $src_pk, int $dest_pk = */ protected function insertRecord(string $type, array $data, bool $newID = true) { + $recordType = $this->component->getMigration()->get('types')[$type]->get('recordName'); + // Check content type - JoomHelper::isAvailable($type); + JoomHelper::isAvailable($recordType); // Create table - if(!$table = $this->getMVCFactory()->createTable($type, 'administrator')) + if(!$table = $this->getMVCFactory()->createTable($recordType, 'administrator')) { $this->component->setError(Text::sprintf('COM_JOOMGALLERY_ERROR_IMGTYPE_TABLE_NOT_EXISTING', $type)); @@ -984,7 +1022,7 @@ protected function insertRecord(string $type, array $data, bool $newID = true) // Special case: Only modification not creation of record if(!$this->component->getMigration()->get('types')[$type]->get('isMigration') && $data[$key] > 0) { - if($table->load($key)) + if($table->load($data[$key])) { // Table successfully loaded $isNew = false; diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index 86792fe6..89409aa5 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -66,6 +66,15 @@ abstract class Migration implements MigrationInterface */ protected $name = ''; + /** + * True to offer the task migration.removesource for this script + * + * @var boolean + * + * @since 4.0.0 + */ + protected $sourceDeletion = false; + /** * Is the migration performed from the command line * @@ -146,14 +155,14 @@ public function __destruct() } /** - * A list of migrations/types which can be performed with this migration script + * A list of content type definitions depending on migration source * * @param bool $names_only True to load type names only. No migration parameters required. * * @return array The source types info - * array(tablename, primarykey, isNested, isCategorized, prerequirements, pkstoskip) + * array(tablename, primarykey, isNested, isCategorized, prerequirements, pkstoskip, ismigration, recordname) * Needed: tablename, primarykey, isNested, isCategorized - * Optional: prerequirements, pkstoskip + * Optional: prerequirements, pkstoskip, ismigration, recordname * * @since 4.0.0 */ @@ -260,12 +269,12 @@ public function precheck(): array $checks = new Checks(); // Check general requirements - $checks->addCategory('general', Text::_('COM_JOOMGALLERY_GENERAL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_GENERAL_CHECK_DESC')); + $checks->addCategory('general', Text::_('COM_JOOMGALLERY_GENERAL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_GENERAL_PRECHECK_DESC')); $this->checkLogFile($checks, 'general'); $this->checkSiteState($checks, 'general'); // Check source extension (version, compatibility) - $checks->addCategory('source', Text::_('COM_JOOMGALLERY_SOURCE'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_SOURCE_CHECK_DESC')); + $checks->addCategory('source', Text::_('COM_JOOMGALLERY_SOURCE'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_SOURCE_PRECHECK_DESC')); $this->checkSourceExtension($checks, 'source'); // Check existance and writeability of source directories @@ -275,7 +284,7 @@ public function precheck(): array $this->checkSourceTable($checks, 'source'); // Check destination extension (version, compatibility) - $checks->addCategory('destination', Text::_('COM_JOOMGALLERY_DESTINATION'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_DESTINATION_CHECK_DESC')); + $checks->addCategory('destination', Text::_('COM_JOOMGALLERY_DESTINATION'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_DESTINATION_PRECHECK_DESC')); $this->checkDestExtension($checks, 'destination'); // Check existance and writeability of destination directories @@ -291,7 +300,7 @@ public function precheck(): array } // Perform some script specific checks - $this->scriptSpecificChecks($checks, 'general'); + $this->scriptSpecificChecks('pre', $checks, 'general'); return $checks->getAll(); } @@ -306,7 +315,30 @@ public function precheck(): array */ public function postcheck() { - return; + // Instantiate a new checks class + $checks = new Checks(); + + // Check general migration + $checks->addCategory('general', Text::_('COM_JOOMGALLERY_GENERAL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_GENERAL_POSTCHECK_DESC')); + + // Check if all queues have been addressed and migrated + $this->checkMigrationQueues($checks, 'general'); + // Check if there are still errors in the migration + $this->checkMigrationErrors($checks, 'general'); + + // Check database + // $checks->addCategory('database', Text::_('JLIB_FORM_VALUE_SESSION_DATABASE'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_DB_POSTCHECK_DESC')); + // $this->checkCategories($checks, 'database'); + // $this->checkImages($checks, 'database'); + + // Check filesystem + // $checks->addCategory('directories', Text::_('COM_JOOMGALLERY_DIRECTORIES'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_DIRS_POSTCHECK_DESC')); + // $this->checkFolder($checks, 'directories'); + + // Perform some script specific checks + $this->scriptSpecificChecks('post', $checks, 'general'); + + return $checks->getAll(); } /** @@ -322,6 +354,19 @@ public function migrate($type, $source, $dest) return; } + /** + * Step 4 + * Delete migration source data. + * + * @return boolean True if successful, false if an error occurs. + * + * @since 4.0.0 + */ + public function deleteSource() + { + return true; + } + /** * Get a database object * @@ -1110,8 +1155,82 @@ protected function checkImageMapping(Checks &$checks, string $category) } /** - * Precheck: Perform script specific checks + * Postcheck: Check if all queues are completed + * + * @param Checks $checks The checks object + * @param string $category The checks-category into which to add the new check + * + * @return void + * + * @since 4.0.0 + */ + protected function checkMigrationQueues(Checks &$checks, string $category) + { + $types = $this->getTypes(); + $migrateables = $this->getMigrateables(); + + // Check if all types are existent in migrateables + if(\count(\array_diff($types, \array_keys($migrateables))) > 0) + { + $checks->addCheck($category, 'types_migrated', false, false, Text::_('COM_JOOMGALLERY_MIGRATIONS'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_MIGRATIONS_ERROR')); + return; + } + + // Check if all queues in migrateables are empty + $empty = true; + foreach($migrateables as $key => $mig) + { + if(\count($mig->queue) > 0) + { + $checks->addCheck($category, 'queue_' . $mig->type, false, false, Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_QUEUE'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_MIGRATIONS_ERROR', $mig->type)); + $empty = false; + } + } + + if($empty) + { + $checks->addCheck($category, 'queues', true, false, Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_QUEUE'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_MIGRATIONS_SUCCESS')); + } + } + + /** + * Postcheck: Check if all migrateables are error free + * + * @param Checks $checks The checks object + * @param string $category The checks-category into which to add the new check + * + * @return void + * + * @since 4.0.0 + */ + protected function checkMigrationErrors(Checks &$checks, string $category) + { + $migrateables = $this->getMigrateables(); + + // Check if all migrateables are error free + $errors = false; + foreach($migrateables as $key => $mig) + { + if($mig->failed->count() > 0) + { + foreach($mig->failed->toArray()as $id => $error) + { + $checks->addCheck($category, 'error_' . $mig->type . '_' . $id, false, false, Text::_('ERROR') . ': ' . $mig->type, Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_ERRORS_ERROR', $mig->type, $id, $error)); + $errors = true; + } + } + } + + if(!$errors) + { + $checks->addCheck($category, 'errors', true, false, Text::_('SUCCESS'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ERRORS_SUCCESS')); + } + } + + /** + * Perform script specific checks * + * @param string $type Type of checks (pre or post) * @param Checks $checks The checks object * @param string $category The checks-category into which to add the new check * @@ -1119,7 +1238,7 @@ protected function checkImageMapping(Checks &$checks, string $category) * * @since 4.0.0 */ - protected function scriptSpecificChecks(Checks &$checks, string $category) + protected function scriptSpecificChecks(string $type, Checks &$checks, string $category) { return; } diff --git a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php index 6494f84e..c94a9e37 100644 --- a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php +++ b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php @@ -66,9 +66,9 @@ public function migrate($type, $source, $dest); * @param bool $names_only True to load type names only. No migration parameters required. * * @return array The source types info - * array(tablename, primarykey, isNested, isCategorized, prerequirements, pkstoskip, ismigration) + * array(tablename, primarykey, isNested, isCategorized, prerequirements, pkstoskip, ismigration, recordname) * Needed: tablename, primarykey, isNested, isCategorized - * Optional: prerequirements, pkstoskip, ismigration + * Optional: prerequirements, pkstoskip, ismigration, recordname * * @since 4.0.0 */ diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index 6ca6c843..5011222c 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -40,6 +40,15 @@ class Jg3ToJg4 extends Migration implements MigrationInterface */ protected $name = 'Jg3ToJg4'; + /** + * True to offer the task migration.removesource for this script + * + * @var boolean + * + * @since 4.0.0 + */ + protected $sourceDeletion = true; + /** * Constructor * @@ -125,9 +134,9 @@ public function getSourceDirs(): array * @param bool $names_only True to load type names only. No migration parameters required. * * @return array The source types info - * array(tablename, primarykey, isNested, isCategorized, prerequirements, pkstoskip, ismigration) + * array(tablename, primarykey, isNested, isCategorized, prerequirements, pkstoskip, ismigration, recordname) * Needed: tablename, primarykey, isNested, isCategorized - * Optional: prerequirements, pkstoskip, ismigration + * Optional: prerequirements, pkstoskip, ismigration, recordname * * @since 4.0.0 */ @@ -138,7 +147,7 @@ public function defineTypes($names_only = false): array // Pay attention to the prerequirements when ordering here !!! $types = array( 'category' => array('#__joomgallery_catg', 'cid', true, false, array(), array(1)), 'image' => array('#__joomgallery', 'id', false, true, array('category')), - 'catimage' => array(_JOOM_TABLE_CATEGORIES, 'id', false, false, array('category', 'image'), array(1), false) + 'catimage' => array(_JOOM_TABLE_CATEGORIES, 'id', false, false, array('category', 'image'), array(1), false, 'category') ); if($names_only) @@ -249,6 +258,8 @@ public function convertData(string $type, array $data): array $data['thumbnail'] = 0; } } + + break; default: // The table structure is the same @@ -383,8 +394,9 @@ public function getImageSource(array $data): array } /** - * Precheck: Perform script specific checks + * Perform script specific checks * + * @param string $type Type of checks (pre or post) * @param Checks $checks The checks object * @param string $category The checks-category into which to add the new check * @@ -392,27 +404,35 @@ public function getImageSource(array $data): array * * @since 4.0.0 */ - protected function scriptSpecificChecks(Checks &$checks, string $category) + protected function scriptSpecificChecks(string $type, Checks &$checks, string $category) { - // Check if imgfilename and imgthumbname are the same - list($db, $dbPrefix) = $this->getDB('source'); - list($tablename, $pkname) = $this->getSourceTableInfo('image'); + if($type == 'pre') + { + // Check if imgfilename and imgthumbname are the same + list($db, $dbPrefix) = $this->getDB('source'); + list($tablename, $pkname) = $this->getSourceTableInfo('image'); - // Create the query - $query = $db->getQuery(true) - ->select($db->quoteName(array('id'))) - ->from($tablename) - ->where($db->quoteName('imgfilename') . ' != ' . $db->quoteName('imgthumbname')); - $db->setQuery($query); + // Create the query + $query = $db->getQuery(true) + ->select($db->quoteName(array('id'))) + ->from($tablename) + ->where($db->quoteName('imgfilename') . ' != ' . $db->quoteName('imgthumbname')); + $db->setQuery($query); - // Load a list of ids that have different values for imgfilename and imgthumbname - $res = $db->loadColumn(); + // Load a list of ids that have different values for imgfilename and imgthumbname + $res = $db->loadColumn(); - if(!empty(\count($res))) - { - $checks->addCheck($category, 'src_table_image_filename', true, true, Text::_('FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_TITLE'), Text::sprintf('FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_DESC', \count($res)), Text::sprintf('FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_HELP', \implode(', ', $res))); + if(!empty(\count($res))) + { + $checks->addCheck($category, 'src_table_image_filename', true, true, Text::_('FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_TITLE'), Text::sprintf('FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_DESC', \count($res)), Text::sprintf('FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_HELP', \implode(', ', $res))); + } } + if($type == 'post') + { + + } + return; } } \ No newline at end of file diff --git a/administrator/com_joomgallery/src/Service/Migration/Type.php b/administrator/com_joomgallery/src/Service/Migration/Type.php index b0e42829..2f6a5479 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Type.php +++ b/administrator/com_joomgallery/src/Service/Migration/Type.php @@ -35,6 +35,15 @@ class Type */ public $name = 'image'; + /** + * Name of the corresponding record + * + * @var string + * + * @since 4.0.0 + */ + public $recordName = 'image'; + /** * Source table name of this content type * @@ -111,7 +120,8 @@ class Type */ public function __construct($name, $list) { - $this->name = $name; + $this->name = $name; + $this->recordName = $name; if(\count($list) < 4) { @@ -144,5 +154,10 @@ public function __construct($name, $list) { $this->isMigration = $list[6]; } + + if(\count($list) > 7) + { + $this->recordName = $list[7]; + } } } \ No newline at end of file diff --git a/administrator/com_joomgallery/src/View/Migration/HtmlView.php b/administrator/com_joomgallery/src/View/Migration/HtmlView.php index e8dd8332..2549f195 100644 --- a/administrator/com_joomgallery/src/View/Migration/HtmlView.php +++ b/administrator/com_joomgallery/src/View/Migration/HtmlView.php @@ -52,7 +52,7 @@ public function display($tpl = null) if($this->layout != 'default') { $this->app->input->set('hidemainmenu', true); - ToolbarHelper::cancel('migration.cancel', 'JTOOLBAR_CLOSE'); + ToolbarHelper::cancel('migration.cancel', 'COM_JOOMGALLERY_MIGRATION_INERRUPT_MIGRATION'); // Check if requested script exists if(!\in_array($this->script->name, \array_keys($this->scripts))) @@ -95,7 +95,19 @@ public function display($tpl = null) case 'step4': // Load postcheck results - $this->postcheck = $this->app->getUserState(_JOOM_OPTION.'.migration.'.$this->script->name.'.step4.results', array()); + $this->postcheck = $this->app->getUserState(_JOOM_OPTION.'.migration.'.$this->script->name.'.step4.results', array()); + $this->success = $this->app->getUserState(_JOOM_OPTION.'.migration.'.$this->script->name.'.step4.success', false); + $this->sourceDeletion = $this->get('sourceDeletion'); + + $this->openMigrations = $this->get('IdList'); + if(!empty($this->openMigrations) && \key_exists($this->script->name, $this->openMigrations)) + { + $this->openMigrations = $this->openMigrations[$this->script->name]; + } + else + { + $this->openMigrations = array(); + } break; default: diff --git a/administrator/com_joomgallery/tmpl/migration/step2.php b/administrator/com_joomgallery/tmpl/migration/step2.php index 6a4b0da7..5fea3126 100644 --- a/administrator/com_joomgallery/tmpl/migration/step2.php +++ b/administrator/com_joomgallery/tmpl/migration/step2.php @@ -62,7 +62,7 @@
title): ?>
-

title; ?>

+

title; ?>

desc): ?> desc; ?> diff --git a/administrator/com_joomgallery/tmpl/migration/step4.php b/administrator/com_joomgallery/tmpl/migration/step4.php index b8d46f9f..e08142e6 100644 --- a/administrator/com_joomgallery/tmpl/migration/step4.php +++ b/administrator/com_joomgallery/tmpl/migration/step4.php @@ -24,8 +24,7 @@ ->useScript('com_joomgallery.admin'); ?> -
- +
\ No newline at end of file From 9caf2ffcd650a91fa047c4e05282d518d50514e4 Mon Sep 17 00:00:00 2001 From: Elfangor Date: Tue, 5 Dec 2023 14:01:25 +0100 Subject: [PATCH 046/121] add source data removal button --- .../com_joomgallery/language/en-GB/com_joomgallery.ini | 1 + .../src/Controller/MigrationController.php | 4 ++-- administrator/com_joomgallery/tmpl/migration/step4.php | 8 ++++++-- media/com_joomgallery/js/form-edit.js | 10 +++++++++- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index 75b8f89e..a3b2063b 100644 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -754,6 +754,7 @@ COM_JOOMGALLERY_SERVICE_MIGRATION_QUEUE="Migration queue" COM_JOOMGALLERY_SERVICE_MIGRATION_QUEUE_ERROR="The migration queue of %s have not yet been fully processed. Please go back to step 3 and finish performing all migrations." COM_JOOMGALLERY_SERVICE_MIGRATION_ERRORS_ERROR="Error during migration of type '%s' and ID '%s'. Error message: %s." COM_JOOMGALLERY_SERVICE_MIGRATION_ERRORS_SUCCESS="No errors detected from migration manager." +COM_JOOMGALLERY_SERVICE_MIGRATION_SOURCE_DATA_DELETE_SUCCESSFUL="Source data successfully removed." ;Menu COM_JOOMGALLERY_MENU_CATEGORY_VIEW_OPTIONS="Category View" diff --git a/administrator/com_joomgallery/src/Controller/MigrationController.php b/administrator/com_joomgallery/src/Controller/MigrationController.php index eb3ee90a..e9126387 100644 --- a/administrator/com_joomgallery/src/Controller/MigrationController.php +++ b/administrator/com_joomgallery/src/Controller/MigrationController.php @@ -355,7 +355,7 @@ public function removesource() } // Check if script allows source data removal - if(!$model->sourceDeletion()) + if(!$model->getSourceDeletion()) { $this->setMessage(Text::sprintf('COM_JOOMGALLERY_ERROR_TASK_NOT_PERMITTED', 'migration.removesource'), 'error'); $this->setRedirect(Route::_('index.php?option=' . _JOOM_OPTION . '&view=migration&layout=step4', false)); @@ -386,7 +386,7 @@ public function removesource() // Remove the source data. if($model->deleteSource($cid)) { - $this->app->enqueueMessage(Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_SOURCE_DATA_DELETE_SUCCESSFUL')); + $this->app->enqueueMessage(Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_SOURCE_DATA_DELETE_SUCCESSFUL'), 'message'); } else { diff --git a/administrator/com_joomgallery/tmpl/migration/step4.php b/administrator/com_joomgallery/tmpl/migration/step4.php index e08142e6..ec2600b4 100644 --- a/administrator/com_joomgallery/tmpl/migration/step4.php +++ b/administrator/com_joomgallery/tmpl/migration/step4.php @@ -21,7 +21,9 @@ // Import CSS $wa = Factory::getApplication()->getDocument()->getWebAssetManager(); $wa->useStyle('com_joomgallery.admin') - ->useScript('com_joomgallery.admin'); + ->useScript('com_joomgallery.admin') + ->useScript('form.validate') + ->useScript('com_joomgallery.form-edit'); ?>
@@ -131,7 +133,7 @@
success) : ?> -
+ sourceDeletion) : ?>
@@ -157,6 +159,8 @@ + + openMigrations as $openMigration) : ?> diff --git a/media/com_joomgallery/js/form-edit.js b/media/com_joomgallery/js/form-edit.js index 0781e6fd..16af3244 100644 --- a/media/com_joomgallery/js/form-edit.js +++ b/media/com_joomgallery/js/form-edit.js @@ -16,7 +16,15 @@ const submitTask = task => { const form = document.getElementById(formId); const type = document.getElementById(typeId).value; - if (task === type+'.cancel' || document.formvalidator.isValid(form)) { + const btn = document.querySelector(`[${buttonDataSelector}="${task}"]`); + if (task === type+'.cancel') { + submitForm(task, form); + } + if (btn.parentElement.getAttribute('confirm-message')) { + if(confirm(btn.parentElement.getAttribute('confirm-message')) && document.formvalidator.isValid(form)) { + submitForm(task, form); + } + } else if (document.formvalidator.isValid(form)) { submitForm(task, form); } }; From a05ebc1678e819e5edc7e8e02e4836abf5b0f70e Mon Sep 17 00:00:00 2001 From: Elfangor Date: Wed, 6 Dec 2023 10:11:55 +0100 Subject: [PATCH 047/121] add checkin to migration.cancel task --- .../src/Controller/MigrationController.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/administrator/com_joomgallery/src/Controller/MigrationController.php b/administrator/com_joomgallery/src/Controller/MigrationController.php index e9126387..2cc79357 100644 --- a/administrator/com_joomgallery/src/Controller/MigrationController.php +++ b/administrator/com_joomgallery/src/Controller/MigrationController.php @@ -145,6 +145,22 @@ public function cancel() return false; } + // Get migrateables if available + $migrateables = $model->getMigrateables(); + + // Checkin migration records of this script + if($migrateables) + { + foreach($migrateables as $mig) + { + if($mig->checked_out || \intval($mig->checked_out) > 0) + { + // Check in record + $model->checkin($mig->id); + } + } + } + // Clean the session data and redirect. $this->app->setUserState(_JOOM_OPTION.'.migration.script', null); $this->app->setUserState(_JOOM_OPTION.'.migration.'.$script.'.params', null); From 74242d5a7347a4695262c9be4f966690198e8181 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Sat, 23 Dec 2023 11:22:44 +0100 Subject: [PATCH 048/121] add incompatible joomla versions --- .../language/en-GB/com_joomgallery.ini | 2 +- script.php | 20 +++++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index a3b2063b..31ee7329 100644 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -242,7 +242,7 @@ COM_JOOMGALLERY_ERROR_CONFIG_INVALID_CONTEXT="Error deriving settings from confi COM_JOOMGALLERY_RESET_CONFIRM="Are you sure you want to restore to factory default?" COM_JOOMGALLERY_CONFIRM_DELETE_CATEGORIES="Are you sure you want to delete the selected categories?" COM_JOOMGALLERY_SETTINGS_POPUP="Settings-Popup" -COM_JOOMGALLERY_ERROR_JOOMLA_COMPATIBILITY="JoomGallery %s is only compatible to Joomla! %s." +COM_JOOMGALLERY_ERROR_JOOMLA_COMPATIBILITY="JoomGallery %s is not compatible with the current Joomla! version %s." COM_JOOMGALLERY_ERROR_PHP_COMPATIBILITY="JoomGallery %s is only compatible to PHP versions greater than 7.3. Your PHP version is %s." COM_JOOMGALLERY_ERROR_READ_XML_FILE="JoomGallery manifest XML file could not be readed." COM_JOOMGALLERY_ERROR_CREATE_DEFAULT_CATEGORY="Default category could not be created." diff --git a/script.php b/script.php index bafa6fa3..885bf7e2 100644 --- a/script.php +++ b/script.php @@ -38,6 +38,13 @@ class com_joomgalleryInstallerScript extends InstallerScript */ protected $extension = 'JoomGallery'; + /** + * List of incompatible Joomla versions + * + * @var array + */ + protected $incompatible = array('4.4.0', '4.4.1', '5.0.0', '5.0.1'); + /** * Minimum PHP version required to install the extension * @@ -95,7 +102,16 @@ public function preflight($type, $parent) // Only proceed if Joomla version is correct if(version_compare(JVERSION, '4.0.0', '<')) { - Factory::getApplication()->enqueueMessage(Text::sprintf('COM_JOOMGALLERY_ERROR_JOOMLA_COMPATIBILITY', '4.x', '4.x'), 'error'); + Factory::getApplication()->enqueueMessage(Text::sprintf('COM_JOOMGALLERY_ERROR_JOOMLA_COMPATIBILITY', '4.x', JVERSION), 'error'); + + return false; + } + + // Only proceed if it is not an incompatible Joomla version + $jversion = explode('-', JVERSION); + if(in_array($jversion[0], $this->incompatible)) + { + Factory::getApplication()->enqueueMessage(Text::sprintf('COM_JOOMGALLERY_ERROR_JOOMLA_COMPATIBILITY', '4.x', JVERSION), 'error'); return false; } @@ -103,7 +119,7 @@ public function preflight($type, $parent) // Only proceed if PHP version is correct if(version_compare(PHP_VERSION, $this->minPhp, '<=')) { - Factory::getApplication()->enqueueMessage(Text::sprintf('COM_JOOMGALLERY_ERROR_PHP_COMPATIBILITY', '4.x', '7.3', $this->minPhp), 'error'); + Factory::getApplication()->enqueueMessage(Text::sprintf('COM_JOOMGALLERY_ERROR_PHP_COMPATIBILITY', '4.x', '7.4', $this->minPhp), 'error'); return false; } From 283803801e3cc49041c1c737e6032be25cd7d330 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Thu, 28 Dec 2023 15:50:55 +0100 Subject: [PATCH 049/121] add MigrationTableTrait to script.php --- script.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/script.php b/script.php index 885bf7e2..e307191c 100644 --- a/script.php +++ b/script.php @@ -549,10 +549,16 @@ public function addDefaultCategory() $db = Factory::getContainer()->get(DatabaseInterface::class); // Load JoomTableTrait - $trait_path = JPATH_ADMINISTRATOR.DIRECTORY_SEPARATOR.'components'.DIRECTORY_SEPARATOR.'com_joomgallery'.DIRECTORY_SEPARATOR.'src'.DIRECTORY_SEPARATOR.'Table'.DIRECTORY_SEPARATOR.'JoomTableTrait.php'; - $traitClass = '\\Joomgallery\\Component\\Joomgallery\\Administrator\\Table\\JoomTableTrait'; + $joomtabletrait_path = JPATH_ADMINISTRATOR.DIRECTORY_SEPARATOR.'components'.DIRECTORY_SEPARATOR.'com_joomgallery'.DIRECTORY_SEPARATOR.'src'.DIRECTORY_SEPARATOR.'Table'.DIRECTORY_SEPARATOR.'JoomTableTrait.php'; + $joomtabletraitClass = '\\Joomgallery\\Component\\Joomgallery\\Administrator\\Table\\JoomTableTrait'; - require_once $trait_path; + require_once $joomtabletrait_path; + + // Load MigrationTableTrait + $migrationtabletrait_path = JPATH_ADMINISTRATOR.DIRECTORY_SEPARATOR.'components'.DIRECTORY_SEPARATOR.'com_joomgallery'.DIRECTORY_SEPARATOR.'src'.DIRECTORY_SEPARATOR.'Table'.DIRECTORY_SEPARATOR.'MigrationTableTrait.php'; + $migrationtabletraitClass = '\\Joomgallery\\Component\\Joomgallery\\Administrator\\Table\\MigrationTableTrait'; + + require_once $migrationtabletrait_path; // Load CategoryTable $class_path = JPATH_ADMINISTRATOR.DIRECTORY_SEPARATOR.'components'.DIRECTORY_SEPARATOR.'com_joomgallery'.DIRECTORY_SEPARATOR.'src'.DIRECTORY_SEPARATOR.'Table'.DIRECTORY_SEPARATOR.'CategoryTable.php'; From 22ab5b6644877a64a8766cf9f4f2117edbfde3cb Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 1 Jan 2024 09:30:12 +0100 Subject: [PATCH 050/121] #__joomgallery_migration missing when update --- .../sql/updates/mysql/4.0.0.sql | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/administrator/com_joomgallery/sql/updates/mysql/4.0.0.sql b/administrator/com_joomgallery/sql/updates/mysql/4.0.0.sql index 0a2f7b0c..517e0886 100644 --- a/administrator/com_joomgallery/sql/updates/mysql/4.0.0.sql +++ b/administrator/com_joomgallery/sql/updates/mysql/4.0.0.sql @@ -167,6 +167,7 @@ CREATE TABLE IF NOT EXISTS `#__joomgallery_configs` ( `jg_votingonlyonce` TINYINT(1) NOT NULL DEFAULT 1, `jg_report_images` TINYINT(1) NOT NULL DEFAULT 1, `jg_report_hint` TINYINT(1) NOT NULL DEFAULT 1, +`jg_showcomments` TINYINT(1) NOT NULL DEFAULT 1, PRIMARY KEY (`id`), KEY `idx_checkout` (`checked_out`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci; @@ -372,6 +373,30 @@ KEY `idx_createdby` (`created_by`) -- -------------------------------------------------------- +-- +-- Table structure for table `#__joomgallery_migration` +-- + +CREATE TABLE IF NOT EXISTS `#__joomgallery_migration` ( +`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, +`script` VARCHAR(50) NOT NULL DEFAULT "", +`type` VARCHAR(50) NOT NULL DEFAULT "", +`src_table` VARCHAR(255) NOT NULL DEFAULT "", +`src_pk` VARCHAR(25) NOT NULL DEFAULT "id", +`dst_table` VARCHAR(255) NOT NULL DEFAULT "", +`dst_pk` VARCHAR(25) NOT NULL DEFAULT "id", +`queue` TEXT NOT NULL, +`successful` TEXT NOT NULL, +`failed` TEXT NOT NULL, +`params` TEXT NOT NULL, +`created_time` DATETIME NOT NULL, +`checked_out` INT(11) UNSIGNED NOT NULL DEFAULT 0, +`checked_out_time` DATETIME DEFAULT NULL, +PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci; + +-- -------------------------------------------------------- + -- -- Dumping data for table `#__content_types` -- From 6895bec69122e72f376be82bd478c71b42f87abd Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 1 Jan 2024 09:32:20 +0100 Subject: [PATCH 051/121] add missing use statement in ImagetypeTable.php --- administrator/com_joomgallery/src/Table/ImagetypeTable.php | 1 + 1 file changed, 1 insertion(+) diff --git a/administrator/com_joomgallery/src/Table/ImagetypeTable.php b/administrator/com_joomgallery/src/Table/ImagetypeTable.php index 19697ecd..c3b011c3 100644 --- a/administrator/com_joomgallery/src/Table/ImagetypeTable.php +++ b/administrator/com_joomgallery/src/Table/ImagetypeTable.php @@ -14,6 +14,7 @@ defined('_JEXEC') or die; use \Joomla\CMS\Table\Table; +use \Joomla\Registry\Registry; use \Joomla\Database\DatabaseDriver; /** From 9afe62810e4b90420b00695f067682bc347d6416 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Tue, 2 Jan 2024 12:06:40 +0100 Subject: [PATCH 052/121] fix php error if XML is not found --- .../language/en-GB/com_joomgallery.ini | 1 + .../src/Service/Migration/Migration.php | 8 +++++++- .../Service/Migration/MigrationInterface.php | 4 ++-- .../Service/Migration/Scripts/Jg3ToJg4.php | 20 ++++++++++++++++--- 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index 31ee7329..54716f54 100644 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -712,6 +712,7 @@ COM_JOOMGALLERY_SERVICE_MIGRATION_OFFLINE_ERROR="Website is currently online." COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_NOT_SUPPORTED="Extension not supported (Extension: %s)" COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_WRONG_VERSION="The current version of the extension is not supported. Current version: %s. Supported version: %s" COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_SUCCESS="Extension is compatible (Extension: %s, Version: %s)" +COM_JOOMGALLERY_SERVICE_MIGRATION_SOURCE_XML="The XML file of your source extension could not be found. Please make sure the XML is available and readable." COM_JOOMGALLERY_SERVICE_MIGRATION_PHP_WRONG_VERSION="The current PHP version is not supported. Current version: %s. Minimum requirement: %s" COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP2="Step 2: Migration pre-check failed. Reason: %s" COM_JOOMGALLERY_SERVICE_MIGRATION_SUCCESS_MIGRATION_STEP2="Step 2: Migration pre-check successful." diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index 89409aa5..a1da2dcc 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -622,7 +622,13 @@ protected function checkLogFile(Checks &$checks, string $category) protected function checkSourceExtension(Checks &$checks, string $category) { $src_info = $this->getTargetinfo('source'); - $src_xml = $this->getSourceXML(); + + if(!($src_xml = $this->getSourceXML())) + { + // Source XML not found + $checks->addCheck($category, 'src_xml', false, false, Text::_('COM_JOOMGALLERY_FIELDS_SRC_EXTENSION_LABEL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_SOURCE_XML')); + return; + } if(\version_compare(PHP_VERSION, $src_info->get('php_min'), '<')) { diff --git a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php index c94a9e37..b610223c 100644 --- a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php +++ b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php @@ -109,11 +109,11 @@ public function getTargetinfo(string $type = 'source'): Targetinfo; /** * Returns the XML object of the source extension * - * @return \SimpleXMLElement Extension XML object + * @return \SimpleXMLElement Extension XML object or False on failure * * @since 4.0.0 */ - public function getSourceXML(): \SimpleXMLElement; + public function getSourceXML(); /** * Returns a list of involved source directories. diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index 5011222c..ee8b784c 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -102,13 +102,27 @@ public function getTargetinfo(string $type = 'source'): Targetinfo /** * Returns the XML object of the source extension * - * @return \SimpleXMLElement Extension XML object + * @return \SimpleXMLElement Extension XML object or False on failure * * @since 4.0.0 */ - public function getSourceXML(): \SimpleXMLElement + public function getSourceXML() { - return \simplexml_load_file(Path::clean(JPATH_ADMINISTRATOR . '/components/com_joomgallery/joomgallery_old.xml')); + if($this->params->get('same_joomla')) + { + return \simplexml_load_file(Path::clean(JPATH_ADMINISTRATOR . '/components/com_joomgallery/joomgallery_old.xml')); + } + else + { + if(\file_exists(Path::clean($this->params->get('joomla_path') . '/components/com_joomgallery/joomgallery.xml'))) + { + return \simplexml_load_file(Path::clean($this->params->get('joomla_path') . '/components/com_joomgallery/joomgallery.xml')); + } + else + { + return \simplexml_load_file(Path::clean($this->params->get('joomla_path') . '/components/com_joomgallery/joomgallery_old.xml')); + } + } } /** From f0aa61fcb9cbce64702f431d943977e1ca51148f Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Tue, 2 Jan 2024 12:40:20 +0100 Subject: [PATCH 053/121] fix checkDestTableIdAvailability() --- .../src/Service/Migration/Migration.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index a1da2dcc..50350f7e 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -622,7 +622,7 @@ protected function checkLogFile(Checks &$checks, string $category) protected function checkSourceExtension(Checks &$checks, string $category) { $src_info = $this->getTargetinfo('source'); - + if(!($src_xml = $this->getSourceXML())) { // Source XML not found @@ -965,7 +965,7 @@ protected function checkDestTable(Checks &$checks, string $category) { $checks->addCheck($category, $check_name, true, false, Text::_('COM_JOOMGALLERY_TABLE') . ': ' . $tablename, Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES', $count)); } - } + } } /** @@ -1025,8 +1025,8 @@ protected function checkDestTableIdAvailability(Checks &$checks, string $categor { // Get list of used ids from source databse $srcQuery = $db->getQuery(true); - $srcQuery->select($db->quoteName($migrateable->get('pk'), 'id')) - ->from($db->quoteName($migrateable->get('table'))); + $srcQuery->select($db->quoteName($migrateable->get('src_pk'), 'id')) + ->from($db->quoteName($migrateable->get('src_table'))); $srcQuery_string = \trim($srcQuery->__toString()); // Get a list of ids used in both source and destination @@ -1043,8 +1043,8 @@ protected function checkDestTableIdAvailability(Checks &$checks, string $categor // Get list of used ids from the source database $query = $src_db->getQuery(true); - $query->select($db->quoteName($migrateable->get('pk'), 'id')) - ->from($db->quoteName($migrateable->get('table'))); + $query->select($db->quoteName($migrateable->get('src_pk'), 'id')) + ->from($db->quoteName($migrateable->get('src_table'))); $src_db->setQuery($query); // Load list from source database From a2e970f427ab9b4effefc1c583a9b677da02dcd6 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Tue, 2 Jan 2024 12:59:22 +0100 Subject: [PATCH 054/121] adjust language strings --- .../com_joomgallery/language/en-GB/com_joomgallery.ini | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index 54716f54..3258c588 100644 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -222,13 +222,13 @@ COM_JOOMGALLERY_MIGRATION_REMOVE_SOURCE_DATA_DESC="If you are really sure that y COM_JOOMGALLERY_SHOWLOG="Show log output" COM_JOOMGALLERY_MIGRATION_START="Start migration" COM_JOOMGALLERY_MIGRATION_STOP="Stop migration" -COM_JOOMGALLERY_MIGRATION_MANUAL="Manual migration" +COM_JOOMGALLERY_MIGRATION_MANUAL="Manual repair" COM_JOOMGALLERY_MIGRATION_MANUAL_BTN="Mark as repaired" -COM_JOOMGALLERY_MIGRATION_MANUAL_DESC="This form can be used to change the state of a migration record.
For example: Failed migrations can be manually patched or migrated and with this form, marked as successful. This can be used to mark blocking records as successful in order to continue with the automatic migration." +COM_JOOMGALLERY_MIGRATION_MANUAL_DESC="This form can be used to change the state of a migration record manually.

For example: Failed migrations can be manually patched or migrated and with this form, marked as successful. This can be used to mark blocking records as successful in order to continue with the automatic migration process." COM_JOOMGALLERY_MIGRATION_REPAIR_SRCPK_LABEL="Source pk" -COM_JOOMGALLERY_MIGRATION_REPAIR_SRCPK_DESC="Primary key of the source record" +COM_JOOMGALLERY_MIGRATION_REPAIR_SRCPK_DESC="Primary key of the source record to be repaired" COM_JOOMGALLERY_MIGRATION_REPAIR_DESTPK_LABEL="Destination pk" -COM_JOOMGALLERY_MIGRATION_REPAIR_DESTPK_DESC="Primary key of the destination record" +COM_JOOMGALLERY_MIGRATION_REPAIR_DESTPK_DESC="Primary key of the destination record to be repaired" COM_JOOMGALLERY_MIGRATION_REPAIR_CONFIRM="I confirm that I successfully migrated and/or fixed the specified record." COM_JOOMGALLERY_MIGRATION_REPAIR_STATE_LABEL="Change state to" COM_JOOMGALLERY_MIGRATION_REPAIR_STATE_DESC="Select a state to which you want to change the selected source record to." @@ -744,7 +744,7 @@ COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_CREATE_IMGTYPE="Error in creating image COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_CREATE_FOLDER="Error in creating folders. See log file for more destails." COM_JOOMGALLERY_SERVICE_MIGRATION_FILENAME_DIFF="It was noticed that two different filenames were used for detail/original and thumbnail in the image with id: %s and alias: %s. The migration script will skip the migration of this image." COM_JOOMGALLERY_SERVICE_MIGRATION_ACTIVE="Currently in migration" -COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_APPLYSTATE_FORMCHECK="Record migration state couldn\'t be modified. Please fill out form correctly." +COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_APPLYSTATE_FORMCHECK="Record migration state couldn't be modified. Please fill out form correctly." COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_APPLYSTATE_SUCCESSFUL_0="Record of type %s with id=%s successfully marked as failed" COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_APPLYSTATE_SUCCESSFUL_1="Record of type %s with id=%s successfully marked as successful" COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_APPLYSTATE_SUCCESSFUL_2="Record of type %s with id=%s successfully marked as pending" From 27ae3fda3e23220be9d3f77d66025300600e916a Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Tue, 2 Jan 2024 13:27:28 +0100 Subject: [PATCH 055/121] Enable next mig-button when previous is completed --- .../js/migrator/dist/migrator.js | 41 +++++++++++++++++-- .../js/migrator/dist/migrator.js.map | 2 +- .../com_joomgallery/js/migrator/src/index.js | 41 +++++++++++++++++-- 3 files changed, 77 insertions(+), 7 deletions(-) diff --git a/media/com_joomgallery/js/migrator/dist/migrator.js b/media/com_joomgallery/js/migrator/dist/migrator.js index a0166581..22236f3d 100644 --- a/media/com_joomgallery/js/migrator/dist/migrator.js +++ b/media/com_joomgallery/js/migrator/dist/migrator.js @@ -47,7 +47,7 @@ __webpack_require__.r(__webpack_exports__); // Selectors used by this script let typeSelector = 'data-type'; let formIdTmpl = 'migrationForm'; -let button = 'migrationBtn'; +let buttonTmpl = 'migrationBtn'; let step4Btn = 'step4Btn'; let tryLimit = 3; @@ -534,7 +534,7 @@ let startTask = function(type, button) { /** * Update GUI to end migration * - * @param {String} type The type defining the content type to be updated + * @param {String} type The type defining the content type to be updated * @param {DOM Element} button The button beeing pressed to start the task * * @returns void @@ -559,12 +559,47 @@ let finishTask = function(type, button) { stopBtn.classList.add('disabled'); stopBtn.setAttribute('disabled', 'true'); - // Enable step 4 button + // If migration is completed if(migrateablesList[type]['complete']) { + // Update next start button + enableNextBtn(type, button); + // Update step 4 button updateStep4Btn(); } } +/** + * Enable start button of next migration content type + * + * @param {String} type The type defining the content type to be updated + * @param {DOM Element} button The current start button + * + * @returns void + */ +let enableNextBtn = function(type, button) { + let types_inputs = document.getElementsByName('type'); + let next_type = ''; + + // Find next migration content type + let this_type = false; + types_inputs.forEach((type_input) => { + if(this_type) { + next_type = type_input.value; + return; + } + if(Boolean(type_input.value) && type_input.value == type) { + this_type = true; + } + }); + + // Get next button + let nextBtn = document.getElementById(buttonTmpl + '-' + next_type); + + // Enable button + nextBtn.classList.remove('disabled'); + nextBtn.removeAttribute('disabled'); +} + /** * Update button to go to step 4 * diff --git a/media/com_joomgallery/js/migrator/dist/migrator.js.map b/media/com_joomgallery/js/migrator/dist/migrator.js.map index 5084fabc..2194196d 100644 --- a/media/com_joomgallery/js/migrator/dist/migrator.js.map +++ b/media/com_joomgallery/js/migrator/dist/migrator.js.map @@ -1 +1 @@ -{"version":3,"file":"migrator.js","mappings":";;;UAAA;UACA;;;;;WCDA;WACA;WACA;WACA;WACA,yCAAyC,wCAAwC;WACjF;WACA;WACA;;;;;WCPA;;;;;WCAA;WACA;WACA;WACA,uDAAuD,iBAAiB;WACxE;WACA,gDAAgD,aAAa;WAC7D;;;;;;;;;;;;;;ACNA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,SAAS;AAClB;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kCAAkC,oGAAoG;AACtI;AACA,gCAAgC;AAChC;AACA;AACA;AACA;AACA;AACA;AACA;AACA,QAAQ;AACR;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kFAAkF,gBAAgB;AAClG;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB;AACA,aAAa,UAAU;AACvB,aAAa,qDAAqD,aAAa;AAC/E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,gFAAgF,SAAS;AACrG;AACA;AACA,sBAAsB;AACtB;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA,WAAW,mFAAmF,SAAS;AACvG,IAAI;AACJ;AACA,8BAA8B;AAC9B,6BAA6B;AAC7B;AACA,WAAW;AACX;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB;AACA,aAAa,UAAU;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,qDAAqD,WAAW;AAC7E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oCAAoC;AACpC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oCAAoC;AACpC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB,YAAY,UAAU;AACtB,cAAc;AACd;AACA;AACA;AACA;AACA;AACA;AACA,kBAAkB;AAClB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB,YAAY,UAAU;AACtB,cAAc;AACd;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,yDAAyD,kDAAkD;AAC3G;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA,C","sources":["webpack://Migrator/webpack/bootstrap","webpack://Migrator/webpack/runtime/define property getters","webpack://Migrator/webpack/runtime/hasOwnProperty shorthand","webpack://Migrator/webpack/runtime/make namespace object","webpack://Migrator/./src/index.js"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","// Selectors used by this script\r\nlet typeSelector = 'data-type';\r\nlet formIdTmpl = 'migrationForm';\r\nlet button = 'migrationBtn';\r\nlet step4Btn = 'step4Btn';\r\nlet tryLimit = 3;\r\n\r\n/**\r\n * Storage for migrateables\r\n * @var {Object} migrateablesList\r\n */\r\nvar migrateablesList = {};\r\n\r\n/**\r\n * Counter of how many times the same migration was tried to perfrom\r\n * @var {Integer} tryCounter\r\n */\r\nvar tryCounter = 0;\r\n\r\n/**\r\n * State. As long as this state is set to true, the migration will be\r\n * continued automatically regarding the pending queue in the migrateablesList.\r\n * @var {Boolean} continueState\r\n */\r\nvar continueState = true;\r\n\r\n/**\r\n * State. Set this state to true to stop automatic execution as soon as the next ajax respond comes back.\r\n * @var {Boolean} forceStop\r\n */\r\nvar forceStop = false;\r\n\r\n/**\r\n * Submit the migration task by pressing the button\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let submitTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n let type = element.getAttribute(typeSelector);\r\n let formId = formIdTmpl + '-' + type;\r\n let task = element.parentNode.querySelector('[name=\"task\"]').value;\r\n\r\n startTask(type, element);\r\n\r\n tryCounter++;\r\n\r\n ajax(formId, task)\r\n .then(res => {\r\n // Handle the successful result here\r\n responseHandler(type, res);\r\n\r\n console.log('forceStop: ' + forceStop);\r\n console.log('continueState: ' + continueState);\r\n\r\n if(tryCounter >= tryLimit) {\r\n // We reached the limit of tries --> looks like we have a network problem\r\n updateMigrateables(type, {'success': false, 'message': Joomla.JText._('COM_JOOMGALLERY_ERROR_NETWORK_PROBLEM'), 'data': false});\r\n // Stop automatic execution and update GUI\r\n console.log('forceStop; We reached the limit of tries');\r\n forceStop = true;\r\n }\r\n \r\n if(continueState && !forceStop) {\r\n // Kick off the next task\r\n console.log('Kick off the next task');\r\n submitTask(event, element);\r\n } else {\r\n // Stop automatic task execution and update GUI\r\n console.log('Stop automatic task execution and update GUI');\r\n finishTask(type, element);\r\n }\r\n })\r\n .catch(error => {\r\n // Handle any errors here\r\n console.error(error);\r\n addLog(error, type, 'error');\r\n });\r\n};\r\n\r\n/**\r\n * Stop the migration task by pressing the button\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let stopTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n let type = element.getAttribute(typeSelector);\r\n let bar = document.getElementById('progress-'+type);\r\n let startBtn = document.getElementById('migrationBtn-'+type);\r\n let stopBtn = element;\r\n\r\n // Force automatic execution to stop\r\n forceStop = true;\r\n\r\n // Update progress bar\r\n bar.classList.remove('progress-bar-striped');\r\n bar.classList.remove('progress-bar-animated');\r\n \r\n // Enable start button\r\n startBtn.classList.remove('disabled');\r\n startBtn.removeAttribute('disabled');\r\n\r\n // Disable stop button\r\n stopBtn.classList.add('disabled');\r\n stopBtn.setAttribute('disabled', 'true');\r\n}\r\n\r\n/**\r\n * Manually set one record migration to true\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let repairTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n // Get relevant elements\r\n let type = element.getAttribute(typeSelector);\r\n let mig = document.getElementById('migrationForm-'+type).querySelector('[name=\"migrateable\"]');\r\n let inputType = document.getElementById('migrepairForm').querySelector('[name=\"type\"]');\r\n let inputMig = document.getElementById('migrepairForm').querySelector('[name=\"migrateable\"]');\r\n\r\n // Fill input values\r\n inputType.value = type;\r\n inputMig.value = mig.value;\r\n\r\n // Show modal\r\n let bsmodal = new bootstrap.Modal(document.getElementById('repair-modal-box'), {keyboard: false});\r\n bsmodal.show();\r\n}\r\n\r\n/**\r\n * Perform an ajax request in json format\r\n * \r\n * @param {String} formId Id of the form element\r\n * @param {String} task Name of the task\r\n * \r\n * @returns {Object} Result object\r\n * {success: true, status: 200, message: '', messages: {}, data: { { {success, data, continue, error, debug, warning} }}\r\n */\r\nlet ajax = async function(formId, task) {\r\n\r\n // Catch form and data\r\n let formData = new FormData(document.getElementById(formId));\r\n formData.append('format', 'json');\r\n\r\n if(task == 'migration.start') {\r\n formData.append('id', getNextMigrationID(formId));\r\n }\r\n\r\n // Set request parameters\r\n let parameters = {\r\n method: 'POST',\r\n mode: 'same-origin',\r\n cache: 'default',\r\n redirect: 'follow',\r\n referrerPolicy: 'no-referrer-when-downgrade',\r\n body: formData,\r\n };\r\n\r\n // Set the url\r\n let url = document.getElementById(formId).getAttribute('action');\r\n\r\n // Perform the fetch request\r\n let response = await fetch(url, parameters);\r\n\r\n // Resolve promise as text string\r\n let txt = await response.text();\r\n let res = null;\r\n\r\n if (!response.ok) {\r\n // Catch network error\r\n return {success: false, status: response.status, message: response.message, messages: {}, data: {error: txt, data:null}};\r\n }\r\n\r\n if(txt.startsWith('{\"success\"')) {\r\n // Response is of type json --> everything fine\r\n res = JSON.parse(txt);\r\n res.status = response.status;\r\n res.data = JSON.parse(res.data);\r\n } else if (txt.includes('Fatal error')) {\r\n // PHP fatal error occurred\r\n res = {success: false, status: response.status, message: response.statusText, messages: {}, data: {error: txt, data:null}};\r\n } else {\r\n // Response is not of type json --> probably some php warnings/notices\r\n let split = txt.split('\\n{\"');\r\n let temp = JSON.parse('{\"'+split[1]);\r\n let data = JSON.parse(temp.data);\r\n res = {success: true, status: response.status, message: split[0], messages: temp.messages, data: data};\r\n }\r\n\r\n // Make sure res.data.data.queue is of type array\r\n if(typeof res.data.data != \"undefined\" && res.data.data != null && 'queue' in res.data.data) {\r\n if(res.data.data.queue.constructor !== Array) {\r\n res.data.data.queue = Object.values(res.data.data.queue);\r\n }\r\n }\r\n\r\n return res;\r\n}\r\n\r\n/**\r\n * Perform a migration task\r\n * @param {String} formId Id of the form element\r\n * \r\n * @returns {String} Id of the database record to be migrated\r\n */\r\nlet getNextMigrationID = function(formId) {\r\n let type = formId.replace(formIdTmpl + '-', '');\r\n let form = document.getElementById(formId);\r\n\r\n let migrateable = atob(form.querySelector('[name=\"migrateable\"]').value);\r\n migrateable = JSON.parse(migrateable);\r\n\r\n // Overwrite migrateable in list\r\n migrateablesList[type] = migrateable;\r\n\r\n // Loop through queue\r\n for (let id of migrateable.queue) {\r\n if (!(id in migrateable.successful) && !(id in migrateable.failed)) {\r\n migrateablesList[type]['currentID'] = id;\r\n break;\r\n }\r\n }\r\n\r\n return migrateablesList[type]['currentID'];\r\n}\r\n\r\n/**\r\n * Handle migration response\r\n * \r\n * @param {Object} response The response object in the form of\r\n * {success: true, status: 200, message: '', messages: {}, data: { {success, data, continue, error, debug, warning} }}\r\n * \r\n * @returns void\r\n */\r\nlet responseHandler = function(type, response) {\r\n if(response.success == false) {\r\n // Ajax request failed\r\n addLog(response.message, type, 'error');\r\n addLog(response.messages, type, 'error');\r\n\r\n // Try again...\r\n }\r\n else {\r\n // Ajax request successful\r\n if(!response.data.success)\r\n {\r\n // Migration failed\r\n addLog('[Migrator.js] Migration of '+type+' with id = '+migrateablesList[type]['currentID']+' failed.', type, 'error');\r\n logMessages(type, response.data);\r\n\r\n // Stop autimatic continuation if requested from backend\r\n if(!response.data.continue || response.data.continue == null || response.data.continue == false) {\r\n console.log('continueState; autimatic continuation requested from backend');\r\n continueState = false;\r\n }\r\n\r\n // Update migrateables\r\n updateMigrateables(type, response.data);\r\n }\r\n else\r\n {\r\n // Save record successful\r\n logMessages(type, response.data);\r\n addLog('[Migrator.js] Migration of '+type+' with id = '+migrateablesList[type]['currentID']+' successful.', type, 'success');\r\n\r\n // Stop autimatic continuation if requested from backend\r\n if(!response.data.continue || response.data.continue == null || response.data.continue == false) {\r\n console.log('continueState; autimatic continuation requested from backend');\r\n continueState = false;\r\n }\r\n\r\n // Update migrateables\r\n updateMigrateables(type, response.data);\r\n\r\n // Reset tryCounter\r\n tryCounter = 0;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Add a message to the logging output and the console\r\n * \r\n * @param {Mixed} msg One or multiple messages to be added to the log\r\n * @param {String} type The type defining the logging output to use\r\n * @param {String} msgType The type of message (available: error, warning, success, info)\r\n * @param {Boolean} console True to add the message also to the console\r\n * @param {Boolean} newLine True to add the message on a new line\r\n * @param {Integer} marginTop Number of how much margin you want on the top of the message\r\n * \r\n * @returns void\r\n */\r\nlet addLog = function(msg, type, msgType, console=false, newLine=true, marginTop=0) {\r\n if(!Boolean(msg) || msg == null || msg == '') {\r\n // Message is empty. Do nothing\r\n return;\r\n } else if(typeof msg === 'string') {\r\n // Your message is a simple string\r\n let tmp_msg = '';\r\n\r\n // Test if your string a json string\r\n try {\r\n tmp_msg = JSON.parse(msg);\r\n } catch (e) {\r\n }\r\n\r\n // Convert string to array\r\n if(tmp_msg !== '') {\r\n msg = Object.values(tmp_msg);\r\n } else {\r\n msg = [msg];\r\n }\r\n } else if(typeof msg === 'object') {\r\n // Your message is an object. Convert to array\r\n msg = Object.values(msg);\r\n }\r\n\r\n // Get logging output element\r\n let logOutput = document.getElementById('logOutput-'+type);\r\n\r\n // Loop through all messages\r\n msg.forEach((message, i) => {\r\n\r\n // Print in console\r\n if(console) {\r\n console.log(message);\r\n }\r\n\r\n // Create element\r\n let line = null;\r\n if(newLine) {\r\n line = document.createElement('p');\r\n } else {\r\n line = document.createElement('span');\r\n }\r\n\r\n // Top margin to element\r\n marginTop = parseInt(marginTop);\r\n if(marginTop > 0) {\r\n line.classList.add('mt-'+String(marginTop));\r\n }\r\n\r\n // Add text color\r\n line.classList.add('color-'+msgType);\r\n \r\n // Add message to element\r\n let msgType_txt = msgType.toLocaleUpperCase();\r\n line.textContent = '['+Joomla.JText._(msgType_txt)+'] '+String(message);\r\n\r\n // Print into logging output\r\n logOutput.appendChild(line);\r\n });\r\n}\r\n\r\n/**\r\n * Clear the logging output\r\n *\r\n * @param {String} type The type defining the logging output to clear\r\n * \r\n * @returns void\r\n */\r\nlet clearLog = function(type) {\r\n // Get logging output element\r\n let logOutput = document.getElementById('logOutput-'+type);\r\n\r\n // clear\r\n logOutput.innerHTML = '';\r\n}\r\n\r\n/**\r\n * Output all available messages from the result object\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {Object} res The result object in the form of\r\n * {success: bool, data: mixed, continue: bool, error: string|array, debug: string|array, warning: string|array}\r\n * \r\n * @returns void\r\n */\r\nlet logMessages = function(type, res) {\r\n // Available message types: error, debug, warning\r\n let available = ['error', 'debug', 'warning'];\r\n let msgTypes = {'error': 'error', 'debug': 'info', 'warning': 'warning'};\r\n\r\n available.forEach((value, index) => {\r\n if(!res[value] || !Boolean(res.data) || res.data == null) {\r\n return;\r\n }\r\n\r\n addLog(res[value], type, msgTypes[value]);\r\n });\r\n}\r\n\r\n/**\r\n * Update migrateable input field, progress bar and badges\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {Object} res The result object in the form of\r\n * {success: bool, data: mixed, continue: bool, error: string|array, debug: string|array, warning: string|array}\r\n * \r\n * @returns void\r\n */\r\nlet updateMigrateables = function(type, res) {\r\n let formId = formIdTmpl + '-' + type;\r\n let form = document.getElementById(formId);\r\n\r\n if(!res.success && (!Boolean(res.data) || res.data == null || res.data == '')) {\r\n // Migration failed, but no data available in result\r\n\r\n // Create result data based on input field\r\n let migrateable = atob(form.querySelector('[name=\"migrateable\"]').value);\r\n res.data = JSON.parse(migrateable);\r\n\r\n // See: Joomgallery\\Component\\Joomgallery\\Administrator\\Model\\MigrationModel::migrate\r\n // Remove migrated primary key from queue\r\n res.data.queue = res.data.queue.filter(function(e) { return e !== migrateablesList[type]['currentID'] })\r\n\r\n // Add migrated primary key to failed object\r\n res.data.failed[migrateablesList[type]['currentID']] = res.message;\r\n }\r\n\r\n if(!Boolean(res.data.progress) || res.data.progress == null || res.data.progress == '') {\r\n // Update progress if not delivered with result object\r\n let total = res.data.queue.lenght + Object.keys(res.data.successful).length + Object.keys(res.data.failed).length;\r\n let finished = Object.keys(res.data.successful).length + Object.keys(res.data.failed).length;\r\n res.data.progress = Math.round((100 / total) * (finished));\r\n }\r\n\r\n // Get badges\r\n let queueBadge = document.getElementById('badgeQueue-'+type);\r\n let resBadge = document.getElementById('badgeSuccessful-'+type);\r\n if(!res.success) {\r\n resBadge = document.getElementById('badgeFailed-'+type);\r\n }\r\n\r\n // Update migrateable input field\r\n let field = form.querySelector('[name=\"migrateable\"]');\r\n field.value = btoa(JSON.stringify(res.data));\r\n\r\n // Update badges\r\n queueBadge.innerHTML = parseInt(queueBadge.innerHTML) - 1;\r\n resBadge.innerHTML = parseInt(resBadge.innerHTML) + 1;\r\n\r\n // Update progress bar\r\n let bar = document.getElementById('progress-'+type);\r\n bar.setAttribute('aria-valuenow', res.data.progress);\r\n bar.style.width = res.data.progress + '%';\r\n bar.innerText = res.data.progress + '%';\r\n}\r\n\r\n/**\r\n * Update GUI to start migration\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {DOM Element} button The button beeing pressed to start the task\r\n * \r\n * @returns void\r\n */\r\nlet startTask = function(type, button) {\r\n let bar = document.getElementById('progress-'+type);\r\n let startBtn = button;\r\n let stopBtn = document.getElementById('stopBtn-'+type);\r\n\r\n // Update progress bar\r\n bar.classList.remove('progress-bar-striped');\r\n bar.classList.remove('progress-bar-animated');\r\n \r\n // Disable start button\r\n startBtn.classList.add('disabled');\r\n startBtn.setAttribute('disabled', 'true');\r\n\r\n // Enable stop button\r\n stopBtn.classList.remove('disabled');\r\n stopBtn.removeAttribute('disabled');\r\n\r\n // Reinitialize variables\r\n tryCounter = 0;\r\n continueState = true;\r\n forceStop = false;\r\n}\r\n\r\n/**\r\n * Update GUI to end migration\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {DOM Element} button The button beeing pressed to start the task\r\n * \r\n * @returns void\r\n */\r\nlet finishTask = function(type, button) {\r\n let bar = document.getElementById('progress-'+type);\r\n let startBtn = button;\r\n let stopBtn = document.getElementById('stopBtn-'+type);\r\n\r\n // Update progress bar\r\n bar.classList.remove('progress-bar-striped');\r\n bar.classList.remove('progress-bar-animated');\r\n \r\n // Enable start button\r\n if(!migrateablesList[type]['complete']) {\r\n // Only enable start button if migration is not finished\r\n startBtn.classList.remove('disabled');\r\n startBtn.removeAttribute('disabled');\r\n }\r\n\r\n // Disable stop button\r\n stopBtn.classList.add('disabled');\r\n stopBtn.setAttribute('disabled', 'true');\r\n\r\n // Enable step 4 button\r\n if(migrateablesList[type]['complete']) {\r\n updateStep4Btn();\r\n }\r\n}\r\n\r\n/**\r\n * Update button to go to step 4\r\n * \r\n * @returns void\r\n */\r\nlet updateStep4Btn = function() {\r\n let types_inputs = document.getElementsByName('type');\r\n let types = {};\r\n\r\n // Add all available migrateables to types object\r\n types_inputs.forEach((type) => {\r\n if(Boolean(type.value)) {\r\n types[type.value] = false;\r\n }\r\n });\r\n\r\n // Check if all migrateables are available and completed\r\n let tot_complete = true;\r\n Object.keys(types).forEach(type => { \r\n if(Boolean(migrateablesList[type])) {\r\n if(!migrateablesList[type]['completed'])\r\n {\r\n // Migrateable not yet completed\r\n tot_complete = false;\r\n }\r\n }\r\n else\r\n {\r\n // Migrateable does not yet exist. Thus not completed\r\n tot_complete = false;\r\n }\r\n });\r\n\r\n if(tot_complete) {\r\n // Enable step 4 button\r\n document.getElementById(step4Btn).classList.remove('disabled');\r\n document.getElementById(step4Btn).removeAttribute('disabled');\r\n }\r\n}"],"names":[],"sourceRoot":""} \ No newline at end of file +{"version":3,"file":"migrator.js","mappings":";;;UAAA;UACA;;;;;WCDA;WACA;WACA;WACA;WACA,yCAAyC,wCAAwC;WACjF;WACA;WACA;;;;;WCPA;;;;;WCAA;WACA;WACA;WACA,uDAAuD,iBAAiB;WACxE;WACA,gDAAgD,aAAa;WAC7D;;;;;;;;;;;;;;ACNA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,SAAS;AAClB;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kCAAkC,oGAAoG;AACtI;AACA,gCAAgC;AAChC;AACA;AACA;AACA;AACA;AACA;AACA;AACA,QAAQ;AACR;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kFAAkF,gBAAgB;AAClG;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB;AACA,aAAa,UAAU;AACvB,aAAa,qDAAqD,aAAa;AAC/E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,gFAAgF,SAAS;AACrG;AACA;AACA,sBAAsB;AACtB;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA,WAAW,mFAAmF,SAAS;AACvG,IAAI;AACJ;AACA,8BAA8B;AAC9B,6BAA6B;AAC7B;AACA,WAAW;AACX;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB;AACA,aAAa,UAAU;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,qDAAqD,WAAW;AAC7E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oCAAoC;AACpC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oCAAoC;AACpC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB,YAAY,UAAU;AACtB,cAAc;AACd;AACA;AACA;AACA;AACA;AACA;AACA,kBAAkB;AAClB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB,YAAY,UAAU;AACtB,cAAc;AACd;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,yDAAyD,kDAAkD;AAC3G;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA,C","sources":["webpack://Migrator/webpack/bootstrap","webpack://Migrator/webpack/runtime/define property getters","webpack://Migrator/webpack/runtime/hasOwnProperty shorthand","webpack://Migrator/webpack/runtime/make namespace object","webpack://Migrator/./src/index.js"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","// Selectors used by this script\r\nlet typeSelector = 'data-type';\r\nlet formIdTmpl = 'migrationForm';\r\nlet buttonTmpl = 'migrationBtn';\r\nlet step4Btn = 'step4Btn';\r\nlet tryLimit = 3;\r\n\r\n/**\r\n * Storage for migrateables\r\n * @var {Object} migrateablesList\r\n */\r\nvar migrateablesList = {};\r\n\r\n/**\r\n * Counter of how many times the same migration was tried to perfrom\r\n * @var {Integer} tryCounter\r\n */\r\nvar tryCounter = 0;\r\n\r\n/**\r\n * State. As long as this state is set to true, the migration will be\r\n * continued automatically regarding the pending queue in the migrateablesList.\r\n * @var {Boolean} continueState\r\n */\r\nvar continueState = true;\r\n\r\n/**\r\n * State. Set this state to true to stop automatic execution as soon as the next ajax respond comes back.\r\n * @var {Boolean} forceStop\r\n */\r\nvar forceStop = false;\r\n\r\n/**\r\n * Submit the migration task by pressing the button\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let submitTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n let type = element.getAttribute(typeSelector);\r\n let formId = formIdTmpl + '-' + type;\r\n let task = element.parentNode.querySelector('[name=\"task\"]').value;\r\n\r\n startTask(type, element);\r\n\r\n tryCounter++;\r\n\r\n ajax(formId, task)\r\n .then(res => {\r\n // Handle the successful result here\r\n responseHandler(type, res);\r\n\r\n console.log('forceStop: ' + forceStop);\r\n console.log('continueState: ' + continueState);\r\n\r\n if(tryCounter >= tryLimit) {\r\n // We reached the limit of tries --> looks like we have a network problem\r\n updateMigrateables(type, {'success': false, 'message': Joomla.JText._('COM_JOOMGALLERY_ERROR_NETWORK_PROBLEM'), 'data': false});\r\n // Stop automatic execution and update GUI\r\n console.log('forceStop; We reached the limit of tries');\r\n forceStop = true;\r\n }\r\n \r\n if(continueState && !forceStop) {\r\n // Kick off the next task\r\n console.log('Kick off the next task');\r\n submitTask(event, element);\r\n } else {\r\n // Stop automatic task execution and update GUI\r\n console.log('Stop automatic task execution and update GUI');\r\n finishTask(type, element);\r\n }\r\n })\r\n .catch(error => {\r\n // Handle any errors here\r\n console.error(error);\r\n addLog(error, type, 'error');\r\n });\r\n};\r\n\r\n/**\r\n * Stop the migration task by pressing the button\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let stopTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n let type = element.getAttribute(typeSelector);\r\n let bar = document.getElementById('progress-'+type);\r\n let startBtn = document.getElementById('migrationBtn-'+type);\r\n let stopBtn = element;\r\n\r\n // Force automatic execution to stop\r\n forceStop = true;\r\n\r\n // Update progress bar\r\n bar.classList.remove('progress-bar-striped');\r\n bar.classList.remove('progress-bar-animated');\r\n \r\n // Enable start button\r\n startBtn.classList.remove('disabled');\r\n startBtn.removeAttribute('disabled');\r\n\r\n // Disable stop button\r\n stopBtn.classList.add('disabled');\r\n stopBtn.setAttribute('disabled', 'true');\r\n}\r\n\r\n/**\r\n * Manually set one record migration to true\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let repairTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n // Get relevant elements\r\n let type = element.getAttribute(typeSelector);\r\n let mig = document.getElementById('migrationForm-'+type).querySelector('[name=\"migrateable\"]');\r\n let inputType = document.getElementById('migrepairForm').querySelector('[name=\"type\"]');\r\n let inputMig = document.getElementById('migrepairForm').querySelector('[name=\"migrateable\"]');\r\n\r\n // Fill input values\r\n inputType.value = type;\r\n inputMig.value = mig.value;\r\n\r\n // Show modal\r\n let bsmodal = new bootstrap.Modal(document.getElementById('repair-modal-box'), {keyboard: false});\r\n bsmodal.show();\r\n}\r\n\r\n/**\r\n * Perform an ajax request in json format\r\n * \r\n * @param {String} formId Id of the form element\r\n * @param {String} task Name of the task\r\n * \r\n * @returns {Object} Result object\r\n * {success: true, status: 200, message: '', messages: {}, data: { { {success, data, continue, error, debug, warning} }}\r\n */\r\nlet ajax = async function(formId, task) {\r\n\r\n // Catch form and data\r\n let formData = new FormData(document.getElementById(formId));\r\n formData.append('format', 'json');\r\n\r\n if(task == 'migration.start') {\r\n formData.append('id', getNextMigrationID(formId));\r\n }\r\n\r\n // Set request parameters\r\n let parameters = {\r\n method: 'POST',\r\n mode: 'same-origin',\r\n cache: 'default',\r\n redirect: 'follow',\r\n referrerPolicy: 'no-referrer-when-downgrade',\r\n body: formData,\r\n };\r\n\r\n // Set the url\r\n let url = document.getElementById(formId).getAttribute('action');\r\n\r\n // Perform the fetch request\r\n let response = await fetch(url, parameters);\r\n\r\n // Resolve promise as text string\r\n let txt = await response.text();\r\n let res = null;\r\n\r\n if (!response.ok) {\r\n // Catch network error\r\n return {success: false, status: response.status, message: response.message, messages: {}, data: {error: txt, data:null}};\r\n }\r\n\r\n if(txt.startsWith('{\"success\"')) {\r\n // Response is of type json --> everything fine\r\n res = JSON.parse(txt);\r\n res.status = response.status;\r\n res.data = JSON.parse(res.data);\r\n } else if (txt.includes('Fatal error')) {\r\n // PHP fatal error occurred\r\n res = {success: false, status: response.status, message: response.statusText, messages: {}, data: {error: txt, data:null}};\r\n } else {\r\n // Response is not of type json --> probably some php warnings/notices\r\n let split = txt.split('\\n{\"');\r\n let temp = JSON.parse('{\"'+split[1]);\r\n let data = JSON.parse(temp.data);\r\n res = {success: true, status: response.status, message: split[0], messages: temp.messages, data: data};\r\n }\r\n\r\n // Make sure res.data.data.queue is of type array\r\n if(typeof res.data.data != \"undefined\" && res.data.data != null && 'queue' in res.data.data) {\r\n if(res.data.data.queue.constructor !== Array) {\r\n res.data.data.queue = Object.values(res.data.data.queue);\r\n }\r\n }\r\n\r\n return res;\r\n}\r\n\r\n/**\r\n * Perform a migration task\r\n * @param {String} formId Id of the form element\r\n * \r\n * @returns {String} Id of the database record to be migrated\r\n */\r\nlet getNextMigrationID = function(formId) {\r\n let type = formId.replace(formIdTmpl + '-', '');\r\n let form = document.getElementById(formId);\r\n\r\n let migrateable = atob(form.querySelector('[name=\"migrateable\"]').value);\r\n migrateable = JSON.parse(migrateable);\r\n\r\n // Overwrite migrateable in list\r\n migrateablesList[type] = migrateable;\r\n\r\n // Loop through queue\r\n for (let id of migrateable.queue) {\r\n if (!(id in migrateable.successful) && !(id in migrateable.failed)) {\r\n migrateablesList[type]['currentID'] = id;\r\n break;\r\n }\r\n }\r\n\r\n return migrateablesList[type]['currentID'];\r\n}\r\n\r\n/**\r\n * Handle migration response\r\n * \r\n * @param {Object} response The response object in the form of\r\n * {success: true, status: 200, message: '', messages: {}, data: { {success, data, continue, error, debug, warning} }}\r\n * \r\n * @returns void\r\n */\r\nlet responseHandler = function(type, response) {\r\n if(response.success == false) {\r\n // Ajax request failed\r\n addLog(response.message, type, 'error');\r\n addLog(response.messages, type, 'error');\r\n\r\n // Try again...\r\n }\r\n else {\r\n // Ajax request successful\r\n if(!response.data.success)\r\n {\r\n // Migration failed\r\n addLog('[Migrator.js] Migration of '+type+' with id = '+migrateablesList[type]['currentID']+' failed.', type, 'error');\r\n logMessages(type, response.data);\r\n\r\n // Stop autimatic continuation if requested from backend\r\n if(!response.data.continue || response.data.continue == null || response.data.continue == false) {\r\n console.log('continueState; autimatic continuation requested from backend');\r\n continueState = false;\r\n }\r\n\r\n // Update migrateables\r\n updateMigrateables(type, response.data);\r\n }\r\n else\r\n {\r\n // Save record successful\r\n logMessages(type, response.data);\r\n addLog('[Migrator.js] Migration of '+type+' with id = '+migrateablesList[type]['currentID']+' successful.', type, 'success');\r\n\r\n // Stop autimatic continuation if requested from backend\r\n if(!response.data.continue || response.data.continue == null || response.data.continue == false) {\r\n console.log('continueState; autimatic continuation requested from backend');\r\n continueState = false;\r\n }\r\n\r\n // Update migrateables\r\n updateMigrateables(type, response.data);\r\n\r\n // Reset tryCounter\r\n tryCounter = 0;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Add a message to the logging output and the console\r\n * \r\n * @param {Mixed} msg One or multiple messages to be added to the log\r\n * @param {String} type The type defining the logging output to use\r\n * @param {String} msgType The type of message (available: error, warning, success, info)\r\n * @param {Boolean} console True to add the message also to the console\r\n * @param {Boolean} newLine True to add the message on a new line\r\n * @param {Integer} marginTop Number of how much margin you want on the top of the message\r\n * \r\n * @returns void\r\n */\r\nlet addLog = function(msg, type, msgType, console=false, newLine=true, marginTop=0) {\r\n if(!Boolean(msg) || msg == null || msg == '') {\r\n // Message is empty. Do nothing\r\n return;\r\n } else if(typeof msg === 'string') {\r\n // Your message is a simple string\r\n let tmp_msg = '';\r\n\r\n // Test if your string a json string\r\n try {\r\n tmp_msg = JSON.parse(msg);\r\n } catch (e) {\r\n }\r\n\r\n // Convert string to array\r\n if(tmp_msg !== '') {\r\n msg = Object.values(tmp_msg);\r\n } else {\r\n msg = [msg];\r\n }\r\n } else if(typeof msg === 'object') {\r\n // Your message is an object. Convert to array\r\n msg = Object.values(msg);\r\n }\r\n\r\n // Get logging output element\r\n let logOutput = document.getElementById('logOutput-'+type);\r\n\r\n // Loop through all messages\r\n msg.forEach((message, i) => {\r\n\r\n // Print in console\r\n if(console) {\r\n console.log(message);\r\n }\r\n\r\n // Create element\r\n let line = null;\r\n if(newLine) {\r\n line = document.createElement('p');\r\n } else {\r\n line = document.createElement('span');\r\n }\r\n\r\n // Top margin to element\r\n marginTop = parseInt(marginTop);\r\n if(marginTop > 0) {\r\n line.classList.add('mt-'+String(marginTop));\r\n }\r\n\r\n // Add text color\r\n line.classList.add('color-'+msgType);\r\n \r\n // Add message to element\r\n let msgType_txt = msgType.toLocaleUpperCase();\r\n line.textContent = '['+Joomla.JText._(msgType_txt)+'] '+String(message);\r\n\r\n // Print into logging output\r\n logOutput.appendChild(line);\r\n });\r\n}\r\n\r\n/**\r\n * Clear the logging output\r\n *\r\n * @param {String} type The type defining the logging output to clear\r\n * \r\n * @returns void\r\n */\r\nlet clearLog = function(type) {\r\n // Get logging output element\r\n let logOutput = document.getElementById('logOutput-'+type);\r\n\r\n // clear\r\n logOutput.innerHTML = '';\r\n}\r\n\r\n/**\r\n * Output all available messages from the result object\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {Object} res The result object in the form of\r\n * {success: bool, data: mixed, continue: bool, error: string|array, debug: string|array, warning: string|array}\r\n * \r\n * @returns void\r\n */\r\nlet logMessages = function(type, res) {\r\n // Available message types: error, debug, warning\r\n let available = ['error', 'debug', 'warning'];\r\n let msgTypes = {'error': 'error', 'debug': 'info', 'warning': 'warning'};\r\n\r\n available.forEach((value, index) => {\r\n if(!res[value] || !Boolean(res.data) || res.data == null) {\r\n return;\r\n }\r\n\r\n addLog(res[value], type, msgTypes[value]);\r\n });\r\n}\r\n\r\n/**\r\n * Update migrateable input field, progress bar and badges\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {Object} res The result object in the form of\r\n * {success: bool, data: mixed, continue: bool, error: string|array, debug: string|array, warning: string|array}\r\n * \r\n * @returns void\r\n */\r\nlet updateMigrateables = function(type, res) {\r\n let formId = formIdTmpl + '-' + type;\r\n let form = document.getElementById(formId);\r\n\r\n if(!res.success && (!Boolean(res.data) || res.data == null || res.data == '')) {\r\n // Migration failed, but no data available in result\r\n\r\n // Create result data based on input field\r\n let migrateable = atob(form.querySelector('[name=\"migrateable\"]').value);\r\n res.data = JSON.parse(migrateable);\r\n\r\n // See: Joomgallery\\Component\\Joomgallery\\Administrator\\Model\\MigrationModel::migrate\r\n // Remove migrated primary key from queue\r\n res.data.queue = res.data.queue.filter(function(e) { return e !== migrateablesList[type]['currentID'] })\r\n\r\n // Add migrated primary key to failed object\r\n res.data.failed[migrateablesList[type]['currentID']] = res.message;\r\n }\r\n\r\n if(!Boolean(res.data.progress) || res.data.progress == null || res.data.progress == '') {\r\n // Update progress if not delivered with result object\r\n let total = res.data.queue.lenght + Object.keys(res.data.successful).length + Object.keys(res.data.failed).length;\r\n let finished = Object.keys(res.data.successful).length + Object.keys(res.data.failed).length;\r\n res.data.progress = Math.round((100 / total) * (finished));\r\n }\r\n\r\n // Get badges\r\n let queueBadge = document.getElementById('badgeQueue-'+type);\r\n let resBadge = document.getElementById('badgeSuccessful-'+type);\r\n if(!res.success) {\r\n resBadge = document.getElementById('badgeFailed-'+type);\r\n }\r\n\r\n // Update migrateable input field\r\n let field = form.querySelector('[name=\"migrateable\"]');\r\n field.value = btoa(JSON.stringify(res.data));\r\n\r\n // Update badges\r\n queueBadge.innerHTML = parseInt(queueBadge.innerHTML) - 1;\r\n resBadge.innerHTML = parseInt(resBadge.innerHTML) + 1;\r\n\r\n // Update progress bar\r\n let bar = document.getElementById('progress-'+type);\r\n bar.setAttribute('aria-valuenow', res.data.progress);\r\n bar.style.width = res.data.progress + '%';\r\n bar.innerText = res.data.progress + '%';\r\n}\r\n\r\n/**\r\n * Update GUI to start migration\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {DOM Element} button The button beeing pressed to start the task\r\n * \r\n * @returns void\r\n */\r\nlet startTask = function(type, button) {\r\n let bar = document.getElementById('progress-'+type);\r\n let startBtn = button;\r\n let stopBtn = document.getElementById('stopBtn-'+type);\r\n\r\n // Update progress bar\r\n bar.classList.remove('progress-bar-striped');\r\n bar.classList.remove('progress-bar-animated');\r\n \r\n // Disable start button\r\n startBtn.classList.add('disabled');\r\n startBtn.setAttribute('disabled', 'true');\r\n\r\n // Enable stop button\r\n stopBtn.classList.remove('disabled');\r\n stopBtn.removeAttribute('disabled');\r\n\r\n // Reinitialize variables\r\n tryCounter = 0;\r\n continueState = true;\r\n forceStop = false;\r\n}\r\n\r\n/**\r\n * Update GUI to end migration\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {DOM Element} button The button beeing pressed to start the task\r\n * \r\n * @returns void\r\n */\r\nlet finishTask = function(type, button) {\r\n let bar = document.getElementById('progress-'+type);\r\n let startBtn = button;\r\n let stopBtn = document.getElementById('stopBtn-'+type);\r\n\r\n // Update progress bar\r\n bar.classList.remove('progress-bar-striped');\r\n bar.classList.remove('progress-bar-animated');\r\n \r\n // Enable start button\r\n if(!migrateablesList[type]['complete']) {\r\n // Only enable start button if migration is not finished\r\n startBtn.classList.remove('disabled');\r\n startBtn.removeAttribute('disabled');\r\n }\r\n\r\n // Disable stop button\r\n stopBtn.classList.add('disabled');\r\n stopBtn.setAttribute('disabled', 'true');\r\n\r\n // If migration is completed\r\n if(migrateablesList[type]['complete']) {\r\n // Update next start button\r\n enableNextBtn(type, button);\r\n // Update step 4 button\r\n updateStep4Btn();\r\n }\r\n}\r\n\r\n/**\r\n * Enable start button of next migration content type\r\n * \r\n * @param {String} type The type defining the content type to be updated\r\n * @param {DOM Element} button The current start button\r\n * \r\n * @returns void\r\n */\r\nlet enableNextBtn = function(type, button) {\r\n let types_inputs = document.getElementsByName('type');\r\n let next_type = '';\r\n\r\n // Find next migration content type\r\n let this_type = false;\r\n types_inputs.forEach((type_input) => {\r\n if(this_type) {\r\n next_type = type_input.value;\r\n return;\r\n }\r\n if(Boolean(type_input.value) && type_input.value == type) {\r\n this_type = true;\r\n }\r\n });\r\n\r\n // Get next button\r\n let nextBtn = document.getElementById(buttonTmpl + '-' + next_type);\r\n\r\n // Enable button\r\n nextBtn.classList.remove('disabled');\r\n nextBtn.removeAttribute('disabled');\r\n}\r\n\r\n/**\r\n * Update button to go to step 4\r\n * \r\n * @returns void\r\n */\r\nlet updateStep4Btn = function() {\r\n let types_inputs = document.getElementsByName('type');\r\n let types = {};\r\n\r\n // Add all available migrateables to types object\r\n types_inputs.forEach((type) => {\r\n if(Boolean(type.value)) {\r\n types[type.value] = false;\r\n }\r\n });\r\n\r\n // Check if all migrateables are available and completed\r\n let tot_complete = true;\r\n Object.keys(types).forEach(type => { \r\n if(Boolean(migrateablesList[type])) {\r\n if(!migrateablesList[type]['completed'])\r\n {\r\n // Migrateable not yet completed\r\n tot_complete = false;\r\n }\r\n }\r\n else\r\n {\r\n // Migrateable does not yet exist. Thus not completed\r\n tot_complete = false;\r\n }\r\n });\r\n\r\n if(tot_complete) {\r\n // Enable step 4 button\r\n document.getElementById(step4Btn).classList.remove('disabled');\r\n document.getElementById(step4Btn).removeAttribute('disabled');\r\n }\r\n}"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/media/com_joomgallery/js/migrator/src/index.js b/media/com_joomgallery/js/migrator/src/index.js index cce62ce7..3502a10c 100644 --- a/media/com_joomgallery/js/migrator/src/index.js +++ b/media/com_joomgallery/js/migrator/src/index.js @@ -1,7 +1,7 @@ // Selectors used by this script let typeSelector = 'data-type'; let formIdTmpl = 'migrationForm'; -let button = 'migrationBtn'; +let buttonTmpl = 'migrationBtn'; let step4Btn = 'step4Btn'; let tryLimit = 3; @@ -488,7 +488,7 @@ let startTask = function(type, button) { /** * Update GUI to end migration * - * @param {String} type The type defining the content type to be updated + * @param {String} type The type defining the content type to be updated * @param {DOM Element} button The button beeing pressed to start the task * * @returns void @@ -513,12 +513,47 @@ let finishTask = function(type, button) { stopBtn.classList.add('disabled'); stopBtn.setAttribute('disabled', 'true'); - // Enable step 4 button + // If migration is completed if(migrateablesList[type]['complete']) { + // Update next start button + enableNextBtn(type, button); + // Update step 4 button updateStep4Btn(); } } +/** + * Enable start button of next migration content type + * + * @param {String} type The type defining the content type to be updated + * @param {DOM Element} button The current start button + * + * @returns void + */ +let enableNextBtn = function(type, button) { + let types_inputs = document.getElementsByName('type'); + let next_type = ''; + + // Find next migration content type + let this_type = false; + types_inputs.forEach((type_input) => { + if(this_type) { + next_type = type_input.value; + return; + } + if(Boolean(type_input.value) && type_input.value == type) { + this_type = true; + } + }); + + // Get next button + let nextBtn = document.getElementById(buttonTmpl + '-' + next_type); + + // Enable button + nextBtn.classList.remove('disabled'); + nextBtn.removeAttribute('disabled'); +} + /** * Update button to go to step 4 * From 1614cced87eceaaf844f8bd089dfb2e4ed6e513b Mon Sep 17 00:00:00 2001 From: Elfangor <39154009+Elfangor93@users.noreply.github.com> Date: Tue, 2 Jan 2024 13:06:12 +0100 Subject: [PATCH 056/121] Add migration menu item --- joomgallery.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/joomgallery.xml b/joomgallery.xml index 8807e778..5dd9f802 100644 --- a/joomgallery.xml +++ b/joomgallery.xml @@ -53,6 +53,7 @@ JCATEGORIES COM_JOOMGALLERY_TAGS COM_JOOMGALLERY_CONFIG_SETS + COM_JOOMGALLERY_MIGRATION access.xml From af8f6585bbf67af018d6776832246edfaf3fafa0 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Tue, 2 Jan 2024 20:00:52 +0100 Subject: [PATCH 057/121] fix issue catpath on direct usage --- .../src/Model/MigrationModel.php | 58 ++++++++++++++----- .../src/Service/Migration/Migration.php | 2 +- .../Service/Migration/Scripts/Jg3ToJg4.php | 11 +++- 3 files changed, 55 insertions(+), 16 deletions(-) diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index f797db47..f2807f76 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -718,17 +718,17 @@ public function migrate(string $type, int $pk): object // Set old catid for image creation $new_catid = $record->catid; - $record->catid = $src_data['catid']; + $record->catid = $src_data['catid']; // Todo: catid only works for Jg3ToJg4 script - if($mig->params->get('image_usage') == 1) + if($mig->params->get('image_usage', 1) == 1) { // Recreate imagetypes based on given image $res = $this->createImages($record, $img_source[0]); } - elseif($mig->params->get('image_usage') == 2 || $mig->params->get('image_usage') == 3) + elseif($mig->params->get('image_usage', 1) == 2 || $mig->params->get('image_usage', 1) == 3) { $copy = false; - if($mig->params->get('image_usage') == 2) + if($mig->params->get('image_usage', 1) == 2) { $copy = true; } @@ -750,9 +750,18 @@ public function migrate(string $type, int $pk): object break; case 'category': - $res = $this->createFolder($record); - - $error_msg_end = 'CREATE_FOLDER'; + if($mig->name == 'Jg3ToJg4' && $mig->params->get('image_usage', 1) == 0) + { + // rename folder + $res = $this->renameFolder($record, $src_data['catpath'], $record->path); + $error_msg_end = 'RENAME_FOLDER'; + } + else + { + // create folder + $res = $this->createFolder($record); + $error_msg_end = 'CREATE_FOLDER'; + } default: $res = true; @@ -1071,7 +1080,7 @@ protected function insertRecord(string $type, array $data, bool $newID = true) } // Store the data. - if(!$table->store()) + if(!$table->store()) { $this->component->setError($table->getError()); @@ -1294,7 +1303,7 @@ protected function reuseImages(ImageTable $img, array $sources, bool $copy = fal } /** - * Creation of category folders based on one source file. + * Creation of category folders based on category object. * * @param CategoryTable $cat CategoryTable object, already stored * @@ -1304,10 +1313,33 @@ protected function reuseImages(ImageTable $img, array $sources, bool $copy = fal */ protected function createFolder(CategoryTable $cat): bool { - // Create file manager service - $this->component->createFileManager(); + // Create file manager service + $this->component->createFileManager(); + + // Create folders + return $this->component->getFileManager()->createCategory($cat->alias, $cat->parent_id); + } + + /** + * Renaming of category folders based on one source file. + * + * @param CategoryTable $cat CategoryTable object, already stored + * @param string $oldName Old foldername of the category + * @param string $newName New foldername of the category + * + * @return bool True on success, false otherwise + * + * @since 4.0.0 + */ + protected function renameFolder(CategoryTable $cat, string $oldName, string $newName): bool + { + // Create file manager service + $this->component->createFileManager(); + + // Reset old foldername + $cat->path = $oldName; - // Create folders - return $this->component->getFileManager()->createCategory($cat->alias, $cat->parent_id); + // Create folders + return $this->component->getFileManager()->renameCategory($cat, $newName); } } diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index 50350f7e..0d9cf35a 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -294,7 +294,7 @@ public function precheck(): array $this->checkDestTable($checks, 'destination'); // Check image mapping - if($this->params->get('image_usage', 0) > 0) + if($this->params->get('image_usage', 1) > 0) { $this->checkImageMapping($checks, 'destination'); } diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index ee8b784c..d6f2684a 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -211,6 +211,13 @@ public function convertData(string $type, array $data): array // Parameter dependet mapping fields $id = \boolval($this->params->get('source_ids', 0)) ? 'id' : false; $owner = \boolval($this->params->get('check_owner', 0)) ? 'created_by' : false; + $path = false; + + if($this->params->get('same_joomla', 1) == 1 && $this->params->get('image_usage', 0) == 0) + { + // Special case: Direct usage of images in the same joomla installation + $path = 'path'; + } // Configure mapping for each content type switch($type) @@ -218,7 +225,7 @@ public function convertData(string $type, array $data): array case 'category': // Apply mapping for category table $mapping = array( 'cid' => $id, 'asset_id' => false, 'name' => 'title', 'alias' => false, 'lft' => false, 'rgt' => false, 'level' => false, - 'owner' => $owner, 'img_position' => false, 'catpath' => 'path', 'params' => array('params', false, false), + 'owner' => $owner, 'img_position' => false, 'catpath' => $path, 'params' => array('params', false, false), 'allow_download' => array('params', 'jg_download', false), 'allow_comment' => array('params', 'jg_showcomment', false), 'allow_rating' => array('params', 'jg_showrating', false), 'allow_watermark' => array('params', 'jg_dynamic_watermark', false), 'allow_watermark_download' => array('params', 'jg_downloadwithwatermark', false) @@ -354,7 +361,7 @@ public function getImageSource(array $data): array $directories = $this->getSourceDirs(); $cat = $this->getData('category', $data['catid']); - switch($this->params->get('image_usage')) + switch($this->params->get('image_usage', 0)) { // Recreate images case 1: From 394ac3edd786f8e6aab1b4bc0aef425eeb498e6b Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Wed, 3 Jan 2024 12:00:11 +0100 Subject: [PATCH 058/121] rearrange migration service --- .../language/en-GB/com_joomgallery.ini | 1 + .../src/Model/MigrationModel.php | 265 +----------------- .../src/Service/Migration/Migration.php | 65 +++-- .../Service/Migration/MigrationInterface.php | 194 ++++++++----- .../Service/Migration/Scripts/Jg3ToJg4.php | 248 +++++++++++++++- 5 files changed, 440 insertions(+), 333 deletions(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index 3258c588..d1675010 100644 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -738,6 +738,7 @@ COM_JOOMGALLERY_SERVICE_MIGRATION_MAPPING_SRC_IMAGETYPE_NOT_EXIST="The source im COM_JOOMGALLERY_SERVICE_MIGRATION_MAPPING_IMAGETYPE_NOT_USED="The following destination imagetypes are not used in the mapping: '%s'. Please go back to step 1 and apply the mapping correctly." COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_RECORD_ID_MISSING="Starting a migration without source data ID not possible. Contact the developer of the migration script to solve this." COM_JOOMGALLERY_SERVICE_ERROR_MIGRATION_RESUME="Migration can not be resumed. There is a problem in fetching the required info." +COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_FETCH_DATA="Data from source could not be loaded. See log file for more destails." COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_CONVERT_DATA="Data from source could not be converted to new data structure. See log file for more destails." COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_INSERT_RECORD="Data could not be inserted into destination database. See log file for more destails." COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_CREATE_IMGTYPE="Error in creating imagetypes. See log file for more destails." diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index f2807f76..4ee150a3 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -18,16 +18,12 @@ use \Joomla\CMS\Form\Form; use \Joomla\CMS\Language\Text; use \Joomla\Registry\Registry; -use \Joomla\CMS\Filesystem\Path; -use \Joomla\CMS\Filesystem\File; use \Joomla\Utilities\ArrayHelper; use \Joomla\CMS\Filesystem\Folder; use \Joomla\CMS\MVC\Model\AdminModel; use \Joomla\CMS\Language\Multilanguage; -use \Joomgallery\Component\Joomgallery\Administrator\Table\MigrationTable; -use \Joomgallery\Component\Joomgallery\Administrator\Table\ImageTable; -use \Joomgallery\Component\Joomgallery\Administrator\Table\CategoryTable; use \Joomgallery\Component\Joomgallery\Administrator\Helper\JoomHelper; +use \Joomgallery\Component\Joomgallery\Administrator\Table\MigrationTable; /** * Migration model. @@ -677,7 +673,7 @@ public function migrate(string $type, int $pk): object $this->component->createMigration($this->app->getUserStateFromRequest(_JOOM_OPTION.'.migration.script', 'script', '', 'cmd')); $mig = $this->component->getMigration()->prepareMigration($type); - // Perform the migration of the element + // Perform the migration of the element if needed if($this->component->getMigration()->needsMigration($type, $pk)) { // Get record data from source @@ -696,7 +692,7 @@ public function migrate(string $type, int $pk): object } else { - // Create new record based on data array + // Create new record at destination based on converted data $sameIDs = \boolval($mig->params->get('source_ids', 0)); $record = $this->insertRecord($type, (array) $data, $sameIDs); @@ -714,54 +710,14 @@ public function migrate(string $type, int $pk): object switch($type) { case 'image': - $img_source = $this->component->getMigration()->getImageSource($src_data); - - // Set old catid for image creation - $new_catid = $record->catid; - $record->catid = $src_data['catid']; // Todo: catid only works for Jg3ToJg4 script - - if($mig->params->get('image_usage', 1) == 1) - { - // Recreate imagetypes based on given image - $res = $this->createImages($record, $img_source[0]); - } - elseif($mig->params->get('image_usage', 1) == 2 || $mig->params->get('image_usage', 1) == 3) - { - $copy = false; - if($mig->params->get('image_usage', 1) == 2) - { - $copy = true; - } - - // Copy/Move images from source based on mapping - $res = $this->reuseImages($record, $img_source, $copy); - } - else - { - // Direct usage of images - // Nothing to do - $res = true; - } - - // Reset new catid - $record->catid = $new_catid; - + $res = $this->component->getMigration()->migrateFiles($record, $src_data); $error_msg_end = 'CREATE_IMGTYPE'; break; case 'category': - if($mig->name == 'Jg3ToJg4' && $mig->params->get('image_usage', 1) == 0) - { - // rename folder - $res = $this->renameFolder($record, $src_data['catpath'], $record->path); - $error_msg_end = 'RENAME_FOLDER'; - } - else - { - // create folder - $res = $this->createFolder($record); - $error_msg_end = 'CREATE_FOLDER'; - } + $res = $this->component->getMigration()->migrateFolder($record, $src_data); + $error_msg_end = 'CREATE_FOLDER'; + break; default: $res = true; @@ -777,6 +733,11 @@ public function migrate(string $type, int $pk): object } } } + else + { + $success = false; + $error_msg = Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_FETCH_DATA'); + } } // Load migration data table @@ -992,7 +953,7 @@ public function deleteSource() $keys = \array_keys($migs); $this->setParams($migs[$keys[0]]->params); - // Perform the postchecks + // Delete sources return $this->component->getMigration()->deleteSource(); } @@ -1142,204 +1103,4 @@ protected function deleteRecord(string $type, int $pk): bool return true; } - - /** - * Creation of imagetypes based on one source file. - * Source file has to be given with a full system path. - * - * @param ImageTable $img ImageTable object, already stored - * @param string $source Source file with which the imagetypes shall be created - * - * @return bool True on success, false otherwise - * - * @since 4.0.0 - */ - protected function createImages(ImageTable $img, string $source): bool - { - // Create file manager service - $this->component->createFileManager(); - - // Update catid based on migrated categories - $migrated_cats = $this->component->getMigration()->get('migrateables')['category']->successful; - $migrated_catid = $migrated_cats->get($img->catid); - - // Create imagetypes - return $this->component->getFileManager()->createImages($source, $img->filename, $migrated_catid); - } - - /** - * Creation of imagetypes based on images already available on the server. - * Source files has to be given for each imagetype with a full system path. - * - * @param ImageTable $img ImageTable object, already stored - * @param array $sources List of source images for each imagetype availabe in JG4 - * @param bool $copy True: copy, False: move - * - * @return bool True on success, false otherwise - * - * @since 4.0.0 - * @throws \Exception - */ - protected function reuseImages(ImageTable $img, array $sources, bool $copy = false): bool - { - // Create services - $this->component->createFileManager(); - $this->component->createFilesystem($this->component->getConfig()->get('jg_filesystem','local-images')); - - // Fetch available imagetypes - $imagetypes = $this->component->getFileManager()->get('imagetypes_dict'); - - // Check the source mapping - if(\count(\array_diff_key($imagetypes, $sources)) !== 0 || \count(\array_diff_key($sources, $imagetypes)) !== 0) - { - throw new \Exception('Imagetype mapping from migration script does not match component configuration!', 1); - } - - // Update catid based on migrated categories - $migrated_cats = $this->component->getMigration()->get('migrateables')['category']->successful; - $migrated_catid = $migrated_cats->get($img->catid); - - // Loop through all sources - $error = false; - foreach($imagetypes as $type => $tmp) - { - // Get image source path (with system root) - $img_src = $sources[$type]; - - // Get category destination path - $cat_dst = $this->component->getFileManager()->getCatPath($migrated_catid, $type); - - // Create image destination path - $img_dst = $cat_dst . '/' . $img->filename; - - // Create destination folder if not existent - $folder_dst = \dirname($img_dst); - try - { - $this->component->getFilesystem()->createFolder(\basename($folder_dst), \dirname($folder_dst)); - } - catch(\FileExistsException $e) - { - // Do nothing - } - catch(\Exception $e) - { - // Debug info - $this->component->addDebug(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_CREATE_CATEGORY', \ucfirst($folder_dst))); - $error = true; - - continue; - } - - // Move / Copy image - try - { - if($this->component->getFilesystem()->get('filesystem') == 'local-images') - { - // Sorce and destination on the local filesystem - if($img_src == Path::clean(JPATH_ROOT . '/' . $img_dst)) - { - // Sorce and destination are identical. Do nothing. - continue; - } - - if($copy) - { - if(!File::copy($img_src, Path::clean(JPATH_ROOT . '/' . $img_dst))) - { - $this->component->addDebug(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_COPY_IMAGETYPE', \basename($img_src), $type)); - $error = true; - continue; - } - } - else - { - if(!File::move($img_src, Path::clean(JPATH_ROOT . '/' . $img_dst))) - { - $this->component->addDebug(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_MOVE_IMAGETYPE', \basename($img_src), $type)); - $error = true; - continue; - } - } - } - else - { - // Destination not on the local filesystem. Upload required - $this->component->getFilesystem()->createFile($img->filename, $cat_dst, \file_get_contents($img_src)); - - if(!$copy) - { - // When image shall be moved, source have to be deleted - File::delete($img_src); - } - } - } - catch(\Exception $e) - { - // Operation failed - if($copy) - { - $this->component->addDebug(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_COPY_IMAGETYPE', \basename($img_src), $type)); - } - else - { - $this->component->addDebug(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_MOVE_IMAGETYPE', \basename($img_src), $type)); - } - - $error = true; - - continue; - } - } - - if($error) - { - return false; - } - else - { - return true; - } - } - - /** - * Creation of category folders based on category object. - * - * @param CategoryTable $cat CategoryTable object, already stored - * - * @return bool True on success, false otherwise - * - * @since 4.0.0 - */ - protected function createFolder(CategoryTable $cat): bool - { - // Create file manager service - $this->component->createFileManager(); - - // Create folders - return $this->component->getFileManager()->createCategory($cat->alias, $cat->parent_id); - } - - /** - * Renaming of category folders based on one source file. - * - * @param CategoryTable $cat CategoryTable object, already stored - * @param string $oldName Old foldername of the category - * @param string $newName New foldername of the category - * - * @return bool True on success, false otherwise - * - * @since 4.0.0 - */ - protected function renameFolder(CategoryTable $cat, string $oldName, string $newName): bool - { - // Create file manager service - $this->component->createFileManager(); - - // Reset old foldername - $cat->path = $oldName; - - // Create folders - return $this->component->getFileManager()->renameCategory($cat, $newName); - } } diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index 0d9cf35a..557ce7c6 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -23,6 +23,8 @@ use \Joomla\Database\DatabaseFactory; use \Joomla\Component\Media\Administrator\Exception\FileNotFoundException; use \Joomgallery\Component\Joomgallery\Administrator\Helper\JoomHelper; +use \Joomgallery\Component\Joomgallery\Administrator\Table\ImageTable; +use \Joomgallery\Component\Joomgallery\Administrator\Table\CategoryTable; use \Joomgallery\Component\Joomgallery\Administrator\Table\MigrationTable; use \Joomgallery\Component\Joomgallery\Administrator\Extension\ServiceTrait; use \Joomgallery\Component\Joomgallery\Administrator\Service\Migration\Checks; @@ -156,6 +158,7 @@ public function __destruct() /** * A list of content type definitions depending on migration source + * (Required in migration scripts. The order of the content types must correspond to its migration order) * * @param bool $names_only True to load type names only. No migration parameters required. * @@ -206,6 +209,49 @@ public function convertData(string $type, array $data): array return $data; } + /** + * Performs the neccessary steps to migrate an image in the filesystem + * + * @param ImageTable $img ImageTable object, already stored + * @param array $data Source data received from getData() + * + * @return bool True on success, false otherwise + * + * @since 4.0.0 + */ + public function migrateFiles(ImageTable $img, array $data): bool + { + // Default: Recreate images based on source image + $this->component->createFileManager(); + + // Get source image + $img_source = $this->getImageSource($data); + + // Update catid based on migrated categories + $migrated_cats = $this->get('migrateables')['category']->successful; + $migrated_catid = $migrated_cats->get($img->catid); + + // Create imagetypes + return $this->component->getFileManager()->createImages($img_source, $img->filename, $migrated_catid); + } + + /** + * Performs the neccessary steps to migrate a category in the filesystem + * + * @param CategoryTable $cat CategoryTable object, already stored + * @param array $data Source data received from getData() + * + * @return bool True on success, false otherwise + * + * @since 4.0.0 + */ + public function migrateFolder(CategoryTable $cat, array $data): bool + { + // Default: Create new folders + $this->component->createFileManager(); + return $this->component->getFileManager()->createCategory($cat->alias, $cat->parent_id); + } + /** * Returns a list of content types which can be migrated. * @@ -342,21 +388,8 @@ public function postcheck() } /** - * Step 3 - * Perform one specific miration step and mark it as done at the end. - * - * @return void - * - * @since 4.0.0 - */ - public function migrate($type, $source, $dest) - { - return; - } - - /** - * Step 4 * Delete migration source data. + * It's recommended to use delete source data by uninstalling source extension if possible. * * @return boolean True if successful, false if an error occurs. * @@ -1234,7 +1267,7 @@ protected function checkMigrationErrors(Checks &$checks, string $category) } /** - * Perform script specific checks + * Perform script specific checks at the end of pre and postcheck. * * @param string $type Type of checks (pre or post) * @param Checks $checks The checks object @@ -1244,7 +1277,7 @@ protected function checkMigrationErrors(Checks &$checks, string $category) * * @since 4.0.0 */ - protected function scriptSpecificChecks(string $type, Checks &$checks, string $category) + public function scriptSpecificChecks(string $type, Checks &$checks, string $category) { return; } diff --git a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php index b610223c..6e7c9c11 100644 --- a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php +++ b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php @@ -13,6 +13,9 @@ // No direct access \defined('_JEXEC') or die; +use \Joomgallery\Component\Joomgallery\Administrator\Table\ImageTable; +use \Joomgallery\Component\Joomgallery\Administrator\Table\CategoryTable; + /** * Interface for the migration service class * @@ -21,47 +24,51 @@ */ interface MigrationInterface { - /** - * Constructor - * - * @return void - * - * @since 4.0.0 - */ - public function __construct(); + // /** + // * Name of the migration script to be used. + // * (Required in migration scripts.) + // * + // * @var string + // * + // * @since 4.0.0 + // */ + // protected $name = 'scriptName'; - /** - * Step 2 - * Perform pre migration checks. - * - * @return array|boolean An array containing the precheck results on success. - * - * @since 4.0.0 - */ - public function precheck(): array; + // /** + // * True to offer the task migration.removesource for this script + // * (Required in migration scripts.) + // * + // * @var boolean + // * + // * @since 4.0.0 + // */ + // protected $sourceDeletion = false; /** - * Step 4 - * Perform post migration checks. + * Returns an object with compatibility info for this migration script. + * (Required in migration scripts.) + * + * @param string $type Select if you get source or destination info * - * @return void + * @return Targetinfo Compatibility info object * * @since 4.0.0 */ - public function postcheck(); + public function getTargetinfo(string $type = 'source'): Targetinfo; /** - * Step 3 - * Perform one specific miration step and mark it as done at the end. + * Returns the XML object of the source extension + * (Required in migration scripts. Source extension XML must at least provide name and version info.) * - * @return void + * @return \SimpleXMLElement Extension XML object or False on failure * * @since 4.0.0 */ - public function migrate($type, $source, $dest); + public function getSourceXML(); /** * A list of content type definitions depending on migration source + * (Required in migration scripts. The order of the content types must correspond to its migration order.) * * @param bool $names_only True to load type names only. No migration parameters required. * @@ -75,57 +82,102 @@ public function migrate($type, $source, $dest); public function defineTypes($names_only = false): array; /** - * Get a database object - * - * @param string $target The target (source or destination) + * Returns a list of involved source directories. + * (Required in migration scripts.) * - * @return array list($db, $dbPrefix) + * @return array List of paths + * + * @since 4.0.0 + */ + public function getSourceDirs(): array; + + /** + * Fetches an array of images from source to be used for creating the imagetypes + * for the current image. + * (Required in migration scripts.) * + * @param array $data Source record data received from getData() - before convertData() + * + * @return array List of images from sources used to create the new imagetypes + * 1. If imagetypes get recreated: array('image/source/path') + * 2. If imagetypes get copied/moved: array('original' => 'image/source/path1', 'detail' => 'image/source/path2', ...) + * * @since 4.0.0 - * @throws \Exception - */ - public function getDB(string $target): array; + */ + public function getImageSource(array $data): array; /** - * Returns a list of content types which can be migrated. + * Returns an associative array containing the record data from source. + * (Required in migration scripts.) * - * @return array List of content types + * @param string $type Name of the content type + * @param int $pk The primary key of the content type + * + * @return array Record data * * @since 4.0.0 */ - public function getMigrateables(): array; + public function getData(string $type, int $pk): array; /** - * Returns an object with compatibility info for this migration script. + * Converts data from source into the structure needed for JoomGallery. + * (Optional in migration scripts, but highly recommended.) + * + * @param string $type Name of the content type + * @param array $data Source data received from getData() * - * @param string $type Select if you get source or destination info + * @return array Converted data to save into JoomGallery + * + * @since 4.0.0 + */ + public function convertData(string $type, array $data): array; + + /** + * Perform pre migration checks. + * (Optional in migration scripts, can be overwritten if required.) * - * @return Targetinfo Compatibility info object + * @return array|boolean An array containing the precheck results on success. * * @since 4.0.0 */ - public function getTargetinfo(string $type = 'source'): Targetinfo; + public function precheck(): array; /** - * Returns the XML object of the source extension + * Perform post migration checks. + * (Optional in migration scripts, can be overwritten if required.) * - * @return \SimpleXMLElement Extension XML object or False on failure + * @return void * * @since 4.0.0 */ - public function getSourceXML(); + public function postcheck(); /** - * Returns a list of involved source directories. + * Get a database object + * (Optional in migration scripts, can be overwritten if required.) + * + * @param string $target The target (source or destination) * - * @return array List of paths + * @return array list($db, $dbPrefix) + * + * @since 4.0.0 + * @throws \Exception + */ + public function getDB(string $target): array; + + /** + * Returns a list of content types which can be migrated. + * (Optional in migration scripts, can be overwritten if required.) + * + * @return array List of content types * * @since 4.0.0 */ - public function getSourceDirs(): array; + public function getMigrateables(): array; /** * Returns tablename and primarykey name of the source table + * (Optional in migration scripts, can be overwritten if required.) * * @param string $type The content type name * @@ -138,6 +190,7 @@ public function getSourceTableInfo(string $type): array; /** * Returns a list of involved source tables. + * (Optional in migration scripts, can be overwritten if required.) * * @return array List of table names (Joomla style, e.g #__joomgallery) * array('image' => '#__joomgallery', ...) @@ -148,6 +201,7 @@ public function getSourceTables(): array; /** * Returns a list of involved content types. + * (Optional in migration scripts, can be overwritten if required.) * * @return array List of type names * array('image', 'category', ...) @@ -159,6 +213,7 @@ public function getTypes(): array; /** * True if the given record has to be migrated * False to skip the migration for this record + * (Optional in migration scripts, can be overwritten if required.) * * @param string $type Name of the content type * @param int $pk The primary key of the content type @@ -170,40 +225,53 @@ public function getTypes(): array; public function needsMigration(string $type, int $pk): bool; /** - * Returns an associative array containing the record data from source. + * Performs the neccessary steps to migrate an image in the filesystem + * (Optional in migration scripts, can be overwritten if required.) * - * @param string $type Name of the content type - * @param int $pk The primary key of the content type + * @param ImageTable $img ImageTable object, already stored + * @param array $data Source data received from getData() * - * @return array Record data + * @return bool True on success, false otherwise * * @since 4.0.0 */ - public function getData(string $type, int $pk): array; + public function migrateFiles(ImageTable $img, array $data): bool; /** - * Converts data from source into the structure needed for JoomGallery. + * Performs the neccessary steps to migrate a category in the filesystem + * (Optional in migration scripts, can be overwritten if required.) * - * @param string $type Name of the content type - * @param array $data Data received from getData() method. + * @param CategoryTable $cat CategoryTable object, already stored + * @param array $data Source data received from getData() * - * @return array Converted data to save into JoomGallery + * @return bool True on success, false otherwise * * @since 4.0.0 */ - public function convertData(string $type, array $data): array; + public function migrateFolder(CategoryTable $cat, array $data): bool; /** - * Fetches an array of images from source to be used for creating the imagetypes - * for the current image. - * - * @param array $data Record data received from getData() + * Perform script specific checks at the end of pre and postcheck. + * (Optional in migration scripts, can be overwritten if required.) * - * @return array List of images from sources used to create the new imagetypes - * 1. If imagetypes get recreated: array('image/source/path') - * 2. If imagetypes get copied: array('original' => 'image/source/path1', 'detail' => 'image/source/path2', ...) + * @param string $type Type of checks (pre or post) + * @param Checks $checks The checks object + * @param string $category The checks-category into which to add the new check + * + * @return void + * + * @since 4.0.0 + */ + public function scriptSpecificChecks(string $type, Checks &$checks, string $category); + + /** + * Delete migration source data. + * It's recommended to use delete source data by uninstalling source extension if possible. + * (Optional in migration scripts, can be overwritten if required.) + * + * @return boolean True if successful, false if an error occurs. * * @since 4.0.0 */ - public function getImageSource(array $data): array; + public function deleteSource(); } diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index d6f2684a..f6ff7370 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -15,8 +15,12 @@ use \Joomla\CMS\Language\Text; use \Joomla\CMS\Filesystem\Path; +use \Joomla\CMS\Filesystem\File; use \Joomla\CMS\User\UserFactoryInterface; +use \Joomla\Component\Media\Administrator\Exception\FileExistsException; use \Joomgallery\Component\Joomgallery\Administrator\Helper\JoomHelper; +use \Joomgallery\Component\Joomgallery\Administrator\Table\ImageTable; +use \Joomgallery\Component\Joomgallery\Administrator\Table\CategoryTable; use \Joomgallery\Component\Joomgallery\Administrator\Service\Migration\Checks; use \Joomgallery\Component\Joomgallery\Administrator\Service\Migration\Migration; use \Joomgallery\Component\Joomgallery\Administrator\Service\Migration\Targetinfo; @@ -144,6 +148,7 @@ public function getSourceDirs(): array /** * A list of content type definitions depending on migration source + * (Required in migration scripts. The order of the content types must correspond to its migration order) * * @param bool $names_only True to load type names only. No migration parameters required. * @@ -344,6 +349,86 @@ public function getData(string $type, int $pk): array } } + /** + * Performs the neccessary steps to migrate an image in the filesystem + * + * @param ImageTable $img ImageTable object, already stored + * @param array $data Source data received from getData() + * + * @return bool True on success, false otherwise + * + * @since 4.0.0 + */ + public function migrateFiles(ImageTable $img, array $data): bool + { + $img_source = $this->getImageSource($data); + + // Set old catid for image creation + $img->catid = $data['catid']; + + if($this->params->get('image_usage', 1) == 1) + { + // Recreate imagetypes based on given image + $res = $this->createImages($img, $img_source[0]); + } + elseif($this->params->get('image_usage', 1) == 2 || $this->params->get('image_usage', 1) == 3) + { + $copy = false; + if($this->params->get('image_usage', 1) == 2) + { + $copy = true; + } + + // Copy/Move images from source based on mapping + $res = $this->reuseImages($img, $img_source, $copy); + } + else + { + // Direct usage of images + // Nothing to do + $res = true; + } + + return $res; + } + + /** + * Performs the neccessary steps to migrate a category in the filesystem + * + * @param CategoryTable $cat CategoryTable object, already stored + * @param array $data Source data received from getData() + * + * @return bool True on success, false otherwise + * + * @since 4.0.0 + */ + public function migrateFolder(CategoryTable $cat, array $data): bool + { + // Create file manager service + $this->component->createFileManager(); + + if($this->params->get('image_usage', 1) == 0) + { + // Direct usage + // Store new foldername + $newName = $cat->path; + + // Reset old foldername + $cat->path = $data['catpath']; + + // Rename existing folders + $res = $this->component->getFileManager()->renameCategory($cat, $newName); + } + else + { + // Recreate, copy or move + // Create new folders + $res = $this->component->getFileManager()->createCategory($cat->alias, $cat->parent_id); + } + + return $res; + } + /** * Fetches an array of images from source to be used for creating the imagetypes * for the current image. @@ -415,7 +500,166 @@ public function getImageSource(array $data): array } /** - * Perform script specific checks + * Creation of imagetypes based on one source file. + * Source file has to be given with a full system path. + * + * @param ImageTable $img ImageTable object, already stored + * @param string $source Source file with which the imagetypes shall be created + * + * @return bool True on success, false otherwise + * + * @since 4.0.0 + */ + protected function createImages(ImageTable $img, string $source): bool + { + // Create file manager service + $this->component->createFileManager(); + + // Update catid based on migrated categories + $migrated_cats = $this->get('migrateables')['category']->successful; + $migrated_catid = $migrated_cats->get($img->catid); + + // Create imagetypes + return $this->component->getFileManager()->createImages($source, $img->filename, $migrated_catid); + } + + /** + * Creation of imagetypes based on images already available on the server. + * Source files has to be given for each imagetype with a full system path. + * + * @param ImageTable $img ImageTable object, already stored + * @param array $sources List of source images for each imagetype availabe in JG4 + * @param bool $copy True: copy, False: move + * + * @return bool True on success, false otherwise + * + * @since 4.0.0 + * @throws \Exception + */ + protected function reuseImages(ImageTable $img, array $sources, bool $copy = false): bool + { + // Create services + $this->component->createFileManager(); + $this->component->createFilesystem($this->component->getConfig()->get('jg_filesystem','local-images')); + + // Fetch available imagetypes + $imagetypes = $this->get('imagetypes_dict'); + + // Check the source mapping + if(\count(\array_diff_key($imagetypes, $sources)) !== 0 || \count(\array_diff_key($sources, $imagetypes)) !== 0) + { + throw new \Exception('Imagetype mapping from migration script does not match component configuration!', 1); + } + + // Update catid based on migrated categories + $migrated_cats = $this->get('migrateables')['category']->successful; + $migrated_catid = $migrated_cats->get($img->catid); + + // Loop through all sources + $error = false; + foreach($imagetypes as $type => $tmp) + { + // Get image source path (with system root) + $img_src = $sources[$type]; + + // Get category destination path + $cat_dst = $this->component->getFileManager()->getCatPath($migrated_catid, $type); + + // Create image destination path + $img_dst = $cat_dst . '/' . $img->filename; + + // Create destination folder if not existent + $folder_dst = \dirname($img_dst); + try + { + $this->component->getFilesystem()->createFolder(\basename($folder_dst), \dirname($folder_dst)); + } + catch(FileExistsException $e) + { + // Do nothing + } + catch(\Exception $e) + { + // Debug info + $this->component->addDebug(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_CREATE_CATEGORY', \ucfirst($folder_dst))); + $error = true; + + continue; + } + + // Move / Copy image + try + { + if($this->component->getFilesystem()->get('filesystem') == 'local-images') + { + // Sorce and destination on the local filesystem + if($img_src == Path::clean(JPATH_ROOT . '/' . $img_dst)) + { + // Sorce and destination are identical. Do nothing. + continue; + } + + if($copy) + { + if(!File::copy($img_src, Path::clean(JPATH_ROOT . '/' . $img_dst))) + { + $this->component->addDebug(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_COPY_IMAGETYPE', \basename($img_src), $type)); + $error = true; + continue; + } + } + else + { + if(!File::move($img_src, Path::clean(JPATH_ROOT . '/' . $img_dst))) + { + $this->component->addDebug(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_MOVE_IMAGETYPE', \basename($img_src), $type)); + $error = true; + continue; + } + } + } + else + { + // Destination not on the local filesystem. Upload required + $this->component->getFilesystem()->createFile($img->filename, $cat_dst, \file_get_contents($img_src)); + + if(!$copy) + { + // When image shall be moved, source have to be deleted + File::delete($img_src); + } + } + } + catch(\Exception $e) + { + // Operation failed + if($copy) + { + $this->component->addDebug(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_COPY_IMAGETYPE', \basename($img_src), $type)); + } + else + { + $this->component->addDebug(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_MOVE_IMAGETYPE', \basename($img_src), $type)); + } + + $error = true; + + continue; + } + } + + if($error) + { + return false; + } + else + { + return true; + } + } + + /** + * Perform script specific checks at the end of pre and postcheck. * * @param string $type Type of checks (pre or post) * @param Checks $checks The checks object @@ -425,7 +669,7 @@ public function getImageSource(array $data): array * * @since 4.0.0 */ - protected function scriptSpecificChecks(string $type, Checks &$checks, string $category) + public function scriptSpecificChecks(string $type, Checks &$checks, string $category) { if($type == 'pre') { From 7f71049c5bfd8de677d9c6032ab2995e05235958 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Wed, 3 Jan 2024 13:20:14 +0100 Subject: [PATCH 059/121] fix error logging --- .../language/en-GB/com_joomgallery.ini | 10 +++---- .../src/Model/MigrationModel.php | 6 ++++ .../Service/Migration/Scripts/Jg3ToJg4.php | 2 +- .../js/migrator/dist/migrator.js | 30 +++++++++++-------- .../js/migrator/dist/migrator.js.map | 2 +- .../com_joomgallery/js/migrator/src/index.js | 30 +++++++++++-------- 6 files changed, 47 insertions(+), 33 deletions(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index d1675010..32de3ae9 100644 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -738,11 +738,11 @@ COM_JOOMGALLERY_SERVICE_MIGRATION_MAPPING_SRC_IMAGETYPE_NOT_EXIST="The source im COM_JOOMGALLERY_SERVICE_MIGRATION_MAPPING_IMAGETYPE_NOT_USED="The following destination imagetypes are not used in the mapping: '%s'. Please go back to step 1 and apply the mapping correctly." COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_RECORD_ID_MISSING="Starting a migration without source data ID not possible. Contact the developer of the migration script to solve this." COM_JOOMGALLERY_SERVICE_ERROR_MIGRATION_RESUME="Migration can not be resumed. There is a problem in fetching the required info." -COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_FETCH_DATA="Data from source could not be loaded. See log file for more destails." -COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_CONVERT_DATA="Data from source could not be converted to new data structure. See log file for more destails." -COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_INSERT_RECORD="Data could not be inserted into destination database. See log file for more destails." -COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_CREATE_IMGTYPE="Error in creating imagetypes. See log file for more destails." -COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_CREATE_FOLDER="Error in creating folders. See log file for more destails." +COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_FETCH_DATA="Data from source could not be loaded. See log file for more details." +COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_CONVERT_DATA="Data from source could not be converted to new data structure. See log file for more details." +COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_INSERT_RECORD="Data could not be inserted into destination database. See log file for more details." +COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_CREATE_IMGTYPE="Error in creating imagetypes. See log file for more details." +COM_JOOMGALLERY_SERVICE_MIGRATION_FAILED_CREATE_FOLDER="Error in creating folders. See log file for more details." COM_JOOMGALLERY_SERVICE_MIGRATION_FILENAME_DIFF="It was noticed that two different filenames were used for detail/original and thumbnail in the image with id: %s and alias: %s. The migration script will skip the migration of this image." COM_JOOMGALLERY_SERVICE_MIGRATION_ACTIVE="Currently in migration" COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_APPLYSTATE_FORMCHECK="Record migration state couldn't be modified. Please fill out form correctly." diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index 4ee150a3..caef85f7 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -717,6 +717,12 @@ public function migrate(string $type, int $pk): object case 'category': $res = $this->component->getMigration()->migrateFolder($record, $src_data); $error_msg_end = 'CREATE_FOLDER'; + + if(!$res) + { + // Stop automatic migration if something went wrong in the filesystem + $this->component->getMigration()->set('continue', false); + } break; default: diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index f6ff7370..879f2e08 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -405,7 +405,7 @@ public function migrateFiles(ImageTable $img, array $data): bool public function migrateFolder(CategoryTable $cat, array $data): bool { // Create file manager service - $this->component->createFileManager(); + $this->component->createFileManager() if($this->params->get('image_usage', 1) == 0) { diff --git a/media/com_joomgallery/js/migrator/dist/migrator.js b/media/com_joomgallery/js/migrator/dist/migrator.js index 22236f3d..a05ea6e2 100644 --- a/media/com_joomgallery/js/migrator/dist/migrator.js +++ b/media/com_joomgallery/js/migrator/dist/migrator.js @@ -89,39 +89,34 @@ let submitTask = function(event, element) { let formId = formIdTmpl + '-' + type; let task = element.parentNode.querySelector('[name="task"]').value; - startTask(type, element); + if(tryCounter == 0) { + startTask(type, element); + } - tryCounter++; + tryCounter = tryCounter + 1; ajax(formId, task) .then(res => { // Handle the successful result here responseHandler(type, res); - console.log('forceStop: ' + forceStop); - console.log('continueState: ' + continueState); - if(tryCounter >= tryLimit) { // We reached the limit of tries --> looks like we have a network problem updateMigrateables(type, {'success': false, 'message': Joomla.JText._('COM_JOOMGALLERY_ERROR_NETWORK_PROBLEM'), 'data': false}); // Stop automatic execution and update GUI - console.log('forceStop; We reached the limit of tries'); forceStop = true; } if(continueState && !forceStop) { // Kick off the next task - console.log('Kick off the next task'); submitTask(event, element); } else { // Stop automatic task execution and update GUI - console.log('Stop automatic task execution and update GUI'); finishTask(type, element); } }) .catch(error => { // Handle any errors here - console.error(error); addLog(error, type, 'error'); }); }; @@ -287,9 +282,12 @@ let getNextMigrationID = function(formId) { */ let responseHandler = function(type, response) { if(response.success == false) { - // Ajax request failed + // Ajax request failed or server responded with error code + addLog('Error in server response. We will try again. ('+tryCounter+'/'+tryLimit+')', type, 'info'); addLog(response.message, type, 'error'); addLog(response.messages, type, 'error'); + addLog(response.data.error, type, 'error'); + // Try again... } @@ -303,7 +301,7 @@ let responseHandler = function(type, response) { // Stop autimatic continuation if requested from backend if(!response.data.continue || response.data.continue == null || response.data.continue == false) { - console.log('continueState; autimatic continuation requested from backend'); + console.log('Stop automatic continuation requested from backend'); continueState = false; } @@ -318,7 +316,7 @@ let responseHandler = function(type, response) { // Stop autimatic continuation if requested from backend if(!response.data.continue || response.data.continue == null || response.data.continue == false) { - console.log('continueState; autimatic continuation requested from backend'); + console.log('Stop automatic continuation requested from backend'); continueState = false; } @@ -359,6 +357,13 @@ let addLog = function(msg, type, msgType, console=false, newLine=true, marginTop // Convert string to array if(tmp_msg !== '') { + // remove object properties 'error' and 'code' if existent + if('error' in tmp_msg) { + delete tmp_msg.error; + } + if('code' in tmp_msg) { + delete tmp_msg.code; + } msg = Object.values(tmp_msg); } else { msg = [msg]; @@ -373,7 +378,6 @@ let addLog = function(msg, type, msgType, console=false, newLine=true, marginTop // Loop through all messages msg.forEach((message, i) => { - // Print in console if(console) { console.log(message); diff --git a/media/com_joomgallery/js/migrator/dist/migrator.js.map b/media/com_joomgallery/js/migrator/dist/migrator.js.map index 2194196d..8d30ef36 100644 --- a/media/com_joomgallery/js/migrator/dist/migrator.js.map +++ b/media/com_joomgallery/js/migrator/dist/migrator.js.map @@ -1 +1 @@ -{"version":3,"file":"migrator.js","mappings":";;;UAAA;UACA;;;;;WCDA;WACA;WACA;WACA;WACA,yCAAyC,wCAAwC;WACjF;WACA;WACA;;;;;WCPA;;;;;WCAA;WACA;WACA;WACA,uDAAuD,iBAAiB;WACxE;WACA,gDAAgD,aAAa;WAC7D;;;;;;;;;;;;;;ACNA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,SAAS;AAClB;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kCAAkC,oGAAoG;AACtI;AACA,gCAAgC;AAChC;AACA;AACA;AACA;AACA;AACA;AACA;AACA,QAAQ;AACR;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kFAAkF,gBAAgB;AAClG;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB;AACA,aAAa,UAAU;AACvB,aAAa,qDAAqD,aAAa;AAC/E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,gFAAgF,SAAS;AACrG;AACA;AACA,sBAAsB;AACtB;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA,WAAW,mFAAmF,SAAS;AACvG,IAAI;AACJ;AACA,8BAA8B;AAC9B,6BAA6B;AAC7B;AACA,WAAW;AACX;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB;AACA,aAAa,UAAU;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,qDAAqD,WAAW;AAC7E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oCAAoC;AACpC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oCAAoC;AACpC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB,YAAY,UAAU;AACtB,cAAc;AACd;AACA;AACA;AACA;AACA;AACA;AACA,kBAAkB;AAClB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB,YAAY,UAAU;AACtB,cAAc;AACd;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,yDAAyD,kDAAkD;AAC3G;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA,C","sources":["webpack://Migrator/webpack/bootstrap","webpack://Migrator/webpack/runtime/define property getters","webpack://Migrator/webpack/runtime/hasOwnProperty shorthand","webpack://Migrator/webpack/runtime/make namespace object","webpack://Migrator/./src/index.js"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","// Selectors used by this script\r\nlet typeSelector = 'data-type';\r\nlet formIdTmpl = 'migrationForm';\r\nlet buttonTmpl = 'migrationBtn';\r\nlet step4Btn = 'step4Btn';\r\nlet tryLimit = 3;\r\n\r\n/**\r\n * Storage for migrateables\r\n * @var {Object} migrateablesList\r\n */\r\nvar migrateablesList = {};\r\n\r\n/**\r\n * Counter of how many times the same migration was tried to perfrom\r\n * @var {Integer} tryCounter\r\n */\r\nvar tryCounter = 0;\r\n\r\n/**\r\n * State. As long as this state is set to true, the migration will be\r\n * continued automatically regarding the pending queue in the migrateablesList.\r\n * @var {Boolean} continueState\r\n */\r\nvar continueState = true;\r\n\r\n/**\r\n * State. Set this state to true to stop automatic execution as soon as the next ajax respond comes back.\r\n * @var {Boolean} forceStop\r\n */\r\nvar forceStop = false;\r\n\r\n/**\r\n * Submit the migration task by pressing the button\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let submitTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n let type = element.getAttribute(typeSelector);\r\n let formId = formIdTmpl + '-' + type;\r\n let task = element.parentNode.querySelector('[name=\"task\"]').value;\r\n\r\n startTask(type, element);\r\n\r\n tryCounter++;\r\n\r\n ajax(formId, task)\r\n .then(res => {\r\n // Handle the successful result here\r\n responseHandler(type, res);\r\n\r\n console.log('forceStop: ' + forceStop);\r\n console.log('continueState: ' + continueState);\r\n\r\n if(tryCounter >= tryLimit) {\r\n // We reached the limit of tries --> looks like we have a network problem\r\n updateMigrateables(type, {'success': false, 'message': Joomla.JText._('COM_JOOMGALLERY_ERROR_NETWORK_PROBLEM'), 'data': false});\r\n // Stop automatic execution and update GUI\r\n console.log('forceStop; We reached the limit of tries');\r\n forceStop = true;\r\n }\r\n \r\n if(continueState && !forceStop) {\r\n // Kick off the next task\r\n console.log('Kick off the next task');\r\n submitTask(event, element);\r\n } else {\r\n // Stop automatic task execution and update GUI\r\n console.log('Stop automatic task execution and update GUI');\r\n finishTask(type, element);\r\n }\r\n })\r\n .catch(error => {\r\n // Handle any errors here\r\n console.error(error);\r\n addLog(error, type, 'error');\r\n });\r\n};\r\n\r\n/**\r\n * Stop the migration task by pressing the button\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let stopTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n let type = element.getAttribute(typeSelector);\r\n let bar = document.getElementById('progress-'+type);\r\n let startBtn = document.getElementById('migrationBtn-'+type);\r\n let stopBtn = element;\r\n\r\n // Force automatic execution to stop\r\n forceStop = true;\r\n\r\n // Update progress bar\r\n bar.classList.remove('progress-bar-striped');\r\n bar.classList.remove('progress-bar-animated');\r\n \r\n // Enable start button\r\n startBtn.classList.remove('disabled');\r\n startBtn.removeAttribute('disabled');\r\n\r\n // Disable stop button\r\n stopBtn.classList.add('disabled');\r\n stopBtn.setAttribute('disabled', 'true');\r\n}\r\n\r\n/**\r\n * Manually set one record migration to true\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let repairTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n // Get relevant elements\r\n let type = element.getAttribute(typeSelector);\r\n let mig = document.getElementById('migrationForm-'+type).querySelector('[name=\"migrateable\"]');\r\n let inputType = document.getElementById('migrepairForm').querySelector('[name=\"type\"]');\r\n let inputMig = document.getElementById('migrepairForm').querySelector('[name=\"migrateable\"]');\r\n\r\n // Fill input values\r\n inputType.value = type;\r\n inputMig.value = mig.value;\r\n\r\n // Show modal\r\n let bsmodal = new bootstrap.Modal(document.getElementById('repair-modal-box'), {keyboard: false});\r\n bsmodal.show();\r\n}\r\n\r\n/**\r\n * Perform an ajax request in json format\r\n * \r\n * @param {String} formId Id of the form element\r\n * @param {String} task Name of the task\r\n * \r\n * @returns {Object} Result object\r\n * {success: true, status: 200, message: '', messages: {}, data: { { {success, data, continue, error, debug, warning} }}\r\n */\r\nlet ajax = async function(formId, task) {\r\n\r\n // Catch form and data\r\n let formData = new FormData(document.getElementById(formId));\r\n formData.append('format', 'json');\r\n\r\n if(task == 'migration.start') {\r\n formData.append('id', getNextMigrationID(formId));\r\n }\r\n\r\n // Set request parameters\r\n let parameters = {\r\n method: 'POST',\r\n mode: 'same-origin',\r\n cache: 'default',\r\n redirect: 'follow',\r\n referrerPolicy: 'no-referrer-when-downgrade',\r\n body: formData,\r\n };\r\n\r\n // Set the url\r\n let url = document.getElementById(formId).getAttribute('action');\r\n\r\n // Perform the fetch request\r\n let response = await fetch(url, parameters);\r\n\r\n // Resolve promise as text string\r\n let txt = await response.text();\r\n let res = null;\r\n\r\n if (!response.ok) {\r\n // Catch network error\r\n return {success: false, status: response.status, message: response.message, messages: {}, data: {error: txt, data:null}};\r\n }\r\n\r\n if(txt.startsWith('{\"success\"')) {\r\n // Response is of type json --> everything fine\r\n res = JSON.parse(txt);\r\n res.status = response.status;\r\n res.data = JSON.parse(res.data);\r\n } else if (txt.includes('Fatal error')) {\r\n // PHP fatal error occurred\r\n res = {success: false, status: response.status, message: response.statusText, messages: {}, data: {error: txt, data:null}};\r\n } else {\r\n // Response is not of type json --> probably some php warnings/notices\r\n let split = txt.split('\\n{\"');\r\n let temp = JSON.parse('{\"'+split[1]);\r\n let data = JSON.parse(temp.data);\r\n res = {success: true, status: response.status, message: split[0], messages: temp.messages, data: data};\r\n }\r\n\r\n // Make sure res.data.data.queue is of type array\r\n if(typeof res.data.data != \"undefined\" && res.data.data != null && 'queue' in res.data.data) {\r\n if(res.data.data.queue.constructor !== Array) {\r\n res.data.data.queue = Object.values(res.data.data.queue);\r\n }\r\n }\r\n\r\n return res;\r\n}\r\n\r\n/**\r\n * Perform a migration task\r\n * @param {String} formId Id of the form element\r\n * \r\n * @returns {String} Id of the database record to be migrated\r\n */\r\nlet getNextMigrationID = function(formId) {\r\n let type = formId.replace(formIdTmpl + '-', '');\r\n let form = document.getElementById(formId);\r\n\r\n let migrateable = atob(form.querySelector('[name=\"migrateable\"]').value);\r\n migrateable = JSON.parse(migrateable);\r\n\r\n // Overwrite migrateable in list\r\n migrateablesList[type] = migrateable;\r\n\r\n // Loop through queue\r\n for (let id of migrateable.queue) {\r\n if (!(id in migrateable.successful) && !(id in migrateable.failed)) {\r\n migrateablesList[type]['currentID'] = id;\r\n break;\r\n }\r\n }\r\n\r\n return migrateablesList[type]['currentID'];\r\n}\r\n\r\n/**\r\n * Handle migration response\r\n * \r\n * @param {Object} response The response object in the form of\r\n * {success: true, status: 200, message: '', messages: {}, data: { {success, data, continue, error, debug, warning} }}\r\n * \r\n * @returns void\r\n */\r\nlet responseHandler = function(type, response) {\r\n if(response.success == false) {\r\n // Ajax request failed\r\n addLog(response.message, type, 'error');\r\n addLog(response.messages, type, 'error');\r\n\r\n // Try again...\r\n }\r\n else {\r\n // Ajax request successful\r\n if(!response.data.success)\r\n {\r\n // Migration failed\r\n addLog('[Migrator.js] Migration of '+type+' with id = '+migrateablesList[type]['currentID']+' failed.', type, 'error');\r\n logMessages(type, response.data);\r\n\r\n // Stop autimatic continuation if requested from backend\r\n if(!response.data.continue || response.data.continue == null || response.data.continue == false) {\r\n console.log('continueState; autimatic continuation requested from backend');\r\n continueState = false;\r\n }\r\n\r\n // Update migrateables\r\n updateMigrateables(type, response.data);\r\n }\r\n else\r\n {\r\n // Save record successful\r\n logMessages(type, response.data);\r\n addLog('[Migrator.js] Migration of '+type+' with id = '+migrateablesList[type]['currentID']+' successful.', type, 'success');\r\n\r\n // Stop autimatic continuation if requested from backend\r\n if(!response.data.continue || response.data.continue == null || response.data.continue == false) {\r\n console.log('continueState; autimatic continuation requested from backend');\r\n continueState = false;\r\n }\r\n\r\n // Update migrateables\r\n updateMigrateables(type, response.data);\r\n\r\n // Reset tryCounter\r\n tryCounter = 0;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Add a message to the logging output and the console\r\n * \r\n * @param {Mixed} msg One or multiple messages to be added to the log\r\n * @param {String} type The type defining the logging output to use\r\n * @param {String} msgType The type of message (available: error, warning, success, info)\r\n * @param {Boolean} console True to add the message also to the console\r\n * @param {Boolean} newLine True to add the message on a new line\r\n * @param {Integer} marginTop Number of how much margin you want on the top of the message\r\n * \r\n * @returns void\r\n */\r\nlet addLog = function(msg, type, msgType, console=false, newLine=true, marginTop=0) {\r\n if(!Boolean(msg) || msg == null || msg == '') {\r\n // Message is empty. Do nothing\r\n return;\r\n } else if(typeof msg === 'string') {\r\n // Your message is a simple string\r\n let tmp_msg = '';\r\n\r\n // Test if your string a json string\r\n try {\r\n tmp_msg = JSON.parse(msg);\r\n } catch (e) {\r\n }\r\n\r\n // Convert string to array\r\n if(tmp_msg !== '') {\r\n msg = Object.values(tmp_msg);\r\n } else {\r\n msg = [msg];\r\n }\r\n } else if(typeof msg === 'object') {\r\n // Your message is an object. Convert to array\r\n msg = Object.values(msg);\r\n }\r\n\r\n // Get logging output element\r\n let logOutput = document.getElementById('logOutput-'+type);\r\n\r\n // Loop through all messages\r\n msg.forEach((message, i) => {\r\n\r\n // Print in console\r\n if(console) {\r\n console.log(message);\r\n }\r\n\r\n // Create element\r\n let line = null;\r\n if(newLine) {\r\n line = document.createElement('p');\r\n } else {\r\n line = document.createElement('span');\r\n }\r\n\r\n // Top margin to element\r\n marginTop = parseInt(marginTop);\r\n if(marginTop > 0) {\r\n line.classList.add('mt-'+String(marginTop));\r\n }\r\n\r\n // Add text color\r\n line.classList.add('color-'+msgType);\r\n \r\n // Add message to element\r\n let msgType_txt = msgType.toLocaleUpperCase();\r\n line.textContent = '['+Joomla.JText._(msgType_txt)+'] '+String(message);\r\n\r\n // Print into logging output\r\n logOutput.appendChild(line);\r\n });\r\n}\r\n\r\n/**\r\n * Clear the logging output\r\n *\r\n * @param {String} type The type defining the logging output to clear\r\n * \r\n * @returns void\r\n */\r\nlet clearLog = function(type) {\r\n // Get logging output element\r\n let logOutput = document.getElementById('logOutput-'+type);\r\n\r\n // clear\r\n logOutput.innerHTML = '';\r\n}\r\n\r\n/**\r\n * Output all available messages from the result object\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {Object} res The result object in the form of\r\n * {success: bool, data: mixed, continue: bool, error: string|array, debug: string|array, warning: string|array}\r\n * \r\n * @returns void\r\n */\r\nlet logMessages = function(type, res) {\r\n // Available message types: error, debug, warning\r\n let available = ['error', 'debug', 'warning'];\r\n let msgTypes = {'error': 'error', 'debug': 'info', 'warning': 'warning'};\r\n\r\n available.forEach((value, index) => {\r\n if(!res[value] || !Boolean(res.data) || res.data == null) {\r\n return;\r\n }\r\n\r\n addLog(res[value], type, msgTypes[value]);\r\n });\r\n}\r\n\r\n/**\r\n * Update migrateable input field, progress bar and badges\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {Object} res The result object in the form of\r\n * {success: bool, data: mixed, continue: bool, error: string|array, debug: string|array, warning: string|array}\r\n * \r\n * @returns void\r\n */\r\nlet updateMigrateables = function(type, res) {\r\n let formId = formIdTmpl + '-' + type;\r\n let form = document.getElementById(formId);\r\n\r\n if(!res.success && (!Boolean(res.data) || res.data == null || res.data == '')) {\r\n // Migration failed, but no data available in result\r\n\r\n // Create result data based on input field\r\n let migrateable = atob(form.querySelector('[name=\"migrateable\"]').value);\r\n res.data = JSON.parse(migrateable);\r\n\r\n // See: Joomgallery\\Component\\Joomgallery\\Administrator\\Model\\MigrationModel::migrate\r\n // Remove migrated primary key from queue\r\n res.data.queue = res.data.queue.filter(function(e) { return e !== migrateablesList[type]['currentID'] })\r\n\r\n // Add migrated primary key to failed object\r\n res.data.failed[migrateablesList[type]['currentID']] = res.message;\r\n }\r\n\r\n if(!Boolean(res.data.progress) || res.data.progress == null || res.data.progress == '') {\r\n // Update progress if not delivered with result object\r\n let total = res.data.queue.lenght + Object.keys(res.data.successful).length + Object.keys(res.data.failed).length;\r\n let finished = Object.keys(res.data.successful).length + Object.keys(res.data.failed).length;\r\n res.data.progress = Math.round((100 / total) * (finished));\r\n }\r\n\r\n // Get badges\r\n let queueBadge = document.getElementById('badgeQueue-'+type);\r\n let resBadge = document.getElementById('badgeSuccessful-'+type);\r\n if(!res.success) {\r\n resBadge = document.getElementById('badgeFailed-'+type);\r\n }\r\n\r\n // Update migrateable input field\r\n let field = form.querySelector('[name=\"migrateable\"]');\r\n field.value = btoa(JSON.stringify(res.data));\r\n\r\n // Update badges\r\n queueBadge.innerHTML = parseInt(queueBadge.innerHTML) - 1;\r\n resBadge.innerHTML = parseInt(resBadge.innerHTML) + 1;\r\n\r\n // Update progress bar\r\n let bar = document.getElementById('progress-'+type);\r\n bar.setAttribute('aria-valuenow', res.data.progress);\r\n bar.style.width = res.data.progress + '%';\r\n bar.innerText = res.data.progress + '%';\r\n}\r\n\r\n/**\r\n * Update GUI to start migration\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {DOM Element} button The button beeing pressed to start the task\r\n * \r\n * @returns void\r\n */\r\nlet startTask = function(type, button) {\r\n let bar = document.getElementById('progress-'+type);\r\n let startBtn = button;\r\n let stopBtn = document.getElementById('stopBtn-'+type);\r\n\r\n // Update progress bar\r\n bar.classList.remove('progress-bar-striped');\r\n bar.classList.remove('progress-bar-animated');\r\n \r\n // Disable start button\r\n startBtn.classList.add('disabled');\r\n startBtn.setAttribute('disabled', 'true');\r\n\r\n // Enable stop button\r\n stopBtn.classList.remove('disabled');\r\n stopBtn.removeAttribute('disabled');\r\n\r\n // Reinitialize variables\r\n tryCounter = 0;\r\n continueState = true;\r\n forceStop = false;\r\n}\r\n\r\n/**\r\n * Update GUI to end migration\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {DOM Element} button The button beeing pressed to start the task\r\n * \r\n * @returns void\r\n */\r\nlet finishTask = function(type, button) {\r\n let bar = document.getElementById('progress-'+type);\r\n let startBtn = button;\r\n let stopBtn = document.getElementById('stopBtn-'+type);\r\n\r\n // Update progress bar\r\n bar.classList.remove('progress-bar-striped');\r\n bar.classList.remove('progress-bar-animated');\r\n \r\n // Enable start button\r\n if(!migrateablesList[type]['complete']) {\r\n // Only enable start button if migration is not finished\r\n startBtn.classList.remove('disabled');\r\n startBtn.removeAttribute('disabled');\r\n }\r\n\r\n // Disable stop button\r\n stopBtn.classList.add('disabled');\r\n stopBtn.setAttribute('disabled', 'true');\r\n\r\n // If migration is completed\r\n if(migrateablesList[type]['complete']) {\r\n // Update next start button\r\n enableNextBtn(type, button);\r\n // Update step 4 button\r\n updateStep4Btn();\r\n }\r\n}\r\n\r\n/**\r\n * Enable start button of next migration content type\r\n * \r\n * @param {String} type The type defining the content type to be updated\r\n * @param {DOM Element} button The current start button\r\n * \r\n * @returns void\r\n */\r\nlet enableNextBtn = function(type, button) {\r\n let types_inputs = document.getElementsByName('type');\r\n let next_type = '';\r\n\r\n // Find next migration content type\r\n let this_type = false;\r\n types_inputs.forEach((type_input) => {\r\n if(this_type) {\r\n next_type = type_input.value;\r\n return;\r\n }\r\n if(Boolean(type_input.value) && type_input.value == type) {\r\n this_type = true;\r\n }\r\n });\r\n\r\n // Get next button\r\n let nextBtn = document.getElementById(buttonTmpl + '-' + next_type);\r\n\r\n // Enable button\r\n nextBtn.classList.remove('disabled');\r\n nextBtn.removeAttribute('disabled');\r\n}\r\n\r\n/**\r\n * Update button to go to step 4\r\n * \r\n * @returns void\r\n */\r\nlet updateStep4Btn = function() {\r\n let types_inputs = document.getElementsByName('type');\r\n let types = {};\r\n\r\n // Add all available migrateables to types object\r\n types_inputs.forEach((type) => {\r\n if(Boolean(type.value)) {\r\n types[type.value] = false;\r\n }\r\n });\r\n\r\n // Check if all migrateables are available and completed\r\n let tot_complete = true;\r\n Object.keys(types).forEach(type => { \r\n if(Boolean(migrateablesList[type])) {\r\n if(!migrateablesList[type]['completed'])\r\n {\r\n // Migrateable not yet completed\r\n tot_complete = false;\r\n }\r\n }\r\n else\r\n {\r\n // Migrateable does not yet exist. Thus not completed\r\n tot_complete = false;\r\n }\r\n });\r\n\r\n if(tot_complete) {\r\n // Enable step 4 button\r\n document.getElementById(step4Btn).classList.remove('disabled');\r\n document.getElementById(step4Btn).removeAttribute('disabled');\r\n }\r\n}"],"names":[],"sourceRoot":""} \ No newline at end of file +{"version":3,"file":"migrator.js","mappings":";;;UAAA;UACA;;;;;WCDA;WACA;WACA;WACA;WACA,yCAAyC,wCAAwC;WACjF;WACA;WACA;;;;;WCPA;;;;;WCAA;WACA;WACA;WACA,uDAAuD,iBAAiB;WACxE;WACA,gDAAgD,aAAa;WAC7D;;;;;;;;;;;;;;ACNA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,SAAS;AAClB;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kCAAkC,oGAAoG;AACtI;AACA;AACA;AACA;AACA;AACA;AACA;AACA,QAAQ;AACR;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kFAAkF,gBAAgB;AAClG;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB;AACA,aAAa,UAAU;AACvB,aAAa,qDAAqD,aAAa;AAC/E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,gFAAgF,SAAS;AACrG;AACA;AACA,sBAAsB;AACtB;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA,WAAW,mFAAmF,SAAS;AACvG,IAAI;AACJ;AACA,8BAA8B;AAC9B,6BAA6B;AAC7B;AACA,WAAW;AACX;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB;AACA,aAAa,UAAU;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,qDAAqD,WAAW;AAC7E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB,YAAY,UAAU;AACtB,cAAc;AACd;AACA;AACA;AACA;AACA;AACA;AACA,kBAAkB;AAClB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB,YAAY,UAAU;AACtB,cAAc;AACd;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,yDAAyD,kDAAkD;AAC3G;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA,C","sources":["webpack://Migrator/webpack/bootstrap","webpack://Migrator/webpack/runtime/define property getters","webpack://Migrator/webpack/runtime/hasOwnProperty shorthand","webpack://Migrator/webpack/runtime/make namespace object","webpack://Migrator/./src/index.js"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","// Selectors used by this script\r\nlet typeSelector = 'data-type';\r\nlet formIdTmpl = 'migrationForm';\r\nlet buttonTmpl = 'migrationBtn';\r\nlet step4Btn = 'step4Btn';\r\nlet tryLimit = 3;\r\n\r\n/**\r\n * Storage for migrateables\r\n * @var {Object} migrateablesList\r\n */\r\nvar migrateablesList = {};\r\n\r\n/**\r\n * Counter of how many times the same migration was tried to perfrom\r\n * @var {Integer} tryCounter\r\n */\r\nvar tryCounter = 0;\r\n\r\n/**\r\n * State. As long as this state is set to true, the migration will be\r\n * continued automatically regarding the pending queue in the migrateablesList.\r\n * @var {Boolean} continueState\r\n */\r\nvar continueState = true;\r\n\r\n/**\r\n * State. Set this state to true to stop automatic execution as soon as the next ajax respond comes back.\r\n * @var {Boolean} forceStop\r\n */\r\nvar forceStop = false;\r\n\r\n/**\r\n * Submit the migration task by pressing the button\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let submitTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n let type = element.getAttribute(typeSelector);\r\n let formId = formIdTmpl + '-' + type;\r\n let task = element.parentNode.querySelector('[name=\"task\"]').value;\r\n\r\n if(tryCounter == 0) {\r\n startTask(type, element);\r\n } \r\n\r\n tryCounter = tryCounter + 1;\r\n\r\n ajax(formId, task)\r\n .then(res => {\r\n // Handle the successful result here\r\n responseHandler(type, res);\r\n\r\n if(tryCounter >= tryLimit) {\r\n // We reached the limit of tries --> looks like we have a network problem\r\n updateMigrateables(type, {'success': false, 'message': Joomla.JText._('COM_JOOMGALLERY_ERROR_NETWORK_PROBLEM'), 'data': false});\r\n // Stop automatic execution and update GUI\r\n forceStop = true;\r\n }\r\n \r\n if(continueState && !forceStop) {\r\n // Kick off the next task\r\n submitTask(event, element);\r\n } else {\r\n // Stop automatic task execution and update GUI\r\n finishTask(type, element);\r\n }\r\n })\r\n .catch(error => {\r\n // Handle any errors here\r\n addLog(error, type, 'error');\r\n });\r\n};\r\n\r\n/**\r\n * Stop the migration task by pressing the button\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let stopTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n let type = element.getAttribute(typeSelector);\r\n let bar = document.getElementById('progress-'+type);\r\n let startBtn = document.getElementById('migrationBtn-'+type);\r\n let stopBtn = element;\r\n\r\n // Force automatic execution to stop\r\n forceStop = true;\r\n\r\n // Update progress bar\r\n bar.classList.remove('progress-bar-striped');\r\n bar.classList.remove('progress-bar-animated');\r\n \r\n // Enable start button\r\n startBtn.classList.remove('disabled');\r\n startBtn.removeAttribute('disabled');\r\n\r\n // Disable stop button\r\n stopBtn.classList.add('disabled');\r\n stopBtn.setAttribute('disabled', 'true');\r\n}\r\n\r\n/**\r\n * Manually set one record migration to true\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let repairTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n // Get relevant elements\r\n let type = element.getAttribute(typeSelector);\r\n let mig = document.getElementById('migrationForm-'+type).querySelector('[name=\"migrateable\"]');\r\n let inputType = document.getElementById('migrepairForm').querySelector('[name=\"type\"]');\r\n let inputMig = document.getElementById('migrepairForm').querySelector('[name=\"migrateable\"]');\r\n\r\n // Fill input values\r\n inputType.value = type;\r\n inputMig.value = mig.value;\r\n\r\n // Show modal\r\n let bsmodal = new bootstrap.Modal(document.getElementById('repair-modal-box'), {keyboard: false});\r\n bsmodal.show();\r\n}\r\n\r\n/**\r\n * Perform an ajax request in json format\r\n * \r\n * @param {String} formId Id of the form element\r\n * @param {String} task Name of the task\r\n * \r\n * @returns {Object} Result object\r\n * {success: true, status: 200, message: '', messages: {}, data: { { {success, data, continue, error, debug, warning} }}\r\n */\r\nlet ajax = async function(formId, task) {\r\n\r\n // Catch form and data\r\n let formData = new FormData(document.getElementById(formId));\r\n formData.append('format', 'json');\r\n\r\n if(task == 'migration.start') {\r\n formData.append('id', getNextMigrationID(formId));\r\n }\r\n\r\n // Set request parameters\r\n let parameters = {\r\n method: 'POST',\r\n mode: 'same-origin',\r\n cache: 'default',\r\n redirect: 'follow',\r\n referrerPolicy: 'no-referrer-when-downgrade',\r\n body: formData,\r\n };\r\n\r\n // Set the url\r\n let url = document.getElementById(formId).getAttribute('action');\r\n\r\n // Perform the fetch request\r\n let response = await fetch(url, parameters);\r\n\r\n // Resolve promise as text string\r\n let txt = await response.text();\r\n let res = null;\r\n\r\n if (!response.ok) {\r\n // Catch network error\r\n return {success: false, status: response.status, message: response.message, messages: {}, data: {error: txt, data:null}};\r\n }\r\n\r\n if(txt.startsWith('{\"success\"')) {\r\n // Response is of type json --> everything fine\r\n res = JSON.parse(txt);\r\n res.status = response.status;\r\n res.data = JSON.parse(res.data);\r\n } else if (txt.includes('Fatal error')) {\r\n // PHP fatal error occurred\r\n res = {success: false, status: response.status, message: response.statusText, messages: {}, data: {error: txt, data:null}};\r\n } else {\r\n // Response is not of type json --> probably some php warnings/notices\r\n let split = txt.split('\\n{\"');\r\n let temp = JSON.parse('{\"'+split[1]);\r\n let data = JSON.parse(temp.data);\r\n res = {success: true, status: response.status, message: split[0], messages: temp.messages, data: data};\r\n }\r\n\r\n // Make sure res.data.data.queue is of type array\r\n if(typeof res.data.data != \"undefined\" && res.data.data != null && 'queue' in res.data.data) {\r\n if(res.data.data.queue.constructor !== Array) {\r\n res.data.data.queue = Object.values(res.data.data.queue);\r\n }\r\n }\r\n\r\n return res;\r\n}\r\n\r\n/**\r\n * Perform a migration task\r\n * @param {String} formId Id of the form element\r\n * \r\n * @returns {String} Id of the database record to be migrated\r\n */\r\nlet getNextMigrationID = function(formId) {\r\n let type = formId.replace(formIdTmpl + '-', '');\r\n let form = document.getElementById(formId);\r\n\r\n let migrateable = atob(form.querySelector('[name=\"migrateable\"]').value);\r\n migrateable = JSON.parse(migrateable);\r\n\r\n // Overwrite migrateable in list\r\n migrateablesList[type] = migrateable;\r\n\r\n // Loop through queue\r\n for (let id of migrateable.queue) {\r\n if (!(id in migrateable.successful) && !(id in migrateable.failed)) {\r\n migrateablesList[type]['currentID'] = id;\r\n break;\r\n }\r\n }\r\n\r\n return migrateablesList[type]['currentID'];\r\n}\r\n\r\n/**\r\n * Handle migration response\r\n * \r\n * @param {Object} response The response object in the form of\r\n * {success: true, status: 200, message: '', messages: {}, data: { {success, data, continue, error, debug, warning} }}\r\n * \r\n * @returns void\r\n */\r\nlet responseHandler = function(type, response) {\r\n if(response.success == false) {\r\n // Ajax request failed or server responded with error code\r\n addLog('Error in server response. We will try again. ('+tryCounter+'/'+tryLimit+')', type, 'info');\r\n addLog(response.message, type, 'error');\r\n addLog(response.messages, type, 'error');\r\n addLog(response.data.error, type, 'error');\r\n \r\n\r\n // Try again...\r\n }\r\n else {\r\n // Ajax request successful\r\n if(!response.data.success)\r\n {\r\n // Migration failed\r\n addLog('[Migrator.js] Migration of '+type+' with id = '+migrateablesList[type]['currentID']+' failed.', type, 'error');\r\n logMessages(type, response.data);\r\n\r\n // Stop autimatic continuation if requested from backend\r\n if(!response.data.continue || response.data.continue == null || response.data.continue == false) {\r\n console.log('Stop automatic continuation requested from backend');\r\n continueState = false;\r\n }\r\n\r\n // Update migrateables\r\n updateMigrateables(type, response.data);\r\n }\r\n else\r\n {\r\n // Save record successful\r\n logMessages(type, response.data);\r\n addLog('[Migrator.js] Migration of '+type+' with id = '+migrateablesList[type]['currentID']+' successful.', type, 'success');\r\n\r\n // Stop autimatic continuation if requested from backend\r\n if(!response.data.continue || response.data.continue == null || response.data.continue == false) {\r\n console.log('Stop automatic continuation requested from backend');\r\n continueState = false;\r\n }\r\n\r\n // Update migrateables\r\n updateMigrateables(type, response.data);\r\n\r\n // Reset tryCounter\r\n tryCounter = 0;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Add a message to the logging output and the console\r\n * \r\n * @param {Mixed} msg One or multiple messages to be added to the log\r\n * @param {String} type The type defining the logging output to use\r\n * @param {String} msgType The type of message (available: error, warning, success, info)\r\n * @param {Boolean} console True to add the message also to the console\r\n * @param {Boolean} newLine True to add the message on a new line\r\n * @param {Integer} marginTop Number of how much margin you want on the top of the message\r\n * \r\n * @returns void\r\n */\r\nlet addLog = function(msg, type, msgType, console=false, newLine=true, marginTop=0) {\r\n if(!Boolean(msg) || msg == null || msg == '') {\r\n // Message is empty. Do nothing\r\n return;\r\n } else if(typeof msg === 'string') {\r\n // Your message is a simple string\r\n let tmp_msg = '';\r\n\r\n // Test if your string a json string\r\n try {\r\n tmp_msg = JSON.parse(msg);\r\n } catch (e) {\r\n }\r\n\r\n // Convert string to array\r\n if(tmp_msg !== '') {\r\n // remove object properties 'error' and 'code' if existent\r\n if('error' in tmp_msg) {\r\n delete tmp_msg.error;\r\n }\r\n if('code' in tmp_msg) {\r\n delete tmp_msg.code;\r\n }\r\n msg = Object.values(tmp_msg);\r\n } else {\r\n msg = [msg];\r\n }\r\n } else if(typeof msg === 'object') {\r\n // Your message is an object. Convert to array\r\n msg = Object.values(msg);\r\n }\r\n\r\n // Get logging output element\r\n let logOutput = document.getElementById('logOutput-'+type);\r\n\r\n // Loop through all messages\r\n msg.forEach((message, i) => {\r\n // Print in console\r\n if(console) {\r\n console.log(message);\r\n }\r\n\r\n // Create element\r\n let line = null;\r\n if(newLine) {\r\n line = document.createElement('p');\r\n } else {\r\n line = document.createElement('span');\r\n }\r\n\r\n // Top margin to element\r\n marginTop = parseInt(marginTop);\r\n if(marginTop > 0) {\r\n line.classList.add('mt-'+String(marginTop));\r\n }\r\n\r\n // Add text color\r\n line.classList.add('color-'+msgType);\r\n \r\n // Add message to element\r\n let msgType_txt = msgType.toLocaleUpperCase();\r\n line.textContent = '['+Joomla.JText._(msgType_txt)+'] '+String(message);\r\n\r\n // Print into logging output\r\n logOutput.appendChild(line);\r\n });\r\n}\r\n\r\n/**\r\n * Clear the logging output\r\n *\r\n * @param {String} type The type defining the logging output to clear\r\n * \r\n * @returns void\r\n */\r\nlet clearLog = function(type) {\r\n // Get logging output element\r\n let logOutput = document.getElementById('logOutput-'+type);\r\n\r\n // clear\r\n logOutput.innerHTML = '';\r\n}\r\n\r\n/**\r\n * Output all available messages from the result object\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {Object} res The result object in the form of\r\n * {success: bool, data: mixed, continue: bool, error: string|array, debug: string|array, warning: string|array}\r\n * \r\n * @returns void\r\n */\r\nlet logMessages = function(type, res) {\r\n // Available message types: error, debug, warning\r\n let available = ['error', 'debug', 'warning'];\r\n let msgTypes = {'error': 'error', 'debug': 'info', 'warning': 'warning'};\r\n\r\n available.forEach((value, index) => {\r\n if(!res[value] || !Boolean(res.data) || res.data == null) {\r\n return;\r\n }\r\n\r\n addLog(res[value], type, msgTypes[value]);\r\n });\r\n}\r\n\r\n/**\r\n * Update migrateable input field, progress bar and badges\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {Object} res The result object in the form of\r\n * {success: bool, data: mixed, continue: bool, error: string|array, debug: string|array, warning: string|array}\r\n * \r\n * @returns void\r\n */\r\nlet updateMigrateables = function(type, res) {\r\n let formId = formIdTmpl + '-' + type;\r\n let form = document.getElementById(formId);\r\n\r\n if(!res.success && (!Boolean(res.data) || res.data == null || res.data == '')) {\r\n // Migration failed, but no data available in result\r\n\r\n // Create result data based on input field\r\n let migrateable = atob(form.querySelector('[name=\"migrateable\"]').value);\r\n res.data = JSON.parse(migrateable);\r\n\r\n // See: Joomgallery\\Component\\Joomgallery\\Administrator\\Model\\MigrationModel::migrate\r\n // Remove migrated primary key from queue\r\n res.data.queue = res.data.queue.filter(function(e) { return e !== migrateablesList[type]['currentID'] })\r\n\r\n // Add migrated primary key to failed object\r\n res.data.failed[migrateablesList[type]['currentID']] = res.message;\r\n }\r\n\r\n if(!Boolean(res.data.progress) || res.data.progress == null || res.data.progress == '') {\r\n // Update progress if not delivered with result object\r\n let total = res.data.queue.lenght + Object.keys(res.data.successful).length + Object.keys(res.data.failed).length;\r\n let finished = Object.keys(res.data.successful).length + Object.keys(res.data.failed).length;\r\n res.data.progress = Math.round((100 / total) * (finished));\r\n }\r\n\r\n // Get badges\r\n let queueBadge = document.getElementById('badgeQueue-'+type);\r\n let resBadge = document.getElementById('badgeSuccessful-'+type);\r\n if(!res.success) {\r\n resBadge = document.getElementById('badgeFailed-'+type);\r\n }\r\n\r\n // Update migrateable input field\r\n let field = form.querySelector('[name=\"migrateable\"]');\r\n field.value = btoa(JSON.stringify(res.data));\r\n\r\n // Update badges\r\n queueBadge.innerHTML = parseInt(queueBadge.innerHTML) - 1;\r\n resBadge.innerHTML = parseInt(resBadge.innerHTML) + 1;\r\n\r\n // Update progress bar\r\n let bar = document.getElementById('progress-'+type);\r\n bar.setAttribute('aria-valuenow', res.data.progress);\r\n bar.style.width = res.data.progress + '%';\r\n bar.innerText = res.data.progress + '%';\r\n}\r\n\r\n/**\r\n * Update GUI to start migration\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {DOM Element} button The button beeing pressed to start the task\r\n * \r\n * @returns void\r\n */\r\nlet startTask = function(type, button) {\r\n let bar = document.getElementById('progress-'+type);\r\n let startBtn = button;\r\n let stopBtn = document.getElementById('stopBtn-'+type);\r\n\r\n // Update progress bar\r\n bar.classList.remove('progress-bar-striped');\r\n bar.classList.remove('progress-bar-animated');\r\n \r\n // Disable start button\r\n startBtn.classList.add('disabled');\r\n startBtn.setAttribute('disabled', 'true');\r\n\r\n // Enable stop button\r\n stopBtn.classList.remove('disabled');\r\n stopBtn.removeAttribute('disabled');\r\n\r\n // Reinitialize variables\r\n tryCounter = 0;\r\n continueState = true;\r\n forceStop = false;\r\n}\r\n\r\n/**\r\n * Update GUI to end migration\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {DOM Element} button The button beeing pressed to start the task\r\n * \r\n * @returns void\r\n */\r\nlet finishTask = function(type, button) {\r\n let bar = document.getElementById('progress-'+type);\r\n let startBtn = button;\r\n let stopBtn = document.getElementById('stopBtn-'+type);\r\n\r\n // Update progress bar\r\n bar.classList.remove('progress-bar-striped');\r\n bar.classList.remove('progress-bar-animated');\r\n \r\n // Enable start button\r\n if(!migrateablesList[type]['complete']) {\r\n // Only enable start button if migration is not finished\r\n startBtn.classList.remove('disabled');\r\n startBtn.removeAttribute('disabled');\r\n }\r\n\r\n // Disable stop button\r\n stopBtn.classList.add('disabled');\r\n stopBtn.setAttribute('disabled', 'true');\r\n\r\n // If migration is completed\r\n if(migrateablesList[type]['complete']) {\r\n // Update next start button\r\n enableNextBtn(type, button);\r\n // Update step 4 button\r\n updateStep4Btn();\r\n }\r\n}\r\n\r\n/**\r\n * Enable start button of next migration content type\r\n * \r\n * @param {String} type The type defining the content type to be updated\r\n * @param {DOM Element} button The current start button\r\n * \r\n * @returns void\r\n */\r\nlet enableNextBtn = function(type, button) {\r\n let types_inputs = document.getElementsByName('type');\r\n let next_type = '';\r\n\r\n // Find next migration content type\r\n let this_type = false;\r\n types_inputs.forEach((type_input) => {\r\n if(this_type) {\r\n next_type = type_input.value;\r\n return;\r\n }\r\n if(Boolean(type_input.value) && type_input.value == type) {\r\n this_type = true;\r\n }\r\n });\r\n\r\n // Get next button\r\n let nextBtn = document.getElementById(buttonTmpl + '-' + next_type);\r\n\r\n // Enable button\r\n nextBtn.classList.remove('disabled');\r\n nextBtn.removeAttribute('disabled');\r\n}\r\n\r\n/**\r\n * Update button to go to step 4\r\n * \r\n * @returns void\r\n */\r\nlet updateStep4Btn = function() {\r\n let types_inputs = document.getElementsByName('type');\r\n let types = {};\r\n\r\n // Add all available migrateables to types object\r\n types_inputs.forEach((type) => {\r\n if(Boolean(type.value)) {\r\n types[type.value] = false;\r\n }\r\n });\r\n\r\n // Check if all migrateables are available and completed\r\n let tot_complete = true;\r\n Object.keys(types).forEach(type => { \r\n if(Boolean(migrateablesList[type])) {\r\n if(!migrateablesList[type]['completed'])\r\n {\r\n // Migrateable not yet completed\r\n tot_complete = false;\r\n }\r\n }\r\n else\r\n {\r\n // Migrateable does not yet exist. Thus not completed\r\n tot_complete = false;\r\n }\r\n });\r\n\r\n if(tot_complete) {\r\n // Enable step 4 button\r\n document.getElementById(step4Btn).classList.remove('disabled');\r\n document.getElementById(step4Btn).removeAttribute('disabled');\r\n }\r\n}"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/media/com_joomgallery/js/migrator/src/index.js b/media/com_joomgallery/js/migrator/src/index.js index 3502a10c..9aac5215 100644 --- a/media/com_joomgallery/js/migrator/src/index.js +++ b/media/com_joomgallery/js/migrator/src/index.js @@ -43,39 +43,34 @@ export let submitTask = function(event, element) { let formId = formIdTmpl + '-' + type; let task = element.parentNode.querySelector('[name="task"]').value; - startTask(type, element); + if(tryCounter == 0) { + startTask(type, element); + } - tryCounter++; + tryCounter = tryCounter + 1; ajax(formId, task) .then(res => { // Handle the successful result here responseHandler(type, res); - console.log('forceStop: ' + forceStop); - console.log('continueState: ' + continueState); - if(tryCounter >= tryLimit) { // We reached the limit of tries --> looks like we have a network problem updateMigrateables(type, {'success': false, 'message': Joomla.JText._('COM_JOOMGALLERY_ERROR_NETWORK_PROBLEM'), 'data': false}); // Stop automatic execution and update GUI - console.log('forceStop; We reached the limit of tries'); forceStop = true; } if(continueState && !forceStop) { // Kick off the next task - console.log('Kick off the next task'); submitTask(event, element); } else { // Stop automatic task execution and update GUI - console.log('Stop automatic task execution and update GUI'); finishTask(type, element); } }) .catch(error => { // Handle any errors here - console.error(error); addLog(error, type, 'error'); }); }; @@ -241,9 +236,12 @@ let getNextMigrationID = function(formId) { */ let responseHandler = function(type, response) { if(response.success == false) { - // Ajax request failed + // Ajax request failed or server responded with error code + addLog('Error in server response. We will try again. ('+tryCounter+'/'+tryLimit+')', type, 'info'); addLog(response.message, type, 'error'); addLog(response.messages, type, 'error'); + addLog(response.data.error, type, 'error'); + // Try again... } @@ -257,7 +255,7 @@ let responseHandler = function(type, response) { // Stop autimatic continuation if requested from backend if(!response.data.continue || response.data.continue == null || response.data.continue == false) { - console.log('continueState; autimatic continuation requested from backend'); + console.log('Stop automatic continuation requested from backend'); continueState = false; } @@ -272,7 +270,7 @@ let responseHandler = function(type, response) { // Stop autimatic continuation if requested from backend if(!response.data.continue || response.data.continue == null || response.data.continue == false) { - console.log('continueState; autimatic continuation requested from backend'); + console.log('Stop automatic continuation requested from backend'); continueState = false; } @@ -313,6 +311,13 @@ let addLog = function(msg, type, msgType, console=false, newLine=true, marginTop // Convert string to array if(tmp_msg !== '') { + // remove object properties 'error' and 'code' if existent + if('error' in tmp_msg) { + delete tmp_msg.error; + } + if('code' in tmp_msg) { + delete tmp_msg.code; + } msg = Object.values(tmp_msg); } else { msg = [msg]; @@ -327,7 +332,6 @@ let addLog = function(msg, type, msgType, console=false, newLine=true, marginTop // Loop through all messages msg.forEach((message, i) => { - // Print in console if(console) { console.log(message); From da433cb135ab55b417b53b18931c918f86b88539 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Wed, 3 Jan 2024 13:21:08 +0100 Subject: [PATCH 060/121] Update Jg3ToJg4.php --- .../com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index 879f2e08..f6ff7370 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -405,7 +405,7 @@ public function migrateFiles(ImageTable $img, array $data): bool public function migrateFolder(CategoryTable $cat, array $data): bool { // Create file manager service - $this->component->createFileManager() + $this->component->createFileManager(); if($this->params->get('image_usage', 1) == 0) { From 676af73960e4f03915d259c7f640a28c2c1f4dae Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Wed, 3 Jan 2024 15:51:20 +0100 Subject: [PATCH 061/121] Add prechecks for direct usage --- .../com_joomgallery.migration.Jg3ToJg4.ini | 8 +- .../Service/Migration/Scripts/Jg3ToJg4.php | 98 ++++++++++++++++++- 2 files changed, 101 insertions(+), 5 deletions(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini index a8b57d00..daccc4d6 100644 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini @@ -18,4 +18,10 @@ FILES_JOOMGALLERY_MIGRATION_CATIMAGE_TITLE="Adjustment: Category thumbnails" FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_TITLE="Image filenames" FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_DESC="There are different filenames for thumbnail and detail image defined in the database of your JG3 tables. Number of affected images: %s" FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_HELP="It was detected that for some images in the database table of the JG3 (source, #__com_joomgallery) different filenames are stored in the rows 'imgfilename' and 'imgthumbname'. This script could result in errorous migration results. We strongly advise to clean up the database manually before starting the migration. For all the affected Images you have to do the following:
  • Rename the thumbnail image in the filesystem to be the one set in the old JoomGallery image table at row 'imgfilename'.
  • In the old JoomGallery image table, set the value of the row 'imgthumbname' to be the same as in row 'imgfilename'.

ID's of affected images: (%s)

If you face any problems, call for help in our forum at https://www.forum.en.joomgalleryfriends.net" -FILES_JOOMGALLERY_MIGRATION_ERROR_TYPE_PREREQUIREMENT="Prerequirement for this content type is not fulfilled. Please make shure the following content types are completely migrated and none of them failed." \ No newline at end of file +FILES_JOOMGALLERY_MIGRATION_ERROR_TYPE_PREREQUIREMENT="Prerequirement for this content type is not fulfilled. Please make shure the following content types are completely migrated and none of them failed." +FILES_JOOMGALLERY_SERVICE_MIGRATION_DIRECT_USAGE_ERROR="Its not possible to directly use images, if source is outside this Joomla! installation. You can not set 'Same Joomla! installation' to yes and 'Image usage' to 'Direct usage' together in step 1." +FILES_JOOMGALLERY_SERVICE_MIGRATION_DIRECT_USAGE_IMGTYPES_ERROR="Direct usage of images is only possible with the three default imagetypes (original, detail, thumbnail) activated. Please remove all other imagetypes from the JoomGallery configuration." +FILES_JOOMGALLERY_SERVICE_MIGRATION_DIRECT_USAGE_LOCAL_ERROR="Direct usage of images is only possible when using the local filesystem. Please switch to the local filesystem in the JoomGallery configuration if you want to use 'Direct usage'." +FILES_JOOMGALLERY_SERVICE_MIGRATION_DIRECT_USAGE_ORIGINAL_WARNING="You have chosen direct usage of images in step 1. Please make sure the 'original' imagetype is deactivated if it was deactivated in your source/previous Joomla version." +FILES_JOOMGALLERY_MIGRATION_CHECK_CATEGORY_CATPATH="Category paths" +FILES_JOOMGALLERY_MIGRATION_CHECK_CATEGORY_CATPATH_DESC="There are inconsistent category folder paths defined in the database of your JG3 tables. Please adjust category paths manually, otherwise automatic migration is not possible. ID's of affected categories: %s" \ No newline at end of file diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index f6ff7370..220a45fa 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -673,11 +673,14 @@ public function scriptSpecificChecks(string $type, Checks &$checks, string $cate { if($type == 'pre') { - // Check if imgfilename and imgthumbname are the same - list($db, $dbPrefix) = $this->getDB('source'); - list($tablename, $pkname) = $this->getSourceTableInfo('image'); + // Get source db info + list($db, $dbPrefix) = $this->getDB('source'); + list($tablename, $pkname) = $this->getSourceTableInfo('image'); + list($cattablename, $catpkname) = $this->getSourceTableInfo('category'); + + //------------------------ - // Create the query + // Check if imgfilename and imgthumbname are the same $query = $db->getQuery(true) ->select($db->quoteName(array('id'))) ->from($tablename) @@ -691,6 +694,70 @@ public function scriptSpecificChecks(string $type, Checks &$checks, string $cate { $checks->addCheck($category, 'src_table_image_filename', true, true, Text::_('FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_TITLE'), Text::sprintf('FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_DESC', \count($res)), Text::sprintf('FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_HELP', \implode(', ', $res))); } + + //------------------------ + + // Check catpath of JG3 category table if they are consistent + if($this->params->get('same_joomla', 1) == 1) + { + $query = $db->getQuery(true) + ->select($db->quoteName(array('cid', 'alias', 'catpath'))) + ->from($cattablename) + ->where($db->quoteName('level') . ' > 0 '); + $db->setQuery($query); + + // Load a list of category objects + $cats = $db->loadObjectList(); + + // Check them for inconsistency + $inconsistent = array(); + foreach($cats as $key => $cat) + { + if(!$this->checkCatpath($cat)) + { + \array_push($inconsistent, $cat->cid); + } + } + + if(\count($inconsistent) > 0) + { + $checks->addCheck($category, 'src_table_cat_path', false, false, Text::_('FILES_JOOMGALLERY_MIGRATION_CHECK_CATEGORY_CATPATH'), Text::sprintf('FILES_JOOMGALLERY_MIGRATION_CHECK_CATEGORY_CATPATH_DESC', \implode(', ', $inconsistent))); + } + } + + //------------------------ + + // Check use case: Direct usage + if($this->params->get('image_usage', 1) == 0) + { + if($this->params->get('same_joomla', 1) == 0) + { + // Direct usage is not possible when source is outside this joomla installation + $checks->addCheck($category, 'direct_usage_joomla', false, false, Text::_('COM_JOOMGALLERY_DIRECT_USAGE'), Text::_('FILES_JOOMGALLERY_SERVICE_MIGRATION_DIRECT_USAGE_ERROR')); + } + else + { + $dest_imagetypes = JoomHelper::getRecords('imagetypes', $this->component); + + if(\count($dest_imagetypes) !== 3) + { + // Direct usage only possible with the three standard imagetypes + $checks->addCheck($category, 'direct_usage_imgtypes', false, false, Text::_('COM_JOOMGALLERY_DIRECT_USAGE'), Text::_('FILES_JOOMGALLERY_SERVICE_MIGRATION_DIRECT_USAGE_IMGTYPES_ERROR')); + } + else + { + // Make sure that original is deactivated is it was the case in JG3 + $checks->addCheck($category, 'direct_usage_orig', true, true, Text::_('COM_JOOMGALLERY_DIRECT_USAGE'), Text::_('FILES_JOOMGALLERY_SERVICE_MIGRATION_DIRECT_USAGE_ORIGINAL_WARNING')); + } + + $this->component->createConfig(); + if($this->component->getConfig()->get('jg_filesystem') !== 'local-images') + { + // Direct usage is only possible with local filesystem + $checks->addCheck($category, 'direct_usage_local', false, false, Text::_('COM_JOOMGALLERY_DIRECT_USAGE'), Text::_('FILES_JOOMGALLERY_SERVICE_MIGRATION_DIRECT_USAGE_LOCAL_ERROR')); + } + } + } } if($type == 'post') @@ -700,4 +767,27 @@ public function scriptSpecificChecks(string $type, Checks &$checks, string $cate return; } + + /** + * Check if catpath is correct due to scheme 'parent-path/alias_cid'. + * + * @param \stdClass $cat Category object + * + * @return bool True if catpath is correct, false otherwise + * + * @since 4.0.0 + */ + protected function checkCatpath(\stdClass $cat): bool + { + $cat->catpath = \str_replace(\DIRECTORY_SEPARATOR, '/', $cat->catpath); + $catpath_arr = \explode('/', $cat->catpath); + $catpath = \end($catpath_arr); + + if($catpath !== $cat->alias.'_'.$cat->cid) + { + return false; + } + + return true; + } } \ No newline at end of file From fc861397f9ed6d747ed8e2a044b8fa65c8359d75 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Wed, 3 Jan 2024 16:49:04 +0100 Subject: [PATCH 062/121] fix issue: activate next button --- .../js/migrator/dist/migrator.js | 22 +++++++++++-------- .../js/migrator/dist/migrator.js.map | 2 +- .../com_joomgallery/js/migrator/src/index.js | 22 +++++++++++-------- 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/media/com_joomgallery/js/migrator/dist/migrator.js b/media/com_joomgallery/js/migrator/dist/migrator.js index a05ea6e2..17d29987 100644 --- a/media/com_joomgallery/js/migrator/dist/migrator.js +++ b/media/com_joomgallery/js/migrator/dist/migrator.js @@ -112,7 +112,7 @@ let submitTask = function(event, element) { submitTask(event, element); } else { // Stop automatic task execution and update GUI - finishTask(type, element); + finishTask(type, element, formId); } }) .catch(error => { @@ -518,8 +518,8 @@ let startTask = function(type, button) { let stopBtn = document.getElementById('stopBtn-'+type); // Update progress bar - bar.classList.remove('progress-bar-striped'); - bar.classList.remove('progress-bar-animated'); + bar.classList.add('progress-bar-striped'); + bar.classList.add('progress-bar-animated'); // Disable start button startBtn.classList.add('disabled'); @@ -540,20 +540,24 @@ let startTask = function(type, button) { * * @param {String} type The type defining the content type to be updated * @param {DOM Element} button The button beeing pressed to start the task + * @param {String} formId Id of the form element * * @returns void */ -let finishTask = function(type, button) { +let finishTask = function(type, button, formId) { let bar = document.getElementById('progress-'+type); let startBtn = button; let stopBtn = document.getElementById('stopBtn-'+type); + // Update migrateablesList + getNextMigrationID(formId); + // Update progress bar bar.classList.remove('progress-bar-striped'); bar.classList.remove('progress-bar-animated'); // Enable start button - if(!migrateablesList[type]['complete']) { + if(!migrateablesList[type]['completed']) { // Only enable start button if migration is not finished startBtn.classList.remove('disabled'); startBtn.removeAttribute('disabled'); @@ -564,7 +568,7 @@ let finishTask = function(type, button) { stopBtn.setAttribute('disabled', 'true'); // If migration is completed - if(migrateablesList[type]['complete']) { + if(migrateablesList[type]['completed']) { // Update next start button enableNextBtn(type, button); // Update step 4 button @@ -586,15 +590,15 @@ let enableNextBtn = function(type, button) { // Find next migration content type let this_type = false; - types_inputs.forEach((type_input) => { + for (const type_input of types_inputs) { if(this_type) { next_type = type_input.value; - return; + break; } if(Boolean(type_input.value) && type_input.value == type) { this_type = true; } - }); + } // Get next button let nextBtn = document.getElementById(buttonTmpl + '-' + next_type); diff --git a/media/com_joomgallery/js/migrator/dist/migrator.js.map b/media/com_joomgallery/js/migrator/dist/migrator.js.map index 8d30ef36..d56128e0 100644 --- a/media/com_joomgallery/js/migrator/dist/migrator.js.map +++ b/media/com_joomgallery/js/migrator/dist/migrator.js.map @@ -1 +1 @@ -{"version":3,"file":"migrator.js","mappings":";;;UAAA;UACA;;;;;WCDA;WACA;WACA;WACA;WACA,yCAAyC,wCAAwC;WACjF;WACA;WACA;;;;;WCPA;;;;;WCAA;WACA;WACA;WACA,uDAAuD,iBAAiB;WACxE;WACA,gDAAgD,aAAa;WAC7D;;;;;;;;;;;;;;ACNA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,SAAS;AAClB;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kCAAkC,oGAAoG;AACtI;AACA;AACA;AACA;AACA;AACA;AACA;AACA,QAAQ;AACR;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kFAAkF,gBAAgB;AAClG;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB;AACA,aAAa,UAAU;AACvB,aAAa,qDAAqD,aAAa;AAC/E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,gFAAgF,SAAS;AACrG;AACA;AACA,sBAAsB;AACtB;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA,WAAW,mFAAmF,SAAS;AACvG,IAAI;AACJ;AACA,8BAA8B;AAC9B,6BAA6B;AAC7B;AACA,WAAW;AACX;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB;AACA,aAAa,UAAU;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,qDAAqD,WAAW;AAC7E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB,YAAY,UAAU;AACtB,cAAc;AACd;AACA;AACA;AACA;AACA;AACA;AACA,kBAAkB;AAClB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB,YAAY,UAAU;AACtB,cAAc;AACd;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,yDAAyD,kDAAkD;AAC3G;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA,C","sources":["webpack://Migrator/webpack/bootstrap","webpack://Migrator/webpack/runtime/define property getters","webpack://Migrator/webpack/runtime/hasOwnProperty shorthand","webpack://Migrator/webpack/runtime/make namespace object","webpack://Migrator/./src/index.js"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","// Selectors used by this script\r\nlet typeSelector = 'data-type';\r\nlet formIdTmpl = 'migrationForm';\r\nlet buttonTmpl = 'migrationBtn';\r\nlet step4Btn = 'step4Btn';\r\nlet tryLimit = 3;\r\n\r\n/**\r\n * Storage for migrateables\r\n * @var {Object} migrateablesList\r\n */\r\nvar migrateablesList = {};\r\n\r\n/**\r\n * Counter of how many times the same migration was tried to perfrom\r\n * @var {Integer} tryCounter\r\n */\r\nvar tryCounter = 0;\r\n\r\n/**\r\n * State. As long as this state is set to true, the migration will be\r\n * continued automatically regarding the pending queue in the migrateablesList.\r\n * @var {Boolean} continueState\r\n */\r\nvar continueState = true;\r\n\r\n/**\r\n * State. Set this state to true to stop automatic execution as soon as the next ajax respond comes back.\r\n * @var {Boolean} forceStop\r\n */\r\nvar forceStop = false;\r\n\r\n/**\r\n * Submit the migration task by pressing the button\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let submitTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n let type = element.getAttribute(typeSelector);\r\n let formId = formIdTmpl + '-' + type;\r\n let task = element.parentNode.querySelector('[name=\"task\"]').value;\r\n\r\n if(tryCounter == 0) {\r\n startTask(type, element);\r\n } \r\n\r\n tryCounter = tryCounter + 1;\r\n\r\n ajax(formId, task)\r\n .then(res => {\r\n // Handle the successful result here\r\n responseHandler(type, res);\r\n\r\n if(tryCounter >= tryLimit) {\r\n // We reached the limit of tries --> looks like we have a network problem\r\n updateMigrateables(type, {'success': false, 'message': Joomla.JText._('COM_JOOMGALLERY_ERROR_NETWORK_PROBLEM'), 'data': false});\r\n // Stop automatic execution and update GUI\r\n forceStop = true;\r\n }\r\n \r\n if(continueState && !forceStop) {\r\n // Kick off the next task\r\n submitTask(event, element);\r\n } else {\r\n // Stop automatic task execution and update GUI\r\n finishTask(type, element);\r\n }\r\n })\r\n .catch(error => {\r\n // Handle any errors here\r\n addLog(error, type, 'error');\r\n });\r\n};\r\n\r\n/**\r\n * Stop the migration task by pressing the button\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let stopTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n let type = element.getAttribute(typeSelector);\r\n let bar = document.getElementById('progress-'+type);\r\n let startBtn = document.getElementById('migrationBtn-'+type);\r\n let stopBtn = element;\r\n\r\n // Force automatic execution to stop\r\n forceStop = true;\r\n\r\n // Update progress bar\r\n bar.classList.remove('progress-bar-striped');\r\n bar.classList.remove('progress-bar-animated');\r\n \r\n // Enable start button\r\n startBtn.classList.remove('disabled');\r\n startBtn.removeAttribute('disabled');\r\n\r\n // Disable stop button\r\n stopBtn.classList.add('disabled');\r\n stopBtn.setAttribute('disabled', 'true');\r\n}\r\n\r\n/**\r\n * Manually set one record migration to true\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let repairTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n // Get relevant elements\r\n let type = element.getAttribute(typeSelector);\r\n let mig = document.getElementById('migrationForm-'+type).querySelector('[name=\"migrateable\"]');\r\n let inputType = document.getElementById('migrepairForm').querySelector('[name=\"type\"]');\r\n let inputMig = document.getElementById('migrepairForm').querySelector('[name=\"migrateable\"]');\r\n\r\n // Fill input values\r\n inputType.value = type;\r\n inputMig.value = mig.value;\r\n\r\n // Show modal\r\n let bsmodal = new bootstrap.Modal(document.getElementById('repair-modal-box'), {keyboard: false});\r\n bsmodal.show();\r\n}\r\n\r\n/**\r\n * Perform an ajax request in json format\r\n * \r\n * @param {String} formId Id of the form element\r\n * @param {String} task Name of the task\r\n * \r\n * @returns {Object} Result object\r\n * {success: true, status: 200, message: '', messages: {}, data: { { {success, data, continue, error, debug, warning} }}\r\n */\r\nlet ajax = async function(formId, task) {\r\n\r\n // Catch form and data\r\n let formData = new FormData(document.getElementById(formId));\r\n formData.append('format', 'json');\r\n\r\n if(task == 'migration.start') {\r\n formData.append('id', getNextMigrationID(formId));\r\n }\r\n\r\n // Set request parameters\r\n let parameters = {\r\n method: 'POST',\r\n mode: 'same-origin',\r\n cache: 'default',\r\n redirect: 'follow',\r\n referrerPolicy: 'no-referrer-when-downgrade',\r\n body: formData,\r\n };\r\n\r\n // Set the url\r\n let url = document.getElementById(formId).getAttribute('action');\r\n\r\n // Perform the fetch request\r\n let response = await fetch(url, parameters);\r\n\r\n // Resolve promise as text string\r\n let txt = await response.text();\r\n let res = null;\r\n\r\n if (!response.ok) {\r\n // Catch network error\r\n return {success: false, status: response.status, message: response.message, messages: {}, data: {error: txt, data:null}};\r\n }\r\n\r\n if(txt.startsWith('{\"success\"')) {\r\n // Response is of type json --> everything fine\r\n res = JSON.parse(txt);\r\n res.status = response.status;\r\n res.data = JSON.parse(res.data);\r\n } else if (txt.includes('Fatal error')) {\r\n // PHP fatal error occurred\r\n res = {success: false, status: response.status, message: response.statusText, messages: {}, data: {error: txt, data:null}};\r\n } else {\r\n // Response is not of type json --> probably some php warnings/notices\r\n let split = txt.split('\\n{\"');\r\n let temp = JSON.parse('{\"'+split[1]);\r\n let data = JSON.parse(temp.data);\r\n res = {success: true, status: response.status, message: split[0], messages: temp.messages, data: data};\r\n }\r\n\r\n // Make sure res.data.data.queue is of type array\r\n if(typeof res.data.data != \"undefined\" && res.data.data != null && 'queue' in res.data.data) {\r\n if(res.data.data.queue.constructor !== Array) {\r\n res.data.data.queue = Object.values(res.data.data.queue);\r\n }\r\n }\r\n\r\n return res;\r\n}\r\n\r\n/**\r\n * Perform a migration task\r\n * @param {String} formId Id of the form element\r\n * \r\n * @returns {String} Id of the database record to be migrated\r\n */\r\nlet getNextMigrationID = function(formId) {\r\n let type = formId.replace(formIdTmpl + '-', '');\r\n let form = document.getElementById(formId);\r\n\r\n let migrateable = atob(form.querySelector('[name=\"migrateable\"]').value);\r\n migrateable = JSON.parse(migrateable);\r\n\r\n // Overwrite migrateable in list\r\n migrateablesList[type] = migrateable;\r\n\r\n // Loop through queue\r\n for (let id of migrateable.queue) {\r\n if (!(id in migrateable.successful) && !(id in migrateable.failed)) {\r\n migrateablesList[type]['currentID'] = id;\r\n break;\r\n }\r\n }\r\n\r\n return migrateablesList[type]['currentID'];\r\n}\r\n\r\n/**\r\n * Handle migration response\r\n * \r\n * @param {Object} response The response object in the form of\r\n * {success: true, status: 200, message: '', messages: {}, data: { {success, data, continue, error, debug, warning} }}\r\n * \r\n * @returns void\r\n */\r\nlet responseHandler = function(type, response) {\r\n if(response.success == false) {\r\n // Ajax request failed or server responded with error code\r\n addLog('Error in server response. We will try again. ('+tryCounter+'/'+tryLimit+')', type, 'info');\r\n addLog(response.message, type, 'error');\r\n addLog(response.messages, type, 'error');\r\n addLog(response.data.error, type, 'error');\r\n \r\n\r\n // Try again...\r\n }\r\n else {\r\n // Ajax request successful\r\n if(!response.data.success)\r\n {\r\n // Migration failed\r\n addLog('[Migrator.js] Migration of '+type+' with id = '+migrateablesList[type]['currentID']+' failed.', type, 'error');\r\n logMessages(type, response.data);\r\n\r\n // Stop autimatic continuation if requested from backend\r\n if(!response.data.continue || response.data.continue == null || response.data.continue == false) {\r\n console.log('Stop automatic continuation requested from backend');\r\n continueState = false;\r\n }\r\n\r\n // Update migrateables\r\n updateMigrateables(type, response.data);\r\n }\r\n else\r\n {\r\n // Save record successful\r\n logMessages(type, response.data);\r\n addLog('[Migrator.js] Migration of '+type+' with id = '+migrateablesList[type]['currentID']+' successful.', type, 'success');\r\n\r\n // Stop autimatic continuation if requested from backend\r\n if(!response.data.continue || response.data.continue == null || response.data.continue == false) {\r\n console.log('Stop automatic continuation requested from backend');\r\n continueState = false;\r\n }\r\n\r\n // Update migrateables\r\n updateMigrateables(type, response.data);\r\n\r\n // Reset tryCounter\r\n tryCounter = 0;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Add a message to the logging output and the console\r\n * \r\n * @param {Mixed} msg One or multiple messages to be added to the log\r\n * @param {String} type The type defining the logging output to use\r\n * @param {String} msgType The type of message (available: error, warning, success, info)\r\n * @param {Boolean} console True to add the message also to the console\r\n * @param {Boolean} newLine True to add the message on a new line\r\n * @param {Integer} marginTop Number of how much margin you want on the top of the message\r\n * \r\n * @returns void\r\n */\r\nlet addLog = function(msg, type, msgType, console=false, newLine=true, marginTop=0) {\r\n if(!Boolean(msg) || msg == null || msg == '') {\r\n // Message is empty. Do nothing\r\n return;\r\n } else if(typeof msg === 'string') {\r\n // Your message is a simple string\r\n let tmp_msg = '';\r\n\r\n // Test if your string a json string\r\n try {\r\n tmp_msg = JSON.parse(msg);\r\n } catch (e) {\r\n }\r\n\r\n // Convert string to array\r\n if(tmp_msg !== '') {\r\n // remove object properties 'error' and 'code' if existent\r\n if('error' in tmp_msg) {\r\n delete tmp_msg.error;\r\n }\r\n if('code' in tmp_msg) {\r\n delete tmp_msg.code;\r\n }\r\n msg = Object.values(tmp_msg);\r\n } else {\r\n msg = [msg];\r\n }\r\n } else if(typeof msg === 'object') {\r\n // Your message is an object. Convert to array\r\n msg = Object.values(msg);\r\n }\r\n\r\n // Get logging output element\r\n let logOutput = document.getElementById('logOutput-'+type);\r\n\r\n // Loop through all messages\r\n msg.forEach((message, i) => {\r\n // Print in console\r\n if(console) {\r\n console.log(message);\r\n }\r\n\r\n // Create element\r\n let line = null;\r\n if(newLine) {\r\n line = document.createElement('p');\r\n } else {\r\n line = document.createElement('span');\r\n }\r\n\r\n // Top margin to element\r\n marginTop = parseInt(marginTop);\r\n if(marginTop > 0) {\r\n line.classList.add('mt-'+String(marginTop));\r\n }\r\n\r\n // Add text color\r\n line.classList.add('color-'+msgType);\r\n \r\n // Add message to element\r\n let msgType_txt = msgType.toLocaleUpperCase();\r\n line.textContent = '['+Joomla.JText._(msgType_txt)+'] '+String(message);\r\n\r\n // Print into logging output\r\n logOutput.appendChild(line);\r\n });\r\n}\r\n\r\n/**\r\n * Clear the logging output\r\n *\r\n * @param {String} type The type defining the logging output to clear\r\n * \r\n * @returns void\r\n */\r\nlet clearLog = function(type) {\r\n // Get logging output element\r\n let logOutput = document.getElementById('logOutput-'+type);\r\n\r\n // clear\r\n logOutput.innerHTML = '';\r\n}\r\n\r\n/**\r\n * Output all available messages from the result object\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {Object} res The result object in the form of\r\n * {success: bool, data: mixed, continue: bool, error: string|array, debug: string|array, warning: string|array}\r\n * \r\n * @returns void\r\n */\r\nlet logMessages = function(type, res) {\r\n // Available message types: error, debug, warning\r\n let available = ['error', 'debug', 'warning'];\r\n let msgTypes = {'error': 'error', 'debug': 'info', 'warning': 'warning'};\r\n\r\n available.forEach((value, index) => {\r\n if(!res[value] || !Boolean(res.data) || res.data == null) {\r\n return;\r\n }\r\n\r\n addLog(res[value], type, msgTypes[value]);\r\n });\r\n}\r\n\r\n/**\r\n * Update migrateable input field, progress bar and badges\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {Object} res The result object in the form of\r\n * {success: bool, data: mixed, continue: bool, error: string|array, debug: string|array, warning: string|array}\r\n * \r\n * @returns void\r\n */\r\nlet updateMigrateables = function(type, res) {\r\n let formId = formIdTmpl + '-' + type;\r\n let form = document.getElementById(formId);\r\n\r\n if(!res.success && (!Boolean(res.data) || res.data == null || res.data == '')) {\r\n // Migration failed, but no data available in result\r\n\r\n // Create result data based on input field\r\n let migrateable = atob(form.querySelector('[name=\"migrateable\"]').value);\r\n res.data = JSON.parse(migrateable);\r\n\r\n // See: Joomgallery\\Component\\Joomgallery\\Administrator\\Model\\MigrationModel::migrate\r\n // Remove migrated primary key from queue\r\n res.data.queue = res.data.queue.filter(function(e) { return e !== migrateablesList[type]['currentID'] })\r\n\r\n // Add migrated primary key to failed object\r\n res.data.failed[migrateablesList[type]['currentID']] = res.message;\r\n }\r\n\r\n if(!Boolean(res.data.progress) || res.data.progress == null || res.data.progress == '') {\r\n // Update progress if not delivered with result object\r\n let total = res.data.queue.lenght + Object.keys(res.data.successful).length + Object.keys(res.data.failed).length;\r\n let finished = Object.keys(res.data.successful).length + Object.keys(res.data.failed).length;\r\n res.data.progress = Math.round((100 / total) * (finished));\r\n }\r\n\r\n // Get badges\r\n let queueBadge = document.getElementById('badgeQueue-'+type);\r\n let resBadge = document.getElementById('badgeSuccessful-'+type);\r\n if(!res.success) {\r\n resBadge = document.getElementById('badgeFailed-'+type);\r\n }\r\n\r\n // Update migrateable input field\r\n let field = form.querySelector('[name=\"migrateable\"]');\r\n field.value = btoa(JSON.stringify(res.data));\r\n\r\n // Update badges\r\n queueBadge.innerHTML = parseInt(queueBadge.innerHTML) - 1;\r\n resBadge.innerHTML = parseInt(resBadge.innerHTML) + 1;\r\n\r\n // Update progress bar\r\n let bar = document.getElementById('progress-'+type);\r\n bar.setAttribute('aria-valuenow', res.data.progress);\r\n bar.style.width = res.data.progress + '%';\r\n bar.innerText = res.data.progress + '%';\r\n}\r\n\r\n/**\r\n * Update GUI to start migration\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {DOM Element} button The button beeing pressed to start the task\r\n * \r\n * @returns void\r\n */\r\nlet startTask = function(type, button) {\r\n let bar = document.getElementById('progress-'+type);\r\n let startBtn = button;\r\n let stopBtn = document.getElementById('stopBtn-'+type);\r\n\r\n // Update progress bar\r\n bar.classList.remove('progress-bar-striped');\r\n bar.classList.remove('progress-bar-animated');\r\n \r\n // Disable start button\r\n startBtn.classList.add('disabled');\r\n startBtn.setAttribute('disabled', 'true');\r\n\r\n // Enable stop button\r\n stopBtn.classList.remove('disabled');\r\n stopBtn.removeAttribute('disabled');\r\n\r\n // Reinitialize variables\r\n tryCounter = 0;\r\n continueState = true;\r\n forceStop = false;\r\n}\r\n\r\n/**\r\n * Update GUI to end migration\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {DOM Element} button The button beeing pressed to start the task\r\n * \r\n * @returns void\r\n */\r\nlet finishTask = function(type, button) {\r\n let bar = document.getElementById('progress-'+type);\r\n let startBtn = button;\r\n let stopBtn = document.getElementById('stopBtn-'+type);\r\n\r\n // Update progress bar\r\n bar.classList.remove('progress-bar-striped');\r\n bar.classList.remove('progress-bar-animated');\r\n \r\n // Enable start button\r\n if(!migrateablesList[type]['complete']) {\r\n // Only enable start button if migration is not finished\r\n startBtn.classList.remove('disabled');\r\n startBtn.removeAttribute('disabled');\r\n }\r\n\r\n // Disable stop button\r\n stopBtn.classList.add('disabled');\r\n stopBtn.setAttribute('disabled', 'true');\r\n\r\n // If migration is completed\r\n if(migrateablesList[type]['complete']) {\r\n // Update next start button\r\n enableNextBtn(type, button);\r\n // Update step 4 button\r\n updateStep4Btn();\r\n }\r\n}\r\n\r\n/**\r\n * Enable start button of next migration content type\r\n * \r\n * @param {String} type The type defining the content type to be updated\r\n * @param {DOM Element} button The current start button\r\n * \r\n * @returns void\r\n */\r\nlet enableNextBtn = function(type, button) {\r\n let types_inputs = document.getElementsByName('type');\r\n let next_type = '';\r\n\r\n // Find next migration content type\r\n let this_type = false;\r\n types_inputs.forEach((type_input) => {\r\n if(this_type) {\r\n next_type = type_input.value;\r\n return;\r\n }\r\n if(Boolean(type_input.value) && type_input.value == type) {\r\n this_type = true;\r\n }\r\n });\r\n\r\n // Get next button\r\n let nextBtn = document.getElementById(buttonTmpl + '-' + next_type);\r\n\r\n // Enable button\r\n nextBtn.classList.remove('disabled');\r\n nextBtn.removeAttribute('disabled');\r\n}\r\n\r\n/**\r\n * Update button to go to step 4\r\n * \r\n * @returns void\r\n */\r\nlet updateStep4Btn = function() {\r\n let types_inputs = document.getElementsByName('type');\r\n let types = {};\r\n\r\n // Add all available migrateables to types object\r\n types_inputs.forEach((type) => {\r\n if(Boolean(type.value)) {\r\n types[type.value] = false;\r\n }\r\n });\r\n\r\n // Check if all migrateables are available and completed\r\n let tot_complete = true;\r\n Object.keys(types).forEach(type => { \r\n if(Boolean(migrateablesList[type])) {\r\n if(!migrateablesList[type]['completed'])\r\n {\r\n // Migrateable not yet completed\r\n tot_complete = false;\r\n }\r\n }\r\n else\r\n {\r\n // Migrateable does not yet exist. Thus not completed\r\n tot_complete = false;\r\n }\r\n });\r\n\r\n if(tot_complete) {\r\n // Enable step 4 button\r\n document.getElementById(step4Btn).classList.remove('disabled');\r\n document.getElementById(step4Btn).removeAttribute('disabled');\r\n }\r\n}"],"names":[],"sourceRoot":""} \ No newline at end of file +{"version":3,"file":"migrator.js","mappings":";;;UAAA;UACA;;;;;WCDA;WACA;WACA;WACA;WACA,yCAAyC,wCAAwC;WACjF;WACA;WACA;;;;;WCPA;;;;;WCAA;WACA;WACA;WACA,uDAAuD,iBAAiB;WACxE;WACA,gDAAgD,aAAa;WAC7D;;;;;;;;;;;;;;ACNA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,SAAS;AAClB;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kCAAkC,oGAAoG;AACtI;AACA;AACA;AACA;AACA;AACA;AACA;AACA,QAAQ;AACR;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kFAAkF,gBAAgB;AAClG;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB;AACA,aAAa,UAAU;AACvB,aAAa,qDAAqD,aAAa;AAC/E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,gFAAgF,SAAS;AACrG;AACA;AACA,sBAAsB;AACtB;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA,WAAW,mFAAmF,SAAS;AACvG,IAAI;AACJ;AACA,8BAA8B;AAC9B,6BAA6B;AAC7B;AACA,WAAW;AACX;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB;AACA,aAAa,UAAU;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,qDAAqD,WAAW;AAC7E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB,YAAY,UAAU;AACtB,cAAc;AACd;AACA;AACA;AACA;AACA;AACA;AACA,kBAAkB;AAClB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB,YAAY,UAAU;AACtB,cAAc;AACd;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,yDAAyD,kDAAkD;AAC3G;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA,C","sources":["webpack://Migrator/webpack/bootstrap","webpack://Migrator/webpack/runtime/define property getters","webpack://Migrator/webpack/runtime/hasOwnProperty shorthand","webpack://Migrator/webpack/runtime/make namespace object","webpack://Migrator/./src/index.js"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","// Selectors used by this script\r\nlet typeSelector = 'data-type';\r\nlet formIdTmpl = 'migrationForm';\r\nlet buttonTmpl = 'migrationBtn';\r\nlet step4Btn = 'step4Btn';\r\nlet tryLimit = 3;\r\n\r\n/**\r\n * Storage for migrateables\r\n * @var {Object} migrateablesList\r\n */\r\nvar migrateablesList = {};\r\n\r\n/**\r\n * Counter of how many times the same migration was tried to perfrom\r\n * @var {Integer} tryCounter\r\n */\r\nvar tryCounter = 0;\r\n\r\n/**\r\n * State. As long as this state is set to true, the migration will be\r\n * continued automatically regarding the pending queue in the migrateablesList.\r\n * @var {Boolean} continueState\r\n */\r\nvar continueState = true;\r\n\r\n/**\r\n * State. Set this state to true to stop automatic execution as soon as the next ajax respond comes back.\r\n * @var {Boolean} forceStop\r\n */\r\nvar forceStop = false;\r\n\r\n/**\r\n * Submit the migration task by pressing the button\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let submitTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n let type = element.getAttribute(typeSelector);\r\n let formId = formIdTmpl + '-' + type;\r\n let task = element.parentNode.querySelector('[name=\"task\"]').value;\r\n\r\n if(tryCounter == 0) {\r\n startTask(type, element);\r\n } \r\n\r\n tryCounter = tryCounter + 1;\r\n\r\n ajax(formId, task)\r\n .then(res => {\r\n // Handle the successful result here\r\n responseHandler(type, res);\r\n\r\n if(tryCounter >= tryLimit) {\r\n // We reached the limit of tries --> looks like we have a network problem\r\n updateMigrateables(type, {'success': false, 'message': Joomla.JText._('COM_JOOMGALLERY_ERROR_NETWORK_PROBLEM'), 'data': false});\r\n // Stop automatic execution and update GUI\r\n forceStop = true;\r\n }\r\n \r\n if(continueState && !forceStop) {\r\n // Kick off the next task\r\n submitTask(event, element);\r\n } else {\r\n // Stop automatic task execution and update GUI\r\n finishTask(type, element, formId);\r\n }\r\n })\r\n .catch(error => {\r\n // Handle any errors here\r\n addLog(error, type, 'error');\r\n });\r\n};\r\n\r\n/**\r\n * Stop the migration task by pressing the button\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let stopTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n let type = element.getAttribute(typeSelector);\r\n let bar = document.getElementById('progress-'+type);\r\n let startBtn = document.getElementById('migrationBtn-'+type);\r\n let stopBtn = element;\r\n\r\n // Force automatic execution to stop\r\n forceStop = true;\r\n\r\n // Update progress bar\r\n bar.classList.remove('progress-bar-striped');\r\n bar.classList.remove('progress-bar-animated');\r\n \r\n // Enable start button\r\n startBtn.classList.remove('disabled');\r\n startBtn.removeAttribute('disabled');\r\n\r\n // Disable stop button\r\n stopBtn.classList.add('disabled');\r\n stopBtn.setAttribute('disabled', 'true');\r\n}\r\n\r\n/**\r\n * Manually set one record migration to true\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let repairTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n // Get relevant elements\r\n let type = element.getAttribute(typeSelector);\r\n let mig = document.getElementById('migrationForm-'+type).querySelector('[name=\"migrateable\"]');\r\n let inputType = document.getElementById('migrepairForm').querySelector('[name=\"type\"]');\r\n let inputMig = document.getElementById('migrepairForm').querySelector('[name=\"migrateable\"]');\r\n\r\n // Fill input values\r\n inputType.value = type;\r\n inputMig.value = mig.value;\r\n\r\n // Show modal\r\n let bsmodal = new bootstrap.Modal(document.getElementById('repair-modal-box'), {keyboard: false});\r\n bsmodal.show();\r\n}\r\n\r\n/**\r\n * Perform an ajax request in json format\r\n * \r\n * @param {String} formId Id of the form element\r\n * @param {String} task Name of the task\r\n * \r\n * @returns {Object} Result object\r\n * {success: true, status: 200, message: '', messages: {}, data: { { {success, data, continue, error, debug, warning} }}\r\n */\r\nlet ajax = async function(formId, task) {\r\n\r\n // Catch form and data\r\n let formData = new FormData(document.getElementById(formId));\r\n formData.append('format', 'json');\r\n\r\n if(task == 'migration.start') {\r\n formData.append('id', getNextMigrationID(formId));\r\n }\r\n\r\n // Set request parameters\r\n let parameters = {\r\n method: 'POST',\r\n mode: 'same-origin',\r\n cache: 'default',\r\n redirect: 'follow',\r\n referrerPolicy: 'no-referrer-when-downgrade',\r\n body: formData,\r\n };\r\n\r\n // Set the url\r\n let url = document.getElementById(formId).getAttribute('action');\r\n\r\n // Perform the fetch request\r\n let response = await fetch(url, parameters);\r\n\r\n // Resolve promise as text string\r\n let txt = await response.text();\r\n let res = null;\r\n\r\n if (!response.ok) {\r\n // Catch network error\r\n return {success: false, status: response.status, message: response.message, messages: {}, data: {error: txt, data:null}};\r\n }\r\n\r\n if(txt.startsWith('{\"success\"')) {\r\n // Response is of type json --> everything fine\r\n res = JSON.parse(txt);\r\n res.status = response.status;\r\n res.data = JSON.parse(res.data);\r\n } else if (txt.includes('Fatal error')) {\r\n // PHP fatal error occurred\r\n res = {success: false, status: response.status, message: response.statusText, messages: {}, data: {error: txt, data:null}};\r\n } else {\r\n // Response is not of type json --> probably some php warnings/notices\r\n let split = txt.split('\\n{\"');\r\n let temp = JSON.parse('{\"'+split[1]);\r\n let data = JSON.parse(temp.data);\r\n res = {success: true, status: response.status, message: split[0], messages: temp.messages, data: data};\r\n }\r\n\r\n // Make sure res.data.data.queue is of type array\r\n if(typeof res.data.data != \"undefined\" && res.data.data != null && 'queue' in res.data.data) {\r\n if(res.data.data.queue.constructor !== Array) {\r\n res.data.data.queue = Object.values(res.data.data.queue);\r\n }\r\n }\r\n\r\n return res;\r\n}\r\n\r\n/**\r\n * Perform a migration task\r\n * @param {String} formId Id of the form element\r\n * \r\n * @returns {String} Id of the database record to be migrated\r\n */\r\nlet getNextMigrationID = function(formId) {\r\n let type = formId.replace(formIdTmpl + '-', '');\r\n let form = document.getElementById(formId);\r\n\r\n let migrateable = atob(form.querySelector('[name=\"migrateable\"]').value);\r\n migrateable = JSON.parse(migrateable);\r\n\r\n // Overwrite migrateable in list\r\n migrateablesList[type] = migrateable;\r\n\r\n // Loop through queue\r\n for (let id of migrateable.queue) {\r\n if (!(id in migrateable.successful) && !(id in migrateable.failed)) {\r\n migrateablesList[type]['currentID'] = id;\r\n break;\r\n }\r\n }\r\n\r\n return migrateablesList[type]['currentID'];\r\n}\r\n\r\n/**\r\n * Handle migration response\r\n * \r\n * @param {Object} response The response object in the form of\r\n * {success: true, status: 200, message: '', messages: {}, data: { {success, data, continue, error, debug, warning} }}\r\n * \r\n * @returns void\r\n */\r\nlet responseHandler = function(type, response) {\r\n if(response.success == false) {\r\n // Ajax request failed or server responded with error code\r\n addLog('Error in server response. We will try again. ('+tryCounter+'/'+tryLimit+')', type, 'info');\r\n addLog(response.message, type, 'error');\r\n addLog(response.messages, type, 'error');\r\n addLog(response.data.error, type, 'error');\r\n \r\n\r\n // Try again...\r\n }\r\n else {\r\n // Ajax request successful\r\n if(!response.data.success)\r\n {\r\n // Migration failed\r\n addLog('[Migrator.js] Migration of '+type+' with id = '+migrateablesList[type]['currentID']+' failed.', type, 'error');\r\n logMessages(type, response.data);\r\n\r\n // Stop autimatic continuation if requested from backend\r\n if(!response.data.continue || response.data.continue == null || response.data.continue == false) {\r\n console.log('Stop automatic continuation requested from backend');\r\n continueState = false;\r\n }\r\n\r\n // Update migrateables\r\n updateMigrateables(type, response.data);\r\n }\r\n else\r\n {\r\n // Save record successful\r\n logMessages(type, response.data);\r\n addLog('[Migrator.js] Migration of '+type+' with id = '+migrateablesList[type]['currentID']+' successful.', type, 'success');\r\n\r\n // Stop autimatic continuation if requested from backend\r\n if(!response.data.continue || response.data.continue == null || response.data.continue == false) {\r\n console.log('Stop automatic continuation requested from backend');\r\n continueState = false;\r\n }\r\n\r\n // Update migrateables\r\n updateMigrateables(type, response.data);\r\n\r\n // Reset tryCounter\r\n tryCounter = 0;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Add a message to the logging output and the console\r\n * \r\n * @param {Mixed} msg One or multiple messages to be added to the log\r\n * @param {String} type The type defining the logging output to use\r\n * @param {String} msgType The type of message (available: error, warning, success, info)\r\n * @param {Boolean} console True to add the message also to the console\r\n * @param {Boolean} newLine True to add the message on a new line\r\n * @param {Integer} marginTop Number of how much margin you want on the top of the message\r\n * \r\n * @returns void\r\n */\r\nlet addLog = function(msg, type, msgType, console=false, newLine=true, marginTop=0) {\r\n if(!Boolean(msg) || msg == null || msg == '') {\r\n // Message is empty. Do nothing\r\n return;\r\n } else if(typeof msg === 'string') {\r\n // Your message is a simple string\r\n let tmp_msg = '';\r\n\r\n // Test if your string a json string\r\n try {\r\n tmp_msg = JSON.parse(msg);\r\n } catch (e) {\r\n }\r\n\r\n // Convert string to array\r\n if(tmp_msg !== '') {\r\n // remove object properties 'error' and 'code' if existent\r\n if('error' in tmp_msg) {\r\n delete tmp_msg.error;\r\n }\r\n if('code' in tmp_msg) {\r\n delete tmp_msg.code;\r\n }\r\n msg = Object.values(tmp_msg);\r\n } else {\r\n msg = [msg];\r\n }\r\n } else if(typeof msg === 'object') {\r\n // Your message is an object. Convert to array\r\n msg = Object.values(msg);\r\n }\r\n\r\n // Get logging output element\r\n let logOutput = document.getElementById('logOutput-'+type);\r\n\r\n // Loop through all messages\r\n msg.forEach((message, i) => {\r\n // Print in console\r\n if(console) {\r\n console.log(message);\r\n }\r\n\r\n // Create element\r\n let line = null;\r\n if(newLine) {\r\n line = document.createElement('p');\r\n } else {\r\n line = document.createElement('span');\r\n }\r\n\r\n // Top margin to element\r\n marginTop = parseInt(marginTop);\r\n if(marginTop > 0) {\r\n line.classList.add('mt-'+String(marginTop));\r\n }\r\n\r\n // Add text color\r\n line.classList.add('color-'+msgType);\r\n \r\n // Add message to element\r\n let msgType_txt = msgType.toLocaleUpperCase();\r\n line.textContent = '['+Joomla.JText._(msgType_txt)+'] '+String(message);\r\n\r\n // Print into logging output\r\n logOutput.appendChild(line);\r\n });\r\n}\r\n\r\n/**\r\n * Clear the logging output\r\n *\r\n * @param {String} type The type defining the logging output to clear\r\n * \r\n * @returns void\r\n */\r\nlet clearLog = function(type) {\r\n // Get logging output element\r\n let logOutput = document.getElementById('logOutput-'+type);\r\n\r\n // clear\r\n logOutput.innerHTML = '';\r\n}\r\n\r\n/**\r\n * Output all available messages from the result object\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {Object} res The result object in the form of\r\n * {success: bool, data: mixed, continue: bool, error: string|array, debug: string|array, warning: string|array}\r\n * \r\n * @returns void\r\n */\r\nlet logMessages = function(type, res) {\r\n // Available message types: error, debug, warning\r\n let available = ['error', 'debug', 'warning'];\r\n let msgTypes = {'error': 'error', 'debug': 'info', 'warning': 'warning'};\r\n\r\n available.forEach((value, index) => {\r\n if(!res[value] || !Boolean(res.data) || res.data == null) {\r\n return;\r\n }\r\n\r\n addLog(res[value], type, msgTypes[value]);\r\n });\r\n}\r\n\r\n/**\r\n * Update migrateable input field, progress bar and badges\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {Object} res The result object in the form of\r\n * {success: bool, data: mixed, continue: bool, error: string|array, debug: string|array, warning: string|array}\r\n * \r\n * @returns void\r\n */\r\nlet updateMigrateables = function(type, res) {\r\n let formId = formIdTmpl + '-' + type;\r\n let form = document.getElementById(formId);\r\n\r\n if(!res.success && (!Boolean(res.data) || res.data == null || res.data == '')) {\r\n // Migration failed, but no data available in result\r\n\r\n // Create result data based on input field\r\n let migrateable = atob(form.querySelector('[name=\"migrateable\"]').value);\r\n res.data = JSON.parse(migrateable);\r\n\r\n // See: Joomgallery\\Component\\Joomgallery\\Administrator\\Model\\MigrationModel::migrate\r\n // Remove migrated primary key from queue\r\n res.data.queue = res.data.queue.filter(function(e) { return e !== migrateablesList[type]['currentID'] })\r\n\r\n // Add migrated primary key to failed object\r\n res.data.failed[migrateablesList[type]['currentID']] = res.message;\r\n }\r\n\r\n if(!Boolean(res.data.progress) || res.data.progress == null || res.data.progress == '') {\r\n // Update progress if not delivered with result object\r\n let total = res.data.queue.lenght + Object.keys(res.data.successful).length + Object.keys(res.data.failed).length;\r\n let finished = Object.keys(res.data.successful).length + Object.keys(res.data.failed).length;\r\n res.data.progress = Math.round((100 / total) * (finished));\r\n }\r\n\r\n // Get badges\r\n let queueBadge = document.getElementById('badgeQueue-'+type);\r\n let resBadge = document.getElementById('badgeSuccessful-'+type);\r\n if(!res.success) {\r\n resBadge = document.getElementById('badgeFailed-'+type);\r\n }\r\n\r\n // Update migrateable input field\r\n let field = form.querySelector('[name=\"migrateable\"]');\r\n field.value = btoa(JSON.stringify(res.data));\r\n\r\n // Update badges\r\n queueBadge.innerHTML = parseInt(queueBadge.innerHTML) - 1;\r\n resBadge.innerHTML = parseInt(resBadge.innerHTML) + 1;\r\n\r\n // Update progress bar\r\n let bar = document.getElementById('progress-'+type);\r\n bar.setAttribute('aria-valuenow', res.data.progress);\r\n bar.style.width = res.data.progress + '%';\r\n bar.innerText = res.data.progress + '%';\r\n}\r\n\r\n/**\r\n * Update GUI to start migration\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {DOM Element} button The button beeing pressed to start the task\r\n * \r\n * @returns void\r\n */\r\nlet startTask = function(type, button) {\r\n let bar = document.getElementById('progress-'+type);\r\n let startBtn = button;\r\n let stopBtn = document.getElementById('stopBtn-'+type);\r\n\r\n // Update progress bar\r\n bar.classList.add('progress-bar-striped');\r\n bar.classList.add('progress-bar-animated');\r\n \r\n // Disable start button\r\n startBtn.classList.add('disabled');\r\n startBtn.setAttribute('disabled', 'true');\r\n\r\n // Enable stop button\r\n stopBtn.classList.remove('disabled');\r\n stopBtn.removeAttribute('disabled');\r\n\r\n // Reinitialize variables\r\n tryCounter = 0;\r\n continueState = true;\r\n forceStop = false;\r\n}\r\n\r\n/**\r\n * Update GUI to end migration\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {DOM Element} button The button beeing pressed to start the task\r\n * @param {String} formId Id of the form element\r\n * \r\n * @returns void\r\n */\r\nlet finishTask = function(type, button, formId) {\r\n let bar = document.getElementById('progress-'+type);\r\n let startBtn = button;\r\n let stopBtn = document.getElementById('stopBtn-'+type);\r\n\r\n // Update migrateablesList\r\n getNextMigrationID(formId);\r\n\r\n // Update progress bar\r\n bar.classList.remove('progress-bar-striped');\r\n bar.classList.remove('progress-bar-animated');\r\n \r\n // Enable start button\r\n if(!migrateablesList[type]['completed']) {\r\n // Only enable start button if migration is not finished\r\n startBtn.classList.remove('disabled');\r\n startBtn.removeAttribute('disabled');\r\n }\r\n\r\n // Disable stop button\r\n stopBtn.classList.add('disabled');\r\n stopBtn.setAttribute('disabled', 'true');\r\n\r\n // If migration is completed\r\n if(migrateablesList[type]['completed']) {\r\n // Update next start button\r\n enableNextBtn(type, button);\r\n // Update step 4 button\r\n updateStep4Btn();\r\n }\r\n}\r\n\r\n/**\r\n * Enable start button of next migration content type\r\n * \r\n * @param {String} type The type defining the content type to be updated\r\n * @param {DOM Element} button The current start button\r\n * \r\n * @returns void\r\n */\r\nlet enableNextBtn = function(type, button) {\r\n let types_inputs = document.getElementsByName('type');\r\n let next_type = '';\r\n\r\n // Find next migration content type\r\n let this_type = false;\r\n for (const type_input of types_inputs) {\r\n if(this_type) {\r\n next_type = type_input.value;\r\n break;\r\n }\r\n if(Boolean(type_input.value) && type_input.value == type) {\r\n this_type = true;\r\n }\r\n }\r\n\r\n // Get next button\r\n let nextBtn = document.getElementById(buttonTmpl + '-' + next_type);\r\n\r\n // Enable button\r\n nextBtn.classList.remove('disabled');\r\n nextBtn.removeAttribute('disabled');\r\n}\r\n\r\n/**\r\n * Update button to go to step 4\r\n * \r\n * @returns void\r\n */\r\nlet updateStep4Btn = function() {\r\n let types_inputs = document.getElementsByName('type');\r\n let types = {};\r\n\r\n // Add all available migrateables to types object\r\n types_inputs.forEach((type) => {\r\n if(Boolean(type.value)) {\r\n types[type.value] = false;\r\n }\r\n });\r\n\r\n // Check if all migrateables are available and completed\r\n let tot_complete = true;\r\n Object.keys(types).forEach(type => { \r\n if(Boolean(migrateablesList[type])) {\r\n if(!migrateablesList[type]['completed'])\r\n {\r\n // Migrateable not yet completed\r\n tot_complete = false;\r\n }\r\n }\r\n else\r\n {\r\n // Migrateable does not yet exist. Thus not completed\r\n tot_complete = false;\r\n }\r\n });\r\n\r\n if(tot_complete) {\r\n // Enable step 4 button\r\n document.getElementById(step4Btn).classList.remove('disabled');\r\n document.getElementById(step4Btn).removeAttribute('disabled');\r\n }\r\n}"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/media/com_joomgallery/js/migrator/src/index.js b/media/com_joomgallery/js/migrator/src/index.js index 9aac5215..96b29351 100644 --- a/media/com_joomgallery/js/migrator/src/index.js +++ b/media/com_joomgallery/js/migrator/src/index.js @@ -66,7 +66,7 @@ export let submitTask = function(event, element) { submitTask(event, element); } else { // Stop automatic task execution and update GUI - finishTask(type, element); + finishTask(type, element, formId); } }) .catch(error => { @@ -472,8 +472,8 @@ let startTask = function(type, button) { let stopBtn = document.getElementById('stopBtn-'+type); // Update progress bar - bar.classList.remove('progress-bar-striped'); - bar.classList.remove('progress-bar-animated'); + bar.classList.add('progress-bar-striped'); + bar.classList.add('progress-bar-animated'); // Disable start button startBtn.classList.add('disabled'); @@ -494,20 +494,24 @@ let startTask = function(type, button) { * * @param {String} type The type defining the content type to be updated * @param {DOM Element} button The button beeing pressed to start the task + * @param {String} formId Id of the form element * * @returns void */ -let finishTask = function(type, button) { +let finishTask = function(type, button, formId) { let bar = document.getElementById('progress-'+type); let startBtn = button; let stopBtn = document.getElementById('stopBtn-'+type); + // Update migrateablesList + getNextMigrationID(formId); + // Update progress bar bar.classList.remove('progress-bar-striped'); bar.classList.remove('progress-bar-animated'); // Enable start button - if(!migrateablesList[type]['complete']) { + if(!migrateablesList[type]['completed']) { // Only enable start button if migration is not finished startBtn.classList.remove('disabled'); startBtn.removeAttribute('disabled'); @@ -518,7 +522,7 @@ let finishTask = function(type, button) { stopBtn.setAttribute('disabled', 'true'); // If migration is completed - if(migrateablesList[type]['complete']) { + if(migrateablesList[type]['completed']) { // Update next start button enableNextBtn(type, button); // Update step 4 button @@ -540,15 +544,15 @@ let enableNextBtn = function(type, button) { // Find next migration content type let this_type = false; - types_inputs.forEach((type_input) => { + for (const type_input of types_inputs) { if(this_type) { next_type = type_input.value; - return; + break; } if(Boolean(type_input.value) && type_input.value == type) { this_type = true; } - }); + } // Get next button let nextBtn = document.getElementById(buttonTmpl + '-' + next_type); From 9329a79c84d2572a8e6262a37f60c4fb0ddd1817 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Wed, 3 Jan 2024 17:43:41 +0100 Subject: [PATCH 063/121] fix naming issue --- .../com_joomgallery/src/Model/MigrationModel.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index caef85f7..e36c1f8f 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -693,8 +693,8 @@ public function migrate(string $type, int $pk): object else { // Create new record at destination based on converted data - $sameIDs = \boolval($mig->params->get('source_ids', 0)); - $record = $this->insertRecord($type, (array) $data, $sameIDs); + $autoIDs = !\boolval($mig->params->get('source_ids', 0)); + $record = $this->insertRecord($type, (array) $data, $autoIDs); if(!$record) { @@ -966,15 +966,15 @@ public function deleteSource() /** * Method to insert a content type record from migration data. * - * @param string $type Name of the content type to insert. - * @param array $data The record data gathered from the migration source. - * @param bool $newID True to auto-increment the id in the database + * @param string $type Name of the content type to insert. + * @param array $data The record data gathered from the migration source. + * @param bool $autoID True to auto-increment the id in the database * * @return object Inserted record object on success, False on error. * * @since 4.0.0 */ - protected function insertRecord(string $type, array $data, bool $newID = true) + protected function insertRecord(string $type, array $data, bool $autoID = true) { $recordType = $this->component->getMigration()->get('types')[$type]->get('recordName'); @@ -1006,7 +1006,7 @@ protected function insertRecord(string $type, array $data, bool $newID = true) } // Disable auto-incrementing record ID - if($isNew && $newID && \in_array($key, \array_keys($data)) && \method_exists($table, 'insertID')) + if($isNew && !$autoID && \in_array($key, \array_keys($data)) && \method_exists($table, 'insertID')) { $table->insertID(); } From 69e843feafe0cec2709a652fa2616ac0900894ee Mon Sep 17 00:00:00 2001 From: Elfangor Date: Thu, 4 Jan 2024 10:57:12 +0100 Subject: [PATCH 064/121] fix load migration form Case-sensitive systems like Linux were not able to find the migration form XML. This should be fixed now. --- .../src/Model/MigrationModel.php | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index e36c1f8f..a24b1dad 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -18,6 +18,7 @@ use \Joomla\CMS\Form\Form; use \Joomla\CMS\Language\Text; use \Joomla\Registry\Registry; +use \Joomla\CMS\Filesystem\Path; use \Joomla\Utilities\ArrayHelper; use \Joomla\CMS\Filesystem\Folder; use \Joomla\CMS\MVC\Model\AdminModel; @@ -577,10 +578,22 @@ public function getForm($data = array(), $loadData = true) Form::addFormPath(JPATH_ADMINISTRATOR.'/components/'._JOOM_OPTION.'/src/Service/Migration/Scripts'); Form::addFormPath(JPATH_ADMINISTRATOR.'/components/'._JOOM_OPTION.'/forms'); + // Get the form file path + $file = Path::find(Form::addFormPath(), strtolower($script->name) . '.xml'); + if(!is_file($file)) + { + $file = Path::find(Form::addFormPath(), $script->name . '.xml'); + } + + if(!is_file($file)) + { + $this->component->setError('Migration form XML could not be found. XML filename: ' . $script->name . '.xml'); + return false; + } + // Get the form. - $name = _JOOM_OPTION.'.migration.'.$this->component->getMigration()->get('name'); - $source = $this->component->getMigration()->get('name'); - $form = $this->loadForm($name, $source, array('control' => 'jform_'.$source, 'load_data' => true)); + $name = _JOOM_OPTION.'.migration.'.$this->component->getMigration()->get('name'); + $form = $this->loadForm($name, $file, array('control' => 'jform_'.$script->name, 'load_data' => true)); if(empty($form)) { From f497943627af3b8bb0ab0360fae84a4f2ea44a43 Mon Sep 17 00:00:00 2001 From: Elfangor Date: Thu, 4 Jan 2024 11:10:10 +0100 Subject: [PATCH 065/121] fix issue missing joomgallery_old.xml When updating from JG3 the old manifest file is needed for the migration. This was missing before. --- script.php | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/script.php b/script.php index e307191c..6c750610 100644 --- a/script.php +++ b/script.php @@ -179,11 +179,12 @@ public function preflight($type, $parent) } } - // copy old XML file (JGv1-3) - $xml_path = JPATH_ADMINISTRATOR.DIRECTORY_SEPARATOR.'components'.DIRECTORY_SEPARATOR.'com_joomgallery'.DIRECTORY_SEPARATOR; + // copy old XML file (JGv1-3) to temp folder + $xml_path = JPATH_ADMINISTRATOR.DIRECTORY_SEPARATOR.'components'.DIRECTORY_SEPARATOR.'com_joomgallery'.DIRECTORY_SEPARATOR; + $tmp_folder = Factory::getApplication()->get('tmp_path'); if(File::exists($xml_path.'joomgallery.xml')) { - File::copy($xml_path.'joomgallery.xml', $xml_path.'joomgallery_old.xml'); + File::copy($xml_path.'joomgallery.xml', $tmp_folder.DIRECTORY_SEPARATOR.'joomgallery_old.xml'); } // remove old JoomGallery files and folders @@ -433,6 +434,17 @@ function postflight($type, $parent) { $app = Factory::getApplication(); + if($this->fromOldJG) + { + // copy old XML file (JGv1-3) back from temp folder + $xml_path = JPATH_ADMINISTRATOR.DIRECTORY_SEPARATOR.'components'.DIRECTORY_SEPARATOR.'com_joomgallery'.DIRECTORY_SEPARATOR; + $tmp_folder = Factory::getApplication()->get('tmp_path'); + if(File::exists($tmp_folder.DIRECTORY_SEPARATOR.'joomgallery_old.xml')) + { + File::copy($tmp_folder.DIRECTORY_SEPARATOR.'joomgallery_old.xml', $xml_path.'joomgallery_old.xml'); + } + } + // Create default Category if(!$this->addDefaultCategory()) { From 5d383e63e7816679272c50d652c57b1c12cc23dd Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Sat, 13 Jan 2024 10:42:01 +0100 Subject: [PATCH 066/121] fix pre-check: JG3 catpath --- .../Service/Migration/Scripts/Jg3ToJg4.php | 37 ++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index 220a45fa..6c2ceebd 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -683,7 +683,7 @@ public function scriptSpecificChecks(string $type, Checks &$checks, string $cate // Check if imgfilename and imgthumbname are the same $query = $db->getQuery(true) ->select($db->quoteName(array('id'))) - ->from($tablename) + ->from($db->quoteName($tablename)) ->where($db->quoteName('imgfilename') . ' != ' . $db->quoteName('imgthumbname')); $db->setQuery($query); @@ -698,11 +698,12 @@ public function scriptSpecificChecks(string $type, Checks &$checks, string $cate //------------------------ // Check catpath of JG3 category table if they are consistent + // Scheme: parent-paht/alias_cid if($this->params->get('same_joomla', 1) == 1) { $query = $db->getQuery(true) - ->select($db->quoteName(array('cid', 'alias', 'catpath'))) - ->from($cattablename) + ->select($db->quoteName(array('cid', 'alias', 'parent_id', 'catpath'))) + ->from($db->quoteName($cattablename)) ->where($db->quoteName('level') . ' > 0 '); $db->setQuery($query); @@ -773,17 +774,43 @@ public function scriptSpecificChecks(string $type, Checks &$checks, string $cate * * @param \stdClass $cat Category object * - * @return bool True if catpath is correct, false otherwise + * @return bool True if catpath is correct, false otherwise * * @since 4.0.0 */ protected function checkCatpath(\stdClass $cat): bool { + // Prepare catpath $cat->catpath = \str_replace(\DIRECTORY_SEPARATOR, '/', $cat->catpath); $catpath_arr = \explode('/', $cat->catpath); $catpath = \end($catpath_arr); + $parentpath = \rtrim($cat->catpath, $catpath); - if($catpath !== $cat->alias.'_'.$cat->cid) + // Prepare alias + $cat->alias = \str_replace(\DIRECTORY_SEPARATOR, '/', $cat->alias); + $alias_arr = \explode('/', $cat->alias); + $alias = \end($alias_arr); + + // Check for alias_cid + if($catpath !== $alias.'_'.$cat->cid) + { + return false; + } + + // Get path of parent category + list($db, $dbPrefix) = $this->getDB('source'); + list($tablename, $pkname) = $this->getSourceTableInfo('category'); + + $query = $db->getQuery(true) + ->select($db->quoteName('catpath')) + ->from($db->quoteName($tablename)) + ->where($db->quoteName('cid') . ' = '. $db->quote($cat->parent_id)); + $db->setQuery($query); + + $path = $db->loadResult(); + + // Check for parent-path + if($parentpath !== $path) { return false; } From 46b62711f2537755439575ece1e643cac43c8bfe Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Sat, 13 Jan 2024 12:43:13 +0100 Subject: [PATCH 067/121] introducing the compatibility mode --- administrator/com_joomgallery/forms/config.xml | 11 +++++++++++ .../language/en-GB/com_joomgallery.ini | 2 ++ .../en-GB/com_joomgallery.migration.Jg3ToJg4.ini | 4 +++- .../com_joomgallery/sql/install.mysql.utf8.sql | 1 + .../com_joomgallery/sql/updates/mysql/4.0.0.sql | 1 + .../src/Service/Migration/Scripts/Jg3ToJg4.xml | 10 ++++++++++ 6 files changed, 28 insertions(+), 1 deletion(-) diff --git a/administrator/com_joomgallery/forms/config.xml b/administrator/com_joomgallery/forms/config.xml index 2cc5b105..4e2f27bf 100644 --- a/administrator/com_joomgallery/forms/config.xml +++ b/administrator/com_joomgallery/forms/config.xml @@ -115,6 +115,17 @@ + + + + + RSS feed provided at joomgalleryfriends.net." COM_JOOMGALLERY_CONFIG_REPLACE_METADATA="Use image metadata" diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini index daccc4d6..fa58135b 100644 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini @@ -24,4 +24,6 @@ FILES_JOOMGALLERY_SERVICE_MIGRATION_DIRECT_USAGE_IMGTYPES_ERROR="Direct usage of FILES_JOOMGALLERY_SERVICE_MIGRATION_DIRECT_USAGE_LOCAL_ERROR="Direct usage of images is only possible when using the local filesystem. Please switch to the local filesystem in the JoomGallery configuration if you want to use 'Direct usage'." FILES_JOOMGALLERY_SERVICE_MIGRATION_DIRECT_USAGE_ORIGINAL_WARNING="You have chosen direct usage of images in step 1. Please make sure the 'original' imagetype is deactivated if it was deactivated in your source/previous Joomla version." FILES_JOOMGALLERY_MIGRATION_CHECK_CATEGORY_CATPATH="Category paths" -FILES_JOOMGALLERY_MIGRATION_CHECK_CATEGORY_CATPATH_DESC="There are inconsistent category folder paths defined in the database of your JG3 tables. Please adjust category paths manually, otherwise automatic migration is not possible. ID's of affected categories: %s" \ No newline at end of file +FILES_JOOMGALLERY_MIGRATION_CHECK_CATEGORY_CATPATH_DESC="There are inconsistent category folder paths defined in the database of your JG3 tables. Please adjust category paths manually, otherwise automatic migration is not possible. ID's of affected categories: %s" +FILES_JOOMGALLERY_FIELDS_NEW_DIRS_LABEL="Use new folder structure" +FILES_JOOMGALLERY_FIELDS_NEW_DIRS_DESC="Do you want to switch your migrated category folder structure (filesystem) to the new folder structure style?{tip}New style (JG4+): root-of-filesystem/joomgallery/imagetype/category-path/alias/
Old style (JG3-): joomla-root/images/joomgallery/imagetype/category-path/name_catid/
In JG3-, in most cases the alias is derivable from the category name. This might be wrong if you renamed category titles without regenrating the alias. In this case a convertion of the old JG3- folder structure to the new one is not possible.
If you choose to keep the old folder structure, you have to switch on the 'JG3 compatibility mode' in the components global configuration in order for the component to find the images stored in the old folder structure style." \ No newline at end of file diff --git a/administrator/com_joomgallery/sql/install.mysql.utf8.sql b/administrator/com_joomgallery/sql/install.mysql.utf8.sql index 517e0886..3a460a69 100644 --- a/administrator/com_joomgallery/sql/install.mysql.utf8.sql +++ b/administrator/com_joomgallery/sql/install.mysql.utf8.sql @@ -117,6 +117,7 @@ CREATE TABLE IF NOT EXISTS `#__joomgallery_configs` ( `jg_pathftpupload` VARCHAR(100) NOT NULL DEFAULT "administrator/components/com_joomgallery/temp/ftp_upload/", `jg_wmfile` VARCHAR(50) NOT NULL DEFAULT "media/joomgallery/images/watermark.png", `jg_use_real_paths` TINYINT(1) NOT NULL DEFAULT 0, +`jg_compatibility_mode` TINYINT(1) NOT NULL DEFAULT 0, `jg_checkupdate` TINYINT(1) NOT NULL DEFAULT 1, `jg_replaceinfo` TEXT NOT NULL, `jg_replaceshowwarning` TINYINT(1) NOT NULL DEFAULT 0, diff --git a/administrator/com_joomgallery/sql/updates/mysql/4.0.0.sql b/administrator/com_joomgallery/sql/updates/mysql/4.0.0.sql index 517e0886..3a460a69 100644 --- a/administrator/com_joomgallery/sql/updates/mysql/4.0.0.sql +++ b/administrator/com_joomgallery/sql/updates/mysql/4.0.0.sql @@ -117,6 +117,7 @@ CREATE TABLE IF NOT EXISTS `#__joomgallery_configs` ( `jg_pathftpupload` VARCHAR(100) NOT NULL DEFAULT "administrator/components/com_joomgallery/temp/ftp_upload/", `jg_wmfile` VARCHAR(50) NOT NULL DEFAULT "media/joomgallery/images/watermark.png", `jg_use_real_paths` TINYINT(1) NOT NULL DEFAULT 0, +`jg_compatibility_mode` TINYINT(1) NOT NULL DEFAULT 0, `jg_checkupdate` TINYINT(1) NOT NULL DEFAULT 1, `jg_replaceinfo` TEXT NOT NULL, `jg_replaceshowwarning` TINYINT(1) NOT NULL DEFAULT 0, diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.xml b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.xml index 30768ab3..d004e6cb 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.xml +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.xml @@ -113,6 +113,16 @@
+ + + + Date: Sat, 13 Jan 2024 13:30:01 +0100 Subject: [PATCH 068/121] adjust prechecks --- .../com_joomgallery.migration.Jg3ToJg4.ini | 6 ++++-- .../Service/Migration/Scripts/Jg3ToJg4.php | 19 +++++++++++++++---- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini index fa58135b..a0786993 100644 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini @@ -24,6 +24,8 @@ FILES_JOOMGALLERY_SERVICE_MIGRATION_DIRECT_USAGE_IMGTYPES_ERROR="Direct usage of FILES_JOOMGALLERY_SERVICE_MIGRATION_DIRECT_USAGE_LOCAL_ERROR="Direct usage of images is only possible when using the local filesystem. Please switch to the local filesystem in the JoomGallery configuration if you want to use 'Direct usage'." FILES_JOOMGALLERY_SERVICE_MIGRATION_DIRECT_USAGE_ORIGINAL_WARNING="You have chosen direct usage of images in step 1. Please make sure the 'original' imagetype is deactivated if it was deactivated in your source/previous Joomla version." FILES_JOOMGALLERY_MIGRATION_CHECK_CATEGORY_CATPATH="Category paths" -FILES_JOOMGALLERY_MIGRATION_CHECK_CATEGORY_CATPATH_DESC="There are inconsistent category folder paths defined in the database of your JG3 tables. Please adjust category paths manually, otherwise automatic migration is not possible. ID's of affected categories: %s" +FILES_JOOMGALLERY_MIGRATION_CHECK_CATEGORY_CATPATH_DESC="There are inconsistent category folder paths defined in the database of your JG3 tables. Please adjust affected category paths manually, or switch off 'Use new folder structure' in step 1 to use the old static catpath instead. ID's of affected categories: %s" FILES_JOOMGALLERY_FIELDS_NEW_DIRS_LABEL="Use new folder structure" -FILES_JOOMGALLERY_FIELDS_NEW_DIRS_DESC="Do you want to switch your migrated category folder structure (filesystem) to the new folder structure style?{tip}New style (JG4+): root-of-filesystem/joomgallery/imagetype/category-path/alias/
Old style (JG3-): joomla-root/images/joomgallery/imagetype/category-path/name_catid/
In JG3-, in most cases the alias is derivable from the category name. This might be wrong if you renamed category titles without regenrating the alias. In this case a convertion of the old JG3- folder structure to the new one is not possible.
If you choose to keep the old folder structure, you have to switch on the 'JG3 compatibility mode' in the components global configuration in order for the component to find the images stored in the old folder structure style." \ No newline at end of file +FILES_JOOMGALLERY_FIELDS_NEW_DIRS_DESC="Do you want to convert your migrated categories to the new folder structure style (filesystem)?{tip}New path style (JG4+): root-of-filesystem/joomgallery/imagetype/parent-category-path/alias/
Old path style (JG3-): joomla-root/images/joomgallery/imagetype/parent-category-path/name_cid/
In JG3-, in most cases the alias is derivable from the category name. This might be wrong if you renamed category titles without regenrating the alias. In this case a convertion of the old JG3- folder structure to the new one is not possible.
If you choose to keep the old folder structure, you have to switch on the 'JG3 compatibility mode' in the components global configuration in order for the component to find images stored in the old folder structure style." +FILES_JOOMGALLERY_MIGRATION_CHECK_COMPATIBILITY_MODE="Compatibility mode" +FILES_JOOMGALLERY_MIGRATION_CHECK_COMPATIBILITY_MODE_DESC="You have chosen to use the old folder structure in step 1. In order for this to work the compatibility mode has to be activated in the component configuration. Please activate 'JG3 compatibility mode' in the JoomGallery configuration." \ No newline at end of file diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index 6c2ceebd..60da2ddf 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -671,6 +671,8 @@ protected function reuseImages(ImageTable $img, array $sources, bool $copy = fal */ public function scriptSpecificChecks(string $type, Checks &$checks, string $category) { + $this->component->createConfig(); + if($type == 'pre') { // Get source db info @@ -697,10 +699,10 @@ public function scriptSpecificChecks(string $type, Checks &$checks, string $cate //------------------------ - // Check catpath of JG3 category table if they are consistent - // Scheme: parent-paht/alias_cid - if($this->params->get('same_joomla', 1) == 1) + if($this->params->get('new_dirs', 1) == 1) { + // We want to use the new folder structure style + // Check catpath of JG3 category table if they are consistent and convertable $query = $db->getQuery(true) ->select($db->quoteName(array('cid', 'alias', 'parent_id', 'catpath'))) ->from($db->quoteName($cattablename)) @@ -725,6 +727,15 @@ public function scriptSpecificChecks(string $type, Checks &$checks, string $cate $checks->addCheck($category, 'src_table_cat_path', false, false, Text::_('FILES_JOOMGALLERY_MIGRATION_CHECK_CATEGORY_CATPATH'), Text::sprintf('FILES_JOOMGALLERY_MIGRATION_CHECK_CATEGORY_CATPATH_DESC', \implode(', ', $inconsistent))); } } + else + { + // We want to use the old folder structure style + // Check if compatibility mode is activated + if($this->component->getConfig()->get('jg_compatibility_mode', 0) == 0) + { + $checks->addCheck($category, 'compatibility_mode', false, false, Text::_('FILES_JOOMGALLERY_MIGRATION_CHECK_COMPATIBILITY_MODE'), Text::_('FILES_JOOMGALLERY_MIGRATION_CHECK_COMPATIBILITY_MODE_DESC')); + } + } //------------------------ @@ -784,7 +795,7 @@ protected function checkCatpath(\stdClass $cat): bool $cat->catpath = \str_replace(\DIRECTORY_SEPARATOR, '/', $cat->catpath); $catpath_arr = \explode('/', $cat->catpath); $catpath = \end($catpath_arr); - $parentpath = \rtrim($cat->catpath, $catpath); + $parentpath = \rtrim($cat->catpath, '/'.$catpath); // Prepare alias $cat->alias = \str_replace(\DIRECTORY_SEPARATOR, '/', $cat->alias); From 7bfeb1e68d3f10dbe6d905c62d86412f1844522b Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Sat, 13 Jan 2024 13:36:44 +0100 Subject: [PATCH 069/121] Adjust check messages --- .../com_joomgallery/language/en-GB/com_joomgallery.ini | 8 ++++---- .../com_joomgallery/src/Service/Migration/Checks.php | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index 461e2c1d..094e5b0b 100644 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -716,13 +716,13 @@ COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_WRONG_VERSION="The current version o COM_JOOMGALLERY_SERVICE_MIGRATION_EXTENSION_SUCCESS="Extension is compatible (Extension: %s, Version: %s)" COM_JOOMGALLERY_SERVICE_MIGRATION_SOURCE_XML="The XML file of your source extension could not be found. Please make sure the XML is available and readable." COM_JOOMGALLERY_SERVICE_MIGRATION_PHP_WRONG_VERSION="The current PHP version is not supported. Current version: %s. Minimum requirement: %s" -COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP2="Step 2: Migration pre-check failed. Reason: %s" +COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP2="Step 2: Migration pre-check failed. Failed check: %s" COM_JOOMGALLERY_SERVICE_MIGRATION_SUCCESS_MIGRATION_STEP2="Step 2: Migration pre-check successful." -COM_JOOMGALLERY_SERVICE_MIGRATION_WARNING_MIGRATION_STEP2="Step 2: Warning appeared during migration pre-check. Reason: %s" +COM_JOOMGALLERY_SERVICE_MIGRATION_WARNING_MIGRATION_STEP2="Step 2: Warning appeared during migration pre-check. Affected check: %s" COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_CHECKS_FAILED="Some of the checks failed." COM_JOOMGALLERY_SERVICE_MIGRATION_SUCCESS_MIGRATION_STEP4="Step 4: Migration post-check successful." -COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP4="Step 4: Migration post-check failed. Reason: %s" -COM_JOOMGALLERY_SERVICE_MIGRATION_WARNING_MIGRATION_STEP4="Step 4: Warning appeared during migration post-check. Reason: %s" +COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_MIGRATION_STEP4="Step 4: Migration post-check failed. Failed check: %s" +COM_JOOMGALLERY_SERVICE_MIGRATION_WARNING_MIGRATION_STEP4="Step 4: Warning appeared during migration post-check. Affected check: %s" COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES="There are already %s records in this table." COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES_EMPTY="This table is empty." COM_JOOMGALLERY_SERVICE_MIGRATION_COUNT_TABLES_USE_IDS_HINT="You have set 'Use source IDs' to true in step 1. But the ID's from source are not free/available in the destination.
Please delete the records with the following ID's: %s." diff --git a/administrator/com_joomgallery/src/Service/Migration/Checks.php b/administrator/com_joomgallery/src/Service/Migration/Checks.php index 8bf3f21d..248250d8 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Checks.php +++ b/administrator/com_joomgallery/src/Service/Migration/Checks.php @@ -199,7 +199,7 @@ public function addCheck(string $category, string $name, bool $result, bool $war if($this->message === '') { - $this->message = $title . ' (' . $desc . ')'; + $this->message = $title; } } else @@ -207,7 +207,7 @@ public function addCheck(string $category, string $name, bool $result, bool $war if($warning && $this->message === '') { // Add message if there is a warning - $this->message = $title . ' (' . $desc . ')'; + $this->message = $title; } } } From ac9e52faf90cbf8e1dd04dd19df6c748153437fe Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Sun, 14 Jan 2024 17:51:37 +0100 Subject: [PATCH 070/121] adjust convertData() --- .../src/Service/Migration/Scripts/Jg3ToJg4.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index 60da2ddf..9425bc8e 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -217,12 +217,6 @@ public function convertData(string $type, array $data): array $id = \boolval($this->params->get('source_ids', 0)) ? 'id' : false; $owner = \boolval($this->params->get('check_owner', 0)) ? 'created_by' : false; $path = false; - - if($this->params->get('same_joomla', 1) == 1 && $this->params->get('image_usage', 0) == 0) - { - // Special case: Direct usage of images in the same joomla installation - $path = 'path'; - } // Configure mapping for each content type switch($type) @@ -230,7 +224,7 @@ public function convertData(string $type, array $data): array case 'category': // Apply mapping for category table $mapping = array( 'cid' => $id, 'asset_id' => false, 'name' => 'title', 'alias' => false, 'lft' => false, 'rgt' => false, 'level' => false, - 'owner' => $owner, 'img_position' => false, 'catpath' => $path, 'params' => array('params', false, false), + 'owner' => $owner, 'img_position' => false, 'catpath' => 'static_path', 'params' => array('params', false, false), 'allow_download' => array('params', 'jg_download', false), 'allow_comment' => array('params', 'jg_showcomment', false), 'allow_rating' => array('params', 'jg_showrating', false), 'allow_watermark' => array('params', 'jg_dynamic_watermark', false), 'allow_watermark_download' => array('params', 'jg_downloadwithwatermark', false) @@ -410,6 +404,14 @@ public function migrateFolder(CategoryTable $cat, array $data): bool if($this->params->get('image_usage', 1) == 0) { // Direct usage + if($this->params->get('same_joomla', 1) == 0) + { + // Direct usage from other source is impossible + $this->component->setError('FILES_JOOMGALLERY_SERVICE_MIGRATION_DIRECT_USAGE_ERROR'); + + return false; + } + // Store new foldername $newName = $cat->path; From 1d46a7886a6681e778fab8fe4c4d3a082f343e0b Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Sun, 14 Jan 2024 18:37:31 +0100 Subject: [PATCH 071/121] Add case "Use source ID's" --- .../language/en-GB/com_joomgallery.ini | 1 + .../src/Model/MigrationModel.php | 88 ++++++++++++++++++- 2 files changed, 86 insertions(+), 3 deletions(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index 094e5b0b..071b4de3 100644 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -759,6 +759,7 @@ COM_JOOMGALLERY_SERVICE_MIGRATION_QUEUE_ERROR="The migration queue of %s have no COM_JOOMGALLERY_SERVICE_MIGRATION_ERRORS_ERROR="Error during migration of type '%s' and ID '%s'. Error message: %s." COM_JOOMGALLERY_SERVICE_MIGRATION_ERRORS_SUCCESS="No errors detected from migration manager." COM_JOOMGALLERY_SERVICE_MIGRATION_SOURCE_DATA_DELETE_SUCCESSFUL="Source data successfully removed." +COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_DUMMY_RECORD="Dummy record of type %s with ID=%s could not be inserted into database. Does a record with this id already exist in the table?" ;Menu COM_JOOMGALLERY_MENU_CATEGORY_VIEW_OPTIONS="Category View" diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index a24b1dad..7b1c70ea 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -1018,10 +1018,21 @@ protected function insertRecord(string $type, array $data, bool $autoID = true) } } - // Disable auto-incrementing record ID - if($isNew && !$autoID && \in_array($key, \array_keys($data)) && \method_exists($table, 'insertID')) + // Special case: Use source IDs. Insert dummy record and after bind data on it. + if($isNew && !$autoID && \in_array($key, \array_keys($data))) { - $table->insertID(); + if(!$this->insertDummyRecord($type, $data[$key])) + { + // Insert dummy failed. Stop migration of this record. + return false; + } + + if(!$table->load($data[$key])) + { + $this->component->setError($table->getError()); + + return false; + } } // Change language to 'All' if multilanguage is not enabled @@ -1122,4 +1133,75 @@ protected function deleteRecord(string $type, int $pk): bool return true; } + + /** + * Method to insert an empty dummy record with a given primary key + * + * @param string $type Name of the content type to insert. + * @param int $key Primary key to use. + * + * @return bool|int Primary key of the created dummy record or false on failure + * + * @since 4.0.0 + */ + protected function insertDummyRecord(string $type, int $key) + { + list($db, $dbPrefix) = $this->component->getMigration()->getDB('destination'); + $date = Factory::getDate(); + + // Create and populate a dummy object. + $record = new stdClass(); + $record->id = $key; + + $needed = array('image', 'category', 'comment', 'gallery', 'tag'); + if(\in_array($type, $needed)) + { + $record->description = ''; + } + + $needed = array('image'); + if(\in_array($type, $needed)) + { + $record->date = $date->toSql(); + $record->imgmetadata = ''; + $record->filename = ''; + } + + $needed = array('image', 'category', 'imagetype', 'user'); + if(\in_array($type, $needed)) + { + $record->params = ''; + } + + $needed = array('image', 'category', 'gallery'); + if(\in_array($type, $needed)) + { + $record->metadesc = ''; + $record->metakey = ''; + } + + $needed = array('image', 'category', 'field', 'tag', 'gallery', 'user', 'vote', 'comment'); + if(\in_array($type, $needed)) + { + $record->created_time = $date->toSql(); + } + + $needed = array('image', 'category', 'tag', 'gallery', 'comment'); + if(\in_array($type, $needed)) + { + $record->modified_time = $date->toSql(); + } + + // Insert the object into the user profile table. + if(!$db->insertObject(JoomHelper::$content_types[$type], $record)) + { + $this->component->setError(Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_DUMMY_RECORD', $type, $key)); + + return false; + } + else + { + return $key; + } + } } From 99737f6688dd96263fc7e3737856df20a0c91407 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 15 Jan 2024 10:05:49 +0100 Subject: [PATCH 072/121] add metadesc to galleries --- .../language/en-GB/com_joomgallery.sys.ini | 8 ++++++-- .../com_joomgallery/sql/install.mysql.utf8.sql | 3 +++ .../sql/updates/mysql/4.0.0.sql | 3 +++ .../src/Table/CategoryTable.php | 12 ++++++++++++ .../src/Table/JoomTableTrait.php | 18 ++++++++++++++++++ 5 files changed, 42 insertions(+), 2 deletions(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.sys.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.sys.ini index adba7309..65fea61f 100644 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.sys.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.sys.ini @@ -13,14 +13,18 @@ COM_JOOMGALLERY_CONFIG_SETS="Configuration Sets" COM_JOOMGALLERY_IMAGES="Images" COM_JOOMGALLERY_MAINTENANCE="Maintenance" COM_JOOMGALLERY_UPLOAD="Upload" +COM_JOOMGALLERY_TAGS="Tags" +COM_JOOMGALLERY_VOTES="Votes" +COM_JOOMGALLERY_DOWNLOADS="Downloads" +COM_JOOMGALLERY_COMMENTS="Comments" +COM_JOOMGALLERY_MIGRATIONS="Migration" +COM_JOOMGALLERY_CHANGELOG="Changelog" COM_JOOMGALLERY_ACTION_UPLOAD_INOWN="Upload in Own" COM_JOOMGALLERY_ACTION_CREATE_INOWN="Create in Own" COM_JOOMGALLERY_ACTION_COMPONENT_UPLOAD_DESC="Allow users in the group to upload images into any categories." COM_JOOMGALLERY_ACTION_COMPONENT_UPLOAD_INOWN_DESC="Allow users in the group to upload images into their own categories." COM_JOOMGALLERY_ACTION_COMPONENT_CREATE_DESC="Allow users in the group to create categories in this extension." COM_JOOMGALLERY_ACTION_COMPONENT_CREATE_INOWN_DESC="Allow users in the group to create categories in their own categories." -COM_JOOMGALLERY_TAGS="Tags" -COM_JOOMGALLERY_CHANGELOG="Changelog" COM_JOOMGALLERY_SUCCESS_UPDATE="JoomGallery was updated to version %s successfully." COM_JOOMGALLERY_SUCCESS_INSTALL="JoomGallery has been installed successfully" COM_JOOMGALLERY_SUCCESS_INSTALL_EXT="%s with name %s was installed successfully." diff --git a/administrator/com_joomgallery/sql/install.mysql.utf8.sql b/administrator/com_joomgallery/sql/install.mysql.utf8.sql index 3a460a69..d80a47a9 100644 --- a/administrator/com_joomgallery/sql/install.mysql.utf8.sql +++ b/administrator/com_joomgallery/sql/install.mysql.utf8.sql @@ -293,6 +293,9 @@ CREATE TABLE IF NOT EXISTS `#__joomgallery_galleries` ( `modified_by` INT(11) UNSIGNED NOT NULL DEFAULT 0, `checked_out` INT(11) UNSIGNED NOT NULL DEFAULT 0, `checked_out_time` DATETIME DEFAULT NULL, +`metadesc` TEXT NOT NULL, +`metakey` TEXT NOT NULL, +`robots` VARCHAR(255) NOT NULL DEFAULT "0", PRIMARY KEY (`id`), KEY `galery_idx` (`published`,`access`), KEY `idx_access` (`access`), diff --git a/administrator/com_joomgallery/sql/updates/mysql/4.0.0.sql b/administrator/com_joomgallery/sql/updates/mysql/4.0.0.sql index 3a460a69..d80a47a9 100644 --- a/administrator/com_joomgallery/sql/updates/mysql/4.0.0.sql +++ b/administrator/com_joomgallery/sql/updates/mysql/4.0.0.sql @@ -293,6 +293,9 @@ CREATE TABLE IF NOT EXISTS `#__joomgallery_galleries` ( `modified_by` INT(11) UNSIGNED NOT NULL DEFAULT 0, `checked_out` INT(11) UNSIGNED NOT NULL DEFAULT 0, `checked_out_time` DATETIME DEFAULT NULL, +`metadesc` TEXT NOT NULL, +`metakey` TEXT NOT NULL, +`robots` VARCHAR(255) NOT NULL DEFAULT "0", PRIMARY KEY (`id`), KEY `galery_idx` (`published`,`access`), KEY `idx_access` (`access`), diff --git a/administrator/com_joomgallery/src/Table/CategoryTable.php b/administrator/com_joomgallery/src/Table/CategoryTable.php index 52222078..28e36475 100644 --- a/administrator/com_joomgallery/src/Table/CategoryTable.php +++ b/administrator/com_joomgallery/src/Table/CategoryTable.php @@ -364,6 +364,18 @@ public function check() $this->params = new Registry($this->params); } + // Support for field metadesc + if(empty($this->metadesc)) + { + $this->metadesc = $this->loadDefaultField('metadesc'); + } + + // Support for field metakey + if(empty($this->metakey)) + { + $this->metakey = $this->loadDefaultField('metakey'); + } + return parent::check(); } diff --git a/administrator/com_joomgallery/src/Table/JoomTableTrait.php b/administrator/com_joomgallery/src/Table/JoomTableTrait.php index 0ef49a13..0fd8e49e 100644 --- a/administrator/com_joomgallery/src/Table/JoomTableTrait.php +++ b/administrator/com_joomgallery/src/Table/JoomTableTrait.php @@ -103,6 +103,24 @@ public function check() } } + // Support for field metadesc + if(property_exists($this, 'metadesc')) + { + if(empty($this->metadesc)) + { + $this->metadesc = $this->loadDefaultField('metadesc'); + } + } + + // Support for field metakey + if(property_exists($this, 'metakey')) + { + if(empty($this->metakey)) + { + $this->metakey = $this->loadDefaultField('metakey'); + } + } + return parent::check(); } From 50ef854295de0b8375d945b8d208e128ec6984b4 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 15 Jan 2024 10:11:22 +0100 Subject: [PATCH 073/121] adjust tables --- administrator/com_joomgallery/src/Table/GalleriesTable.php | 3 +++ administrator/com_joomgallery/src/Table/ImageTable.php | 3 +++ 2 files changed, 6 insertions(+) diff --git a/administrator/com_joomgallery/src/Table/GalleriesTable.php b/administrator/com_joomgallery/src/Table/GalleriesTable.php index b433d717..a3a511aa 100644 --- a/administrator/com_joomgallery/src/Table/GalleriesTable.php +++ b/administrator/com_joomgallery/src/Table/GalleriesTable.php @@ -121,6 +121,9 @@ public function bind($array, $ignore = '') $array['modified_by'] = Factory::getUser()->id; } + // Support for multiple field: robots + $this->multipleFieldSupport($array, 'robots'); + // Support for images if(!isset($this->images)) { diff --git a/administrator/com_joomgallery/src/Table/ImageTable.php b/administrator/com_joomgallery/src/Table/ImageTable.php index 3f68c505..d5092d72 100644 --- a/administrator/com_joomgallery/src/Table/ImageTable.php +++ b/administrator/com_joomgallery/src/Table/ImageTable.php @@ -368,6 +368,9 @@ public function check() } } + // Support for multiple field: robots + $this->multipleFieldSupport($array, 'robots'); + // Support for subform field params if(empty($this->params)) { From cc2f68cd6ab0df4cb135f5f395d22a2e0682b342 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 15 Jan 2024 10:49:26 +0100 Subject: [PATCH 074/121] fix folder renaming --- .../src/Model/MigrationModel.php | 4 ++-- .../Service/Migration/Scripts/Jg3ToJg4.php | 24 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index 7b1c70ea..65b9120f 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -1018,12 +1018,12 @@ protected function insertRecord(string $type, array $data, bool $autoID = true) } } - // Special case: Use source IDs. Insert dummy record and after bind data on it. + // Special case: Use source IDs. Insert dummy record with JDatabase before binding data on it. if($isNew && !$autoID && \in_array($key, \array_keys($data))) { if(!$this->insertDummyRecord($type, $data[$key])) { - // Insert dummy failed. Stop migration of this record. + // Insert dummy failed. Stop migration. return false; } diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index 9425bc8e..f881e3bc 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -412,14 +412,18 @@ public function migrateFolder(CategoryTable $cat, array $data): bool return false; } - // Store new foldername - $newName = $cat->path; + // Get new foldername out of path + $newName = \basename($cat->path); - // Reset old foldername - $cat->path = $data['catpath']; + // Get old foldername out of static_path + $oldName = \basename($cat->static_path); + + // Create dummy object for category Renaming + $tmp_cat = new \stdClass(); + $tmp_cat->path = \substr($cat->path, 0, strrpos($cat->path, \basename($cat->path))) . $oldName; // Rename existing folders - $res = $this->component->getFileManager()->renameCategory($cat, $newName); + $res = $this->component->getFileManager()->renameCategory($tmp_cat, $newName); } else { @@ -794,15 +798,11 @@ public function scriptSpecificChecks(string $type, Checks &$checks, string $cate protected function checkCatpath(\stdClass $cat): bool { // Prepare catpath - $cat->catpath = \str_replace(\DIRECTORY_SEPARATOR, '/', $cat->catpath); - $catpath_arr = \explode('/', $cat->catpath); - $catpath = \end($catpath_arr); - $parentpath = \rtrim($cat->catpath, '/'.$catpath); + $catpath = \basename($cat->catpath); + $parentpath = \rtrim($cat->catpath, '/'.$catpath); // Prepare alias - $cat->alias = \str_replace(\DIRECTORY_SEPARATOR, '/', $cat->alias); - $alias_arr = \explode('/', $cat->alias); - $alias = \end($alias_arr); + $alias = \basename($cat->alias); // Check for alias_cid if($catpath !== $alias.'_'.$cat->cid) From 208a3fb8c80ab79d661938ddaab07a1b26322949 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 15 Jan 2024 14:29:40 +0100 Subject: [PATCH 075/121] fix manual repair --- .../com_joomgallery/src/Controller/MigrationController.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/administrator/com_joomgallery/src/Controller/MigrationController.php b/administrator/com_joomgallery/src/Controller/MigrationController.php index 2cc79357..a51af732 100644 --- a/administrator/com_joomgallery/src/Controller/MigrationController.php +++ b/administrator/com_joomgallery/src/Controller/MigrationController.php @@ -833,11 +833,11 @@ public function applyState() $new_state = $this->app->getInput()->get('state', 0, 'int'); $src_pk = $this->app->getInput()->get('src_pk', 0, 'int'); $dest_pk = $this->app->getInput()->get('dest_pk', 0, 'int'); - $dest_pk = $this->app->getInput()->get('error', '', 'string'); - $error_msg = $this->app->getInput()->get('confirmation', false, 'bool'); + $error_msg = $this->app->getInput()->get('error', '', 'string'); + $cofirm = $this->app->getInput()->get('confirmation', false, 'bool'); $json = \json_decode(\base64_decode($this->app->getInput()->get('migrateable', '', 'string')), true); - if(!$cofirm || empty($src_pk) || empty($dest_pk)) + if(!$cofirm || empty($src_pk) || ($new_state === 1 && empty($dest_pk))) { $this->app->enqueueMessage(Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_APPLYSTATE_FORMCHECK'), 'warning'); } From a46bba8f507b5388fb16c73b88d83985f3fe249f Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 15 Jan 2024 14:46:35 +0100 Subject: [PATCH 076/121] fix migrateFolders() --- .../Service/Migration/Scripts/Jg3ToJg4.php | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index f881e3bc..924f6e16 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -412,27 +412,31 @@ public function migrateFolder(CategoryTable $cat, array $data): bool return false; } - // Get new foldername out of path - $newName = \basename($cat->path); + // Rename folders if needed + if($this->params->get('new_dirs', 1) == 1) + { + // Get new foldername out of path + $newName = \basename($cat->path); - // Get old foldername out of static_path - $oldName = \basename($cat->static_path); + // Get old foldername out of static_path + $oldName = \basename($cat->static_path); - // Create dummy object for category Renaming - $tmp_cat = new \stdClass(); - $tmp_cat->path = \substr($cat->path, 0, strrpos($cat->path, \basename($cat->path))) . $oldName; + // Create dummy object for category Renaming + $tmp_cat = new \stdClass(); + $tmp_cat->path = \substr($cat->path, 0, strrpos($cat->path, \basename($cat->path))) . $oldName; - // Rename existing folders - $res = $this->component->getFileManager()->renameCategory($tmp_cat, $newName); + // Rename existing folders + return $this->component->getFileManager()->renameCategory($tmp_cat, $newName); + } } else { // Recreate, copy or move // Create new folders - $res = $this->component->getFileManager()->createCategory($cat->alias, $cat->parent_id); + return $this->component->getFileManager()->createCategory($cat->alias, $cat->parent_id); } - return $res; + return true; } /** From 3382b86639aeb2e42f3baadb567a79fa366c61bf Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 15 Jan 2024 15:32:28 +0100 Subject: [PATCH 077/121] fix issues with dummy records --- .../com_joomgallery/src/Model/MigrationModel.php | 9 ++++++++- .../com_joomgallery/src/Table/CategoryTable.php | 3 --- .../com_joomgallery/src/Table/GalleriesTable.php | 3 --- administrator/com_joomgallery/src/Table/ImageTable.php | 6 ------ 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index 65b9120f..4da75ab7 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -1150,9 +1150,16 @@ protected function insertDummyRecord(string $type, int $key) $date = Factory::getDate(); // Create and populate a dummy object. - $record = new stdClass(); + $record = new \stdClass(); $record->id = $key; + $needed = array('category'); + if(\in_array($type, $needed)) + { + $record->lft = 2147483644; + $record->rgt = 2147483645; + } + $needed = array('image', 'category', 'comment', 'gallery', 'tag'); if(\in_array($type, $needed)) { diff --git a/administrator/com_joomgallery/src/Table/CategoryTable.php b/administrator/com_joomgallery/src/Table/CategoryTable.php index 28e36475..15dfdb61 100644 --- a/administrator/com_joomgallery/src/Table/CategoryTable.php +++ b/administrator/com_joomgallery/src/Table/CategoryTable.php @@ -226,9 +226,6 @@ public function bind($array, $ignore = '') } } - // Support for multiple field: robots - $this->multipleFieldSupport($array, 'robots'); - if(isset($array['params']) && is_array($array['params'])) { $registry = new Registry; diff --git a/administrator/com_joomgallery/src/Table/GalleriesTable.php b/administrator/com_joomgallery/src/Table/GalleriesTable.php index a3a511aa..b433d717 100644 --- a/administrator/com_joomgallery/src/Table/GalleriesTable.php +++ b/administrator/com_joomgallery/src/Table/GalleriesTable.php @@ -121,9 +121,6 @@ public function bind($array, $ignore = '') $array['modified_by'] = Factory::getUser()->id; } - // Support for multiple field: robots - $this->multipleFieldSupport($array, 'robots'); - // Support for images if(!isset($this->images)) { diff --git a/administrator/com_joomgallery/src/Table/ImageTable.php b/administrator/com_joomgallery/src/Table/ImageTable.php index d5092d72..601481f6 100644 --- a/administrator/com_joomgallery/src/Table/ImageTable.php +++ b/administrator/com_joomgallery/src/Table/ImageTable.php @@ -228,9 +228,6 @@ public function bind($array, $ignore = '') $array['modified_by'] = Factory::getUser()->id; } - // Support for multiple field: robots - $this->multipleFieldSupport($array, 'robots'); - // Support for empty date field: date if(!\key_exists('date', $array) || $array['date'] == '0000-00-00' || empty($array['date'])) { @@ -368,9 +365,6 @@ public function check() } } - // Support for multiple field: robots - $this->multipleFieldSupport($array, 'robots'); - // Support for subform field params if(empty($this->params)) { From 6f526709a8cf72f45fefe097e6edee4bc5138ccb Mon Sep 17 00:00:00 2001 From: Elfangor Date: Tue, 16 Jan 2024 19:24:57 +0100 Subject: [PATCH 078/121] reordering queue after manual repair --- .../com_joomgallery/src/Model/MigrationModel.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index 4da75ab7..5ff417de 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -512,6 +512,19 @@ public function getQueue($type, $table=null): array ->from($db->quoteName($migrateable->get('src_table'))) ->order($db->quoteName($migrateable->get('src_pk', 'id')) . ' ASC'); + // Apply id filter (reordering queue) + if(\property_exists($migrateable, 'queue') && !empty($migrateable->queue)) + { + $queue = (array) $migrateable->get('queue', array()); + $query->where($db->quoteName($migrateable->get('src_pk', 'id')) . ' IN (' . implode(',', $queue) .')'); + } + + // Gather migration types info + if(empty($this->component->getMigration()->get('types'))) + { + $this->component->getMigration()->getSourceTableInfo($type); + } + // Apply ordering based on level if it is a nested type if($this->component->getMigration()->get('types')[$type]->get('nested')) { @@ -894,6 +907,9 @@ public function applyState(string $type, int $state, int $src_pk, int $dest_pk = \array_push($table->queue, $src_pk); } + // Reordering queue + $table->queue = $this->getQueue($type, $table); + break; // apply failed state From 452eb19ce15aa76f4a5068b2fee14153eb0f7684 Mon Sep 17 00:00:00 2001 From: Elfangor Date: Tue, 16 Jan 2024 21:14:40 +0100 Subject: [PATCH 079/121] fix issue: Field xxx doesn't have a default value --- .../src/Table/CategoryTable.php | 12 +++++++++++- .../src/Table/FaultiesTable.php | 18 ++++++++++++++++++ .../com_joomgallery/src/Table/FieldsTable.php | 18 ++++++++++++++++++ .../com_joomgallery/src/Table/ImageTable.php | 6 ++++++ .../src/Table/JoomTableTrait.php | 15 ++++++++++++++- 5 files changed, 67 insertions(+), 2 deletions(-) diff --git a/administrator/com_joomgallery/src/Table/CategoryTable.php b/administrator/com_joomgallery/src/Table/CategoryTable.php index 15dfdb61..ca2d8e58 100644 --- a/administrator/com_joomgallery/src/Table/CategoryTable.php +++ b/administrator/com_joomgallery/src/Table/CategoryTable.php @@ -355,12 +355,22 @@ public function check() $manager = JoomHelper::getService('FileManager'); $this->path = $manager->getCatPath(0, false, $this->parent_id, $this->alias); - // Support for params field + // Support for subform field params + if(empty($this->params)) + { + $this->params = $this->loadDefaultField('params'); + } if(isset($this->params)) { $this->params = new Registry($this->params); } + // Support for field description + if(empty($this->description)) + { + $this->description = $this->loadDefaultField('description'); + } + // Support for field metadesc if(empty($this->metadesc)) { diff --git a/administrator/com_joomgallery/src/Table/FaultiesTable.php b/administrator/com_joomgallery/src/Table/FaultiesTable.php index adbe8571..7986ef77 100644 --- a/administrator/com_joomgallery/src/Table/FaultiesTable.php +++ b/administrator/com_joomgallery/src/Table/FaultiesTable.php @@ -61,4 +61,22 @@ public function bind($array, $ignore = '') return parent::bind($array, $ignore); } + + /** + * Overloaded check function + * + * @return bool + */ + public function check() + { + // Support for subform field paths + if(empty($this->paths)) + { + $this->paths = $this->loadDefaultField('paths'); + } + elseif(is_array($this->paths)) + { + $this->paths = json_encode($this->paths, JSON_UNESCAPED_UNICODE); + } + } } diff --git a/administrator/com_joomgallery/src/Table/FieldsTable.php b/administrator/com_joomgallery/src/Table/FieldsTable.php index 6e3aa727..50c2f65d 100644 --- a/administrator/com_joomgallery/src/Table/FieldsTable.php +++ b/administrator/com_joomgallery/src/Table/FieldsTable.php @@ -66,4 +66,22 @@ public function bind($array, $ignore = '') return parent::bind($array, $ignore); } + + /** + * Overloaded check function + * + * @return bool + */ + public function check() + { + // Support for subform field value + if(empty($this->value)) + { + $this->value = $this->loadDefaultField('value'); + } + elseif(is_array($this->value)) + { + $this->value = json_encode($this->value, JSON_UNESCAPED_UNICODE); + } + } } diff --git a/administrator/com_joomgallery/src/Table/ImageTable.php b/administrator/com_joomgallery/src/Table/ImageTable.php index 601481f6..32b31a4f 100644 --- a/administrator/com_joomgallery/src/Table/ImageTable.php +++ b/administrator/com_joomgallery/src/Table/ImageTable.php @@ -375,6 +375,12 @@ public function check() $this->params = new Registry($this->params); } + // Support for field description + if(empty($this->description)) + { + $this->description = $this->loadDefaultField('description'); + } + // Support for field metadesc if(empty($this->metadesc)) { diff --git a/administrator/com_joomgallery/src/Table/JoomTableTrait.php b/administrator/com_joomgallery/src/Table/JoomTableTrait.php index 0fd8e49e..c87e6910 100644 --- a/administrator/com_joomgallery/src/Table/JoomTableTrait.php +++ b/administrator/com_joomgallery/src/Table/JoomTableTrait.php @@ -94,10 +94,23 @@ public function check() } } + // Support for field description + if(property_exists($this, 'description')) + { + if(empty($this->description)) + { + $this->description = $this->loadDefaultField('description'); + } + } + // Support for subform field params if(property_exists($this, 'params')) { - if(is_array($this->params)) + if(empty($this->params)) + { + $this->params = $this->loadDefaultField('params'); + } + elseif(is_array($this->params)) { $this->params = json_encode($this->params, JSON_UNESCAPED_UNICODE); } From 1933b85280b60596ad1661eabb9a41d6cf91c097 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Tue, 16 Jan 2024 22:01:05 +0100 Subject: [PATCH 080/121] add precheck: impossible combination --- .../en-GB/com_joomgallery.migration.Jg3ToJg4.ini | 4 +++- .../src/Service/Migration/Scripts/Jg3ToJg4.php | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini index a0786993..7298ff91 100644 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini @@ -28,4 +28,6 @@ FILES_JOOMGALLERY_MIGRATION_CHECK_CATEGORY_CATPATH_DESC="There are inconsistent FILES_JOOMGALLERY_FIELDS_NEW_DIRS_LABEL="Use new folder structure" FILES_JOOMGALLERY_FIELDS_NEW_DIRS_DESC="Do you want to convert your migrated categories to the new folder structure style (filesystem)?{tip}New path style (JG4+): root-of-filesystem/joomgallery/imagetype/parent-category-path/alias/
Old path style (JG3-): joomla-root/images/joomgallery/imagetype/parent-category-path/name_cid/
In JG3-, in most cases the alias is derivable from the category name. This might be wrong if you renamed category titles without regenrating the alias. In this case a convertion of the old JG3- folder structure to the new one is not possible.
If you choose to keep the old folder structure, you have to switch on the 'JG3 compatibility mode' in the components global configuration in order for the component to find images stored in the old folder structure style." FILES_JOOMGALLERY_MIGRATION_CHECK_COMPATIBILITY_MODE="Compatibility mode" -FILES_JOOMGALLERY_MIGRATION_CHECK_COMPATIBILITY_MODE_DESC="You have chosen to use the old folder structure in step 1. In order for this to work the compatibility mode has to be activated in the component configuration. Please activate 'JG3 compatibility mode' in the JoomGallery configuration." \ No newline at end of file +FILES_JOOMGALLERY_MIGRATION_CHECK_COMPATIBILITY_MODE_DESC="You have chosen to use the old folder structure in step 1. In order for this to work the compatibility mode has to be activated in the component configuration. Please activate 'JG3 compatibility mode' in the JoomGallery configuration." +FILES_JOOMGALLERY_SERVICE_MIGRATION_MCR="Move/Copy/Recreate" +FILES_JOOMGALLERY_SERVICE_MIGRATION_MCR_ERROR="Move, Copy and Recreate is not possible if source and destination is in the same filesystem and at the same time the old folder structure should be used. You have chosen an impossible combination of migration options. Please chose other migration options. Impossible combination: [Image usage: Move|Copy|Recreate, Same Joomla! instalaltion: Yes, Use new folder structure: No, (JG4 config)Filesystem: Local]" \ No newline at end of file diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index 924f6e16..b903cf59 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -780,6 +780,21 @@ public function scriptSpecificChecks(string $type, Checks &$checks, string $cate } } } + + //------------------------ + + // Check use case: Move/Copy/Recreate at same joomla, local filesystem at old folder structure + if($this->params->get('image_usage', 0) > 0) + { + if( $this->params->get('same_joomla', 1) == 1 && + $this->params->get('new_dirs', 1) == 0 && + $this->component->getConfig()->get('jg_filesystem', 'local-images') == 'local-images' + ) + { + // Move/Copy/Recreate is not possible since source and destination folders are identical + $checks->addCheck($category, 'copy_identical_folders', false, false, Text::_('FILES_JOOMGALLERY_SERVICE_MIGRATION_MCR'), Text::_('FILES_JOOMGALLERY_SERVICE_MIGRATION_MCR_ERROR')); + } + } } if($type == 'post') From cca7640f97e6aca5742ffd327ff2e2280875dfbe Mon Sep 17 00:00:00 2001 From: Elfangor Date: Thu, 18 Jan 2024 07:21:32 +0100 Subject: [PATCH 081/121] adjust image mapping to new db field names --- .../com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index b903cf59..92ba15c4 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -240,7 +240,8 @@ public function convertData(string $type, array $data): array case 'image': // Apply mapping for image table - $mapping = array( 'id' => $id, 'asset_id' => false, 'alias' => false, 'imgfilename' => 'filename', 'imgthumbname' => false, + $mapping = array( 'id' => $id, 'asset_id' => false, 'alias' => false, 'imgtitle' => 'title', 'imgtext' => 'description', 'imgauthor' => 'author', + 'imgdate' => 'date', 'imgfilename' => 'filename', 'imgvotes' => 'votes', 'imgvotesum' => 'votesum', 'imgthumbname' => false, 'owner' => $owner, 'params' => array('params', false, false) ); From 0ff44f18e26d4628002a3e74e7f52ec8c8cf6144 Mon Sep 17 00:00:00 2001 From: Elfangor Date: Thu, 18 Jan 2024 08:10:47 +0100 Subject: [PATCH 082/121] fix issue: data mapping against itself --- .../com_joomgallery/src/Service/Migration/Migration.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index 557ce7c6..281d6be4 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -1314,7 +1314,11 @@ protected function applyConvertData(array $data, array $mapping, string $pk_name if(\is_string($mapping[$key]) && !empty($mapping[$key])) { $data[$mapping[$key]] = $value; - unset($data[$key]); + + if($key !== $mapping[$key]) + { + unset($data[$key]); + } continue; } From 22139129f8b57136b3669c45ae59bbaf62b59cc1 Mon Sep 17 00:00:00 2001 From: Elfangor Date: Fri, 19 Jan 2024 16:11:45 +0100 Subject: [PATCH 083/121] fix issue: reload page when dependency migration is completed --- .../src/Service/Migration/Migration.php | 28 ++++++++++++---- .../Service/Migration/MigrationInterface.php | 4 +-- .../Service/Migration/Scripts/Jg3ToJg4.php | 6 ++-- .../src/Service/Migration/Type.php | 32 ++++++++++++++++--- .../src/View/Migration/HtmlView.php | 7 +++- .../com_joomgallery/tmpl/migration/step3.php | 1 + .../js/migrator/dist/migrator.js | 21 ++++++++---- .../js/migrator/dist/migrator.js.map | 2 +- .../com_joomgallery/js/migrator/src/index.js | 21 ++++++++---- 9 files changed, 91 insertions(+), 31 deletions(-) diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index 281d6be4..8322ee1d 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -163,9 +163,9 @@ public function __destruct() * @param bool $names_only True to load type names only. No migration parameters required. * * @return array The source types info - * array(tablename, primarykey, isNested, isCategorized, prerequirements, pkstoskip, ismigration, recordname) + * array(tablename, primarykey, isNested, isCategorized, dependent_on, pkstoskip, ismigration, recordname) * Needed: tablename, primarykey, isNested, isCategorized - * Optional: prerequirements, pkstoskip, ismigration, recordname + * Optional: dependent_on, pkstoskip, ismigration, recordname * * @since 4.0.0 */ @@ -173,7 +173,7 @@ public function defineTypes($names_only = false): array { // Content type definition array // Order of the content types must correspond to the migration order - // Pay attention to the prerequirements when ordering here !!! + // Pay attention to the dependent_on when ordering here !!! /* Example: $types = array( 'category' => array('#__joomgallery_catg', 'cid', true, false, array(), array(1)), @@ -492,7 +492,7 @@ protected function loadTypes() foreach($types as $key => $list) { - $type = new Type($key, $list); + $type = new Type($key, $list, $types); $this->types[$key] = $type; } @@ -552,6 +552,22 @@ public function getTypes(): array return $types; } + /** + * Returns a type object based on type name. + * + * @param string $type The content type name + * + * @return Type Type object + * + * @since 4.0.0 + */ + public function getType(string $name): Type + { + $this->loadTypes(); + + return $this->types[$name]; + } + /** * True if the given record has to be migrated * False to skip the migration for this record @@ -570,12 +586,12 @@ public function needsMigration(string $type, int $pk): bool // Content types that require another type beeing migrated completely if(!empty($this->types[$type])) { - foreach($this->types[$type]->get('prerequirement') as $key => $req) + foreach($this->types[$type]->get('dependent_on') as $key => $req) { if(!$this->migrateables[$req] || !$this->migrateables[$req]->completed || $this->migrateables[$req]->failed->count() > 0) { $this->continue = false; - $this->component->setError(Text::sprintf('FILES_JOOMGALLERY_MIGRATION_PREREQUIREMENT_ERROR', \implode(', ', $this->types[$type]->get('prerequirement')))); + $this->component->setError(Text::sprintf('FILES_JOOMGALLERY_MIGRATION_PREREQUIREMENT_ERROR', \implode(', ', $this->types[$type]->get('dependent_on')))); return false; } diff --git a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php index 6e7c9c11..9534411d 100644 --- a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php +++ b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php @@ -73,9 +73,9 @@ public function getSourceXML(); * @param bool $names_only True to load type names only. No migration parameters required. * * @return array The source types info - * array(tablename, primarykey, isNested, isCategorized, prerequirements, pkstoskip, ismigration, recordname) + * array(tablename, primarykey, isNested, isCategorized, dependent_on, pkstoskip, ismigration, recordname) * Needed: tablename, primarykey, isNested, isCategorized - * Optional: prerequirements, pkstoskip, ismigration, recordname + * Optional: dependent_on, pkstoskip, ismigration, recordname * * @since 4.0.0 */ diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index 92ba15c4..702f3b28 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -153,9 +153,9 @@ public function getSourceDirs(): array * @param bool $names_only True to load type names only. No migration parameters required. * * @return array The source types info - * array(tablename, primarykey, isNested, isCategorized, prerequirements, pkstoskip, ismigration, recordname) + * array(tablename, primarykey, isNested, isCategorized, dependent_on, pkstoskip, ismigration, recordname) * Needed: tablename, primarykey, isNested, isCategorized - * Optional: prerequirements, pkstoskip, ismigration, recordname + * Optional: dependent_on, pkstoskip, ismigration, recordname * * @since 4.0.0 */ @@ -163,7 +163,7 @@ public function defineTypes($names_only = false): array { // Content type definition array // Order of the content types must correspond to the migration order - // Pay attention to the prerequirements when ordering here !!! + // Pay attention to the dependent_on when ordering here !!! $types = array( 'category' => array('#__joomgallery_catg', 'cid', true, false, array(), array(1)), 'image' => array('#__joomgallery', 'id', false, true, array('category')), 'catimage' => array(_JOOM_TABLE_CATEGORIES, 'id', false, false, array('category', 'image'), array(1), false, 'category') diff --git a/administrator/com_joomgallery/src/Service/Migration/Type.php b/administrator/com_joomgallery/src/Service/Migration/Type.php index 2f6a5479..26e4628b 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Type.php +++ b/administrator/com_joomgallery/src/Service/Migration/Type.php @@ -90,13 +90,24 @@ class Type protected $skip = array(0); /** - * List of content types that has to be migrated before this one. + * List of types this type depends on. + * They have to be migrated before this one. * * @var array * * @since 4.0.0 */ - protected $prerequirement = array(); + protected $dependent_on = array(); + + /** + * List of types depending on this type. + * This type has to be migrated before the dependent ones. + * + * @var array + * + * @since 4.0.0 + */ + protected $dependent_of = array(); /** * Is this migration type really a migration? @@ -113,12 +124,13 @@ class Type * * @param string $name Name of this content type * @param array $list Source types info created by Migration::defineTypes() + * @param array $lists List of source types info * * @return void * * @since 4.0.0 */ - public function __construct($name, $list) + public function __construct($name, $list, $lists=array()) { $this->name = $name; $this->recordName = $name; @@ -135,7 +147,7 @@ public function __construct($name, $list) if(\count($list) > 4) { - $this->prerequirement = $list[4]; + $this->dependent_on = $list[4]; } if(\count($list) > 5) @@ -159,5 +171,17 @@ public function __construct($name, $list) { $this->recordName = $list[7]; } + + // search for types depending on this one + if(!empty($lists)) + { + foreach($lists as $type_name => $type_list) + { + if(\count($type_list) > 4 && !empty($type_list[4]) && in_array($name, $type_list[4])) + { + array_push($this->dependent_of, $type_name); + } + } + } } } \ No newline at end of file diff --git a/administrator/com_joomgallery/src/View/Migration/HtmlView.php b/administrator/com_joomgallery/src/View/Migration/HtmlView.php index 2549f195..75f8ee35 100644 --- a/administrator/com_joomgallery/src/View/Migration/HtmlView.php +++ b/administrator/com_joomgallery/src/View/Migration/HtmlView.php @@ -90,7 +90,12 @@ public function display($tpl = null) // Data for the migration view $this->precheck = $this->app->getUserState(_JOOM_OPTION.'.migration.'.$this->script->name.'.step2.success', false); $this->migrateables = $this->get('Migrateables'); - $this->migration = $this->app->getUserState(_JOOM_OPTION.'.migration.'.$this->script->name.'.step3.results', array()); + $this->migration = $this->app->getUserState(_JOOM_OPTION.'.migration.'.$this->script->name.'.step3.results', array()); + $this->dependencies = array(); + foreach ($this->migrateables as $key => $value) + { + $this->dependencies[$key] = $this->component->getMigration()->getType($key)->get('dependent_of'); + } break; case 'step4': diff --git a/administrator/com_joomgallery/tmpl/migration/step3.php b/administrator/com_joomgallery/tmpl/migration/step3.php index c539dc8d..508e7611 100644 --- a/administrator/com_joomgallery/tmpl/migration/step3.php +++ b/administrator/com_joomgallery/tmpl/migration/step3.php @@ -92,6 +92,7 @@ +
progress > 0){echo $migrateable->progress.'%';}; ?>
diff --git a/media/com_joomgallery/js/migrator/dist/migrator.js b/media/com_joomgallery/js/migrator/dist/migrator.js index 17d29987..34b65749 100644 --- a/media/com_joomgallery/js/migrator/dist/migrator.js +++ b/media/com_joomgallery/js/migrator/dist/migrator.js @@ -545,9 +545,10 @@ let startTask = function(type, button) { * @returns void */ let finishTask = function(type, button, formId) { - let bar = document.getElementById('progress-'+type); - let startBtn = button; - let stopBtn = document.getElementById('stopBtn-'+type); + let bar = document.getElementById('progress-'+type); + let startBtn = button; + let stopBtn = document.getElementById('stopBtn-'+type); + let dependency = document.getElementById('dependent_of-'+type); // Update migrateablesList getNextMigrationID(formId); @@ -569,10 +570,16 @@ let finishTask = function(type, button, formId) { // If migration is completed if(migrateablesList[type]['completed']) { - // Update next start button - enableNextBtn(type, button); - // Update step 4 button - updateStep4Btn(); + dependency = JSON.parse(dependency.innerHTML); + if(dependency.length > 0) { + // Reload page + location.reload(); + } else { + // Update next start button + enableNextBtn(type, button); + // Update step 4 button + updateStep4Btn(); + } } } diff --git a/media/com_joomgallery/js/migrator/dist/migrator.js.map b/media/com_joomgallery/js/migrator/dist/migrator.js.map index d56128e0..393c5880 100644 --- a/media/com_joomgallery/js/migrator/dist/migrator.js.map +++ b/media/com_joomgallery/js/migrator/dist/migrator.js.map @@ -1 +1 @@ -{"version":3,"file":"migrator.js","mappings":";;;UAAA;UACA;;;;;WCDA;WACA;WACA;WACA;WACA,yCAAyC,wCAAwC;WACjF;WACA;WACA;;;;;WCPA;;;;;WCAA;WACA;WACA;WACA,uDAAuD,iBAAiB;WACxE;WACA,gDAAgD,aAAa;WAC7D;;;;;;;;;;;;;;ACNA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,SAAS;AAClB;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kCAAkC,oGAAoG;AACtI;AACA;AACA;AACA;AACA;AACA;AACA;AACA,QAAQ;AACR;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kFAAkF,gBAAgB;AAClG;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB;AACA,aAAa,UAAU;AACvB,aAAa,qDAAqD,aAAa;AAC/E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,gFAAgF,SAAS;AACrG;AACA;AACA,sBAAsB;AACtB;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA,WAAW,mFAAmF,SAAS;AACvG,IAAI;AACJ;AACA,8BAA8B;AAC9B,6BAA6B;AAC7B;AACA,WAAW;AACX;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB;AACA,aAAa,UAAU;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,qDAAqD,WAAW;AAC7E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB,YAAY,UAAU;AACtB,cAAc;AACd;AACA;AACA;AACA;AACA;AACA;AACA,kBAAkB;AAClB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB,YAAY,UAAU;AACtB,cAAc;AACd;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,yDAAyD,kDAAkD;AAC3G;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA,C","sources":["webpack://Migrator/webpack/bootstrap","webpack://Migrator/webpack/runtime/define property getters","webpack://Migrator/webpack/runtime/hasOwnProperty shorthand","webpack://Migrator/webpack/runtime/make namespace object","webpack://Migrator/./src/index.js"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","// Selectors used by this script\r\nlet typeSelector = 'data-type';\r\nlet formIdTmpl = 'migrationForm';\r\nlet buttonTmpl = 'migrationBtn';\r\nlet step4Btn = 'step4Btn';\r\nlet tryLimit = 3;\r\n\r\n/**\r\n * Storage for migrateables\r\n * @var {Object} migrateablesList\r\n */\r\nvar migrateablesList = {};\r\n\r\n/**\r\n * Counter of how many times the same migration was tried to perfrom\r\n * @var {Integer} tryCounter\r\n */\r\nvar tryCounter = 0;\r\n\r\n/**\r\n * State. As long as this state is set to true, the migration will be\r\n * continued automatically regarding the pending queue in the migrateablesList.\r\n * @var {Boolean} continueState\r\n */\r\nvar continueState = true;\r\n\r\n/**\r\n * State. Set this state to true to stop automatic execution as soon as the next ajax respond comes back.\r\n * @var {Boolean} forceStop\r\n */\r\nvar forceStop = false;\r\n\r\n/**\r\n * Submit the migration task by pressing the button\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let submitTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n let type = element.getAttribute(typeSelector);\r\n let formId = formIdTmpl + '-' + type;\r\n let task = element.parentNode.querySelector('[name=\"task\"]').value;\r\n\r\n if(tryCounter == 0) {\r\n startTask(type, element);\r\n } \r\n\r\n tryCounter = tryCounter + 1;\r\n\r\n ajax(formId, task)\r\n .then(res => {\r\n // Handle the successful result here\r\n responseHandler(type, res);\r\n\r\n if(tryCounter >= tryLimit) {\r\n // We reached the limit of tries --> looks like we have a network problem\r\n updateMigrateables(type, {'success': false, 'message': Joomla.JText._('COM_JOOMGALLERY_ERROR_NETWORK_PROBLEM'), 'data': false});\r\n // Stop automatic execution and update GUI\r\n forceStop = true;\r\n }\r\n \r\n if(continueState && !forceStop) {\r\n // Kick off the next task\r\n submitTask(event, element);\r\n } else {\r\n // Stop automatic task execution and update GUI\r\n finishTask(type, element, formId);\r\n }\r\n })\r\n .catch(error => {\r\n // Handle any errors here\r\n addLog(error, type, 'error');\r\n });\r\n};\r\n\r\n/**\r\n * Stop the migration task by pressing the button\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let stopTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n let type = element.getAttribute(typeSelector);\r\n let bar = document.getElementById('progress-'+type);\r\n let startBtn = document.getElementById('migrationBtn-'+type);\r\n let stopBtn = element;\r\n\r\n // Force automatic execution to stop\r\n forceStop = true;\r\n\r\n // Update progress bar\r\n bar.classList.remove('progress-bar-striped');\r\n bar.classList.remove('progress-bar-animated');\r\n \r\n // Enable start button\r\n startBtn.classList.remove('disabled');\r\n startBtn.removeAttribute('disabled');\r\n\r\n // Disable stop button\r\n stopBtn.classList.add('disabled');\r\n stopBtn.setAttribute('disabled', 'true');\r\n}\r\n\r\n/**\r\n * Manually set one record migration to true\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let repairTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n // Get relevant elements\r\n let type = element.getAttribute(typeSelector);\r\n let mig = document.getElementById('migrationForm-'+type).querySelector('[name=\"migrateable\"]');\r\n let inputType = document.getElementById('migrepairForm').querySelector('[name=\"type\"]');\r\n let inputMig = document.getElementById('migrepairForm').querySelector('[name=\"migrateable\"]');\r\n\r\n // Fill input values\r\n inputType.value = type;\r\n inputMig.value = mig.value;\r\n\r\n // Show modal\r\n let bsmodal = new bootstrap.Modal(document.getElementById('repair-modal-box'), {keyboard: false});\r\n bsmodal.show();\r\n}\r\n\r\n/**\r\n * Perform an ajax request in json format\r\n * \r\n * @param {String} formId Id of the form element\r\n * @param {String} task Name of the task\r\n * \r\n * @returns {Object} Result object\r\n * {success: true, status: 200, message: '', messages: {}, data: { { {success, data, continue, error, debug, warning} }}\r\n */\r\nlet ajax = async function(formId, task) {\r\n\r\n // Catch form and data\r\n let formData = new FormData(document.getElementById(formId));\r\n formData.append('format', 'json');\r\n\r\n if(task == 'migration.start') {\r\n formData.append('id', getNextMigrationID(formId));\r\n }\r\n\r\n // Set request parameters\r\n let parameters = {\r\n method: 'POST',\r\n mode: 'same-origin',\r\n cache: 'default',\r\n redirect: 'follow',\r\n referrerPolicy: 'no-referrer-when-downgrade',\r\n body: formData,\r\n };\r\n\r\n // Set the url\r\n let url = document.getElementById(formId).getAttribute('action');\r\n\r\n // Perform the fetch request\r\n let response = await fetch(url, parameters);\r\n\r\n // Resolve promise as text string\r\n let txt = await response.text();\r\n let res = null;\r\n\r\n if (!response.ok) {\r\n // Catch network error\r\n return {success: false, status: response.status, message: response.message, messages: {}, data: {error: txt, data:null}};\r\n }\r\n\r\n if(txt.startsWith('{\"success\"')) {\r\n // Response is of type json --> everything fine\r\n res = JSON.parse(txt);\r\n res.status = response.status;\r\n res.data = JSON.parse(res.data);\r\n } else if (txt.includes('Fatal error')) {\r\n // PHP fatal error occurred\r\n res = {success: false, status: response.status, message: response.statusText, messages: {}, data: {error: txt, data:null}};\r\n } else {\r\n // Response is not of type json --> probably some php warnings/notices\r\n let split = txt.split('\\n{\"');\r\n let temp = JSON.parse('{\"'+split[1]);\r\n let data = JSON.parse(temp.data);\r\n res = {success: true, status: response.status, message: split[0], messages: temp.messages, data: data};\r\n }\r\n\r\n // Make sure res.data.data.queue is of type array\r\n if(typeof res.data.data != \"undefined\" && res.data.data != null && 'queue' in res.data.data) {\r\n if(res.data.data.queue.constructor !== Array) {\r\n res.data.data.queue = Object.values(res.data.data.queue);\r\n }\r\n }\r\n\r\n return res;\r\n}\r\n\r\n/**\r\n * Perform a migration task\r\n * @param {String} formId Id of the form element\r\n * \r\n * @returns {String} Id of the database record to be migrated\r\n */\r\nlet getNextMigrationID = function(formId) {\r\n let type = formId.replace(formIdTmpl + '-', '');\r\n let form = document.getElementById(formId);\r\n\r\n let migrateable = atob(form.querySelector('[name=\"migrateable\"]').value);\r\n migrateable = JSON.parse(migrateable);\r\n\r\n // Overwrite migrateable in list\r\n migrateablesList[type] = migrateable;\r\n\r\n // Loop through queue\r\n for (let id of migrateable.queue) {\r\n if (!(id in migrateable.successful) && !(id in migrateable.failed)) {\r\n migrateablesList[type]['currentID'] = id;\r\n break;\r\n }\r\n }\r\n\r\n return migrateablesList[type]['currentID'];\r\n}\r\n\r\n/**\r\n * Handle migration response\r\n * \r\n * @param {Object} response The response object in the form of\r\n * {success: true, status: 200, message: '', messages: {}, data: { {success, data, continue, error, debug, warning} }}\r\n * \r\n * @returns void\r\n */\r\nlet responseHandler = function(type, response) {\r\n if(response.success == false) {\r\n // Ajax request failed or server responded with error code\r\n addLog('Error in server response. We will try again. ('+tryCounter+'/'+tryLimit+')', type, 'info');\r\n addLog(response.message, type, 'error');\r\n addLog(response.messages, type, 'error');\r\n addLog(response.data.error, type, 'error');\r\n \r\n\r\n // Try again...\r\n }\r\n else {\r\n // Ajax request successful\r\n if(!response.data.success)\r\n {\r\n // Migration failed\r\n addLog('[Migrator.js] Migration of '+type+' with id = '+migrateablesList[type]['currentID']+' failed.', type, 'error');\r\n logMessages(type, response.data);\r\n\r\n // Stop autimatic continuation if requested from backend\r\n if(!response.data.continue || response.data.continue == null || response.data.continue == false) {\r\n console.log('Stop automatic continuation requested from backend');\r\n continueState = false;\r\n }\r\n\r\n // Update migrateables\r\n updateMigrateables(type, response.data);\r\n }\r\n else\r\n {\r\n // Save record successful\r\n logMessages(type, response.data);\r\n addLog('[Migrator.js] Migration of '+type+' with id = '+migrateablesList[type]['currentID']+' successful.', type, 'success');\r\n\r\n // Stop autimatic continuation if requested from backend\r\n if(!response.data.continue || response.data.continue == null || response.data.continue == false) {\r\n console.log('Stop automatic continuation requested from backend');\r\n continueState = false;\r\n }\r\n\r\n // Update migrateables\r\n updateMigrateables(type, response.data);\r\n\r\n // Reset tryCounter\r\n tryCounter = 0;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Add a message to the logging output and the console\r\n * \r\n * @param {Mixed} msg One or multiple messages to be added to the log\r\n * @param {String} type The type defining the logging output to use\r\n * @param {String} msgType The type of message (available: error, warning, success, info)\r\n * @param {Boolean} console True to add the message also to the console\r\n * @param {Boolean} newLine True to add the message on a new line\r\n * @param {Integer} marginTop Number of how much margin you want on the top of the message\r\n * \r\n * @returns void\r\n */\r\nlet addLog = function(msg, type, msgType, console=false, newLine=true, marginTop=0) {\r\n if(!Boolean(msg) || msg == null || msg == '') {\r\n // Message is empty. Do nothing\r\n return;\r\n } else if(typeof msg === 'string') {\r\n // Your message is a simple string\r\n let tmp_msg = '';\r\n\r\n // Test if your string a json string\r\n try {\r\n tmp_msg = JSON.parse(msg);\r\n } catch (e) {\r\n }\r\n\r\n // Convert string to array\r\n if(tmp_msg !== '') {\r\n // remove object properties 'error' and 'code' if existent\r\n if('error' in tmp_msg) {\r\n delete tmp_msg.error;\r\n }\r\n if('code' in tmp_msg) {\r\n delete tmp_msg.code;\r\n }\r\n msg = Object.values(tmp_msg);\r\n } else {\r\n msg = [msg];\r\n }\r\n } else if(typeof msg === 'object') {\r\n // Your message is an object. Convert to array\r\n msg = Object.values(msg);\r\n }\r\n\r\n // Get logging output element\r\n let logOutput = document.getElementById('logOutput-'+type);\r\n\r\n // Loop through all messages\r\n msg.forEach((message, i) => {\r\n // Print in console\r\n if(console) {\r\n console.log(message);\r\n }\r\n\r\n // Create element\r\n let line = null;\r\n if(newLine) {\r\n line = document.createElement('p');\r\n } else {\r\n line = document.createElement('span');\r\n }\r\n\r\n // Top margin to element\r\n marginTop = parseInt(marginTop);\r\n if(marginTop > 0) {\r\n line.classList.add('mt-'+String(marginTop));\r\n }\r\n\r\n // Add text color\r\n line.classList.add('color-'+msgType);\r\n \r\n // Add message to element\r\n let msgType_txt = msgType.toLocaleUpperCase();\r\n line.textContent = '['+Joomla.JText._(msgType_txt)+'] '+String(message);\r\n\r\n // Print into logging output\r\n logOutput.appendChild(line);\r\n });\r\n}\r\n\r\n/**\r\n * Clear the logging output\r\n *\r\n * @param {String} type The type defining the logging output to clear\r\n * \r\n * @returns void\r\n */\r\nlet clearLog = function(type) {\r\n // Get logging output element\r\n let logOutput = document.getElementById('logOutput-'+type);\r\n\r\n // clear\r\n logOutput.innerHTML = '';\r\n}\r\n\r\n/**\r\n * Output all available messages from the result object\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {Object} res The result object in the form of\r\n * {success: bool, data: mixed, continue: bool, error: string|array, debug: string|array, warning: string|array}\r\n * \r\n * @returns void\r\n */\r\nlet logMessages = function(type, res) {\r\n // Available message types: error, debug, warning\r\n let available = ['error', 'debug', 'warning'];\r\n let msgTypes = {'error': 'error', 'debug': 'info', 'warning': 'warning'};\r\n\r\n available.forEach((value, index) => {\r\n if(!res[value] || !Boolean(res.data) || res.data == null) {\r\n return;\r\n }\r\n\r\n addLog(res[value], type, msgTypes[value]);\r\n });\r\n}\r\n\r\n/**\r\n * Update migrateable input field, progress bar and badges\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {Object} res The result object in the form of\r\n * {success: bool, data: mixed, continue: bool, error: string|array, debug: string|array, warning: string|array}\r\n * \r\n * @returns void\r\n */\r\nlet updateMigrateables = function(type, res) {\r\n let formId = formIdTmpl + '-' + type;\r\n let form = document.getElementById(formId);\r\n\r\n if(!res.success && (!Boolean(res.data) || res.data == null || res.data == '')) {\r\n // Migration failed, but no data available in result\r\n\r\n // Create result data based on input field\r\n let migrateable = atob(form.querySelector('[name=\"migrateable\"]').value);\r\n res.data = JSON.parse(migrateable);\r\n\r\n // See: Joomgallery\\Component\\Joomgallery\\Administrator\\Model\\MigrationModel::migrate\r\n // Remove migrated primary key from queue\r\n res.data.queue = res.data.queue.filter(function(e) { return e !== migrateablesList[type]['currentID'] })\r\n\r\n // Add migrated primary key to failed object\r\n res.data.failed[migrateablesList[type]['currentID']] = res.message;\r\n }\r\n\r\n if(!Boolean(res.data.progress) || res.data.progress == null || res.data.progress == '') {\r\n // Update progress if not delivered with result object\r\n let total = res.data.queue.lenght + Object.keys(res.data.successful).length + Object.keys(res.data.failed).length;\r\n let finished = Object.keys(res.data.successful).length + Object.keys(res.data.failed).length;\r\n res.data.progress = Math.round((100 / total) * (finished));\r\n }\r\n\r\n // Get badges\r\n let queueBadge = document.getElementById('badgeQueue-'+type);\r\n let resBadge = document.getElementById('badgeSuccessful-'+type);\r\n if(!res.success) {\r\n resBadge = document.getElementById('badgeFailed-'+type);\r\n }\r\n\r\n // Update migrateable input field\r\n let field = form.querySelector('[name=\"migrateable\"]');\r\n field.value = btoa(JSON.stringify(res.data));\r\n\r\n // Update badges\r\n queueBadge.innerHTML = parseInt(queueBadge.innerHTML) - 1;\r\n resBadge.innerHTML = parseInt(resBadge.innerHTML) + 1;\r\n\r\n // Update progress bar\r\n let bar = document.getElementById('progress-'+type);\r\n bar.setAttribute('aria-valuenow', res.data.progress);\r\n bar.style.width = res.data.progress + '%';\r\n bar.innerText = res.data.progress + '%';\r\n}\r\n\r\n/**\r\n * Update GUI to start migration\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {DOM Element} button The button beeing pressed to start the task\r\n * \r\n * @returns void\r\n */\r\nlet startTask = function(type, button) {\r\n let bar = document.getElementById('progress-'+type);\r\n let startBtn = button;\r\n let stopBtn = document.getElementById('stopBtn-'+type);\r\n\r\n // Update progress bar\r\n bar.classList.add('progress-bar-striped');\r\n bar.classList.add('progress-bar-animated');\r\n \r\n // Disable start button\r\n startBtn.classList.add('disabled');\r\n startBtn.setAttribute('disabled', 'true');\r\n\r\n // Enable stop button\r\n stopBtn.classList.remove('disabled');\r\n stopBtn.removeAttribute('disabled');\r\n\r\n // Reinitialize variables\r\n tryCounter = 0;\r\n continueState = true;\r\n forceStop = false;\r\n}\r\n\r\n/**\r\n * Update GUI to end migration\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {DOM Element} button The button beeing pressed to start the task\r\n * @param {String} formId Id of the form element\r\n * \r\n * @returns void\r\n */\r\nlet finishTask = function(type, button, formId) {\r\n let bar = document.getElementById('progress-'+type);\r\n let startBtn = button;\r\n let stopBtn = document.getElementById('stopBtn-'+type);\r\n\r\n // Update migrateablesList\r\n getNextMigrationID(formId);\r\n\r\n // Update progress bar\r\n bar.classList.remove('progress-bar-striped');\r\n bar.classList.remove('progress-bar-animated');\r\n \r\n // Enable start button\r\n if(!migrateablesList[type]['completed']) {\r\n // Only enable start button if migration is not finished\r\n startBtn.classList.remove('disabled');\r\n startBtn.removeAttribute('disabled');\r\n }\r\n\r\n // Disable stop button\r\n stopBtn.classList.add('disabled');\r\n stopBtn.setAttribute('disabled', 'true');\r\n\r\n // If migration is completed\r\n if(migrateablesList[type]['completed']) {\r\n // Update next start button\r\n enableNextBtn(type, button);\r\n // Update step 4 button\r\n updateStep4Btn();\r\n }\r\n}\r\n\r\n/**\r\n * Enable start button of next migration content type\r\n * \r\n * @param {String} type The type defining the content type to be updated\r\n * @param {DOM Element} button The current start button\r\n * \r\n * @returns void\r\n */\r\nlet enableNextBtn = function(type, button) {\r\n let types_inputs = document.getElementsByName('type');\r\n let next_type = '';\r\n\r\n // Find next migration content type\r\n let this_type = false;\r\n for (const type_input of types_inputs) {\r\n if(this_type) {\r\n next_type = type_input.value;\r\n break;\r\n }\r\n if(Boolean(type_input.value) && type_input.value == type) {\r\n this_type = true;\r\n }\r\n }\r\n\r\n // Get next button\r\n let nextBtn = document.getElementById(buttonTmpl + '-' + next_type);\r\n\r\n // Enable button\r\n nextBtn.classList.remove('disabled');\r\n nextBtn.removeAttribute('disabled');\r\n}\r\n\r\n/**\r\n * Update button to go to step 4\r\n * \r\n * @returns void\r\n */\r\nlet updateStep4Btn = function() {\r\n let types_inputs = document.getElementsByName('type');\r\n let types = {};\r\n\r\n // Add all available migrateables to types object\r\n types_inputs.forEach((type) => {\r\n if(Boolean(type.value)) {\r\n types[type.value] = false;\r\n }\r\n });\r\n\r\n // Check if all migrateables are available and completed\r\n let tot_complete = true;\r\n Object.keys(types).forEach(type => { \r\n if(Boolean(migrateablesList[type])) {\r\n if(!migrateablesList[type]['completed'])\r\n {\r\n // Migrateable not yet completed\r\n tot_complete = false;\r\n }\r\n }\r\n else\r\n {\r\n // Migrateable does not yet exist. Thus not completed\r\n tot_complete = false;\r\n }\r\n });\r\n\r\n if(tot_complete) {\r\n // Enable step 4 button\r\n document.getElementById(step4Btn).classList.remove('disabled');\r\n document.getElementById(step4Btn).removeAttribute('disabled');\r\n }\r\n}"],"names":[],"sourceRoot":""} \ No newline at end of file +{"version":3,"file":"migrator.js","mappings":";;;UAAA;UACA;;;;;WCDA;WACA;WACA;WACA;WACA,yCAAyC,wCAAwC;WACjF;WACA;WACA;;;;;WCPA;;;;;WCAA;WACA;WACA;WACA,uDAAuD,iBAAiB;WACxE;WACA,gDAAgD,aAAa;WAC7D;;;;;;;;;;;;;;ACNA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,SAAS;AAClB;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kCAAkC,oGAAoG;AACtI;AACA;AACA;AACA;AACA;AACA;AACA;AACA,QAAQ;AACR;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kFAAkF,gBAAgB;AAClG;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB;AACA,aAAa,UAAU;AACvB,aAAa,qDAAqD,aAAa;AAC/E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,gFAAgF,SAAS;AACrG;AACA;AACA,sBAAsB;AACtB;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA,WAAW,mFAAmF,SAAS;AACvG,IAAI;AACJ;AACA,8BAA8B;AAC9B,6BAA6B;AAC7B;AACA,WAAW;AACX;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB;AACA,aAAa,UAAU;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,qDAAqD,WAAW;AAC7E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB,YAAY,UAAU;AACtB,cAAc;AACd;AACA;AACA;AACA;AACA;AACA;AACA,kBAAkB;AAClB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB,YAAY,UAAU;AACtB,cAAc;AACd;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,yDAAyD,kDAAkD;AAC3G;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA,C","sources":["webpack://Migrator/webpack/bootstrap","webpack://Migrator/webpack/runtime/define property getters","webpack://Migrator/webpack/runtime/hasOwnProperty shorthand","webpack://Migrator/webpack/runtime/make namespace object","webpack://Migrator/./src/index.js"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","// Selectors used by this script\r\nlet typeSelector = 'data-type';\r\nlet formIdTmpl = 'migrationForm';\r\nlet buttonTmpl = 'migrationBtn';\r\nlet step4Btn = 'step4Btn';\r\nlet tryLimit = 3;\r\n\r\n/**\r\n * Storage for migrateables\r\n * @var {Object} migrateablesList\r\n */\r\nvar migrateablesList = {};\r\n\r\n/**\r\n * Counter of how many times the same migration was tried to perfrom\r\n * @var {Integer} tryCounter\r\n */\r\nvar tryCounter = 0;\r\n\r\n/**\r\n * State. As long as this state is set to true, the migration will be\r\n * continued automatically regarding the pending queue in the migrateablesList.\r\n * @var {Boolean} continueState\r\n */\r\nvar continueState = true;\r\n\r\n/**\r\n * State. Set this state to true to stop automatic execution as soon as the next ajax respond comes back.\r\n * @var {Boolean} forceStop\r\n */\r\nvar forceStop = false;\r\n\r\n/**\r\n * Submit the migration task by pressing the button\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let submitTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n let type = element.getAttribute(typeSelector);\r\n let formId = formIdTmpl + '-' + type;\r\n let task = element.parentNode.querySelector('[name=\"task\"]').value;\r\n\r\n if(tryCounter == 0) {\r\n startTask(type, element);\r\n } \r\n\r\n tryCounter = tryCounter + 1;\r\n\r\n ajax(formId, task)\r\n .then(res => {\r\n // Handle the successful result here\r\n responseHandler(type, res);\r\n\r\n if(tryCounter >= tryLimit) {\r\n // We reached the limit of tries --> looks like we have a network problem\r\n updateMigrateables(type, {'success': false, 'message': Joomla.JText._('COM_JOOMGALLERY_ERROR_NETWORK_PROBLEM'), 'data': false});\r\n // Stop automatic execution and update GUI\r\n forceStop = true;\r\n }\r\n \r\n if(continueState && !forceStop) {\r\n // Kick off the next task\r\n submitTask(event, element);\r\n } else {\r\n // Stop automatic task execution and update GUI\r\n finishTask(type, element, formId);\r\n }\r\n })\r\n .catch(error => {\r\n // Handle any errors here\r\n addLog(error, type, 'error');\r\n });\r\n};\r\n\r\n/**\r\n * Stop the migration task by pressing the button\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let stopTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n let type = element.getAttribute(typeSelector);\r\n let bar = document.getElementById('progress-'+type);\r\n let startBtn = document.getElementById('migrationBtn-'+type);\r\n let stopBtn = element;\r\n\r\n // Force automatic execution to stop\r\n forceStop = true;\r\n\r\n // Update progress bar\r\n bar.classList.remove('progress-bar-striped');\r\n bar.classList.remove('progress-bar-animated');\r\n \r\n // Enable start button\r\n startBtn.classList.remove('disabled');\r\n startBtn.removeAttribute('disabled');\r\n\r\n // Disable stop button\r\n stopBtn.classList.add('disabled');\r\n stopBtn.setAttribute('disabled', 'true');\r\n}\r\n\r\n/**\r\n * Manually set one record migration to true\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let repairTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n // Get relevant elements\r\n let type = element.getAttribute(typeSelector);\r\n let mig = document.getElementById('migrationForm-'+type).querySelector('[name=\"migrateable\"]');\r\n let inputType = document.getElementById('migrepairForm').querySelector('[name=\"type\"]');\r\n let inputMig = document.getElementById('migrepairForm').querySelector('[name=\"migrateable\"]');\r\n\r\n // Fill input values\r\n inputType.value = type;\r\n inputMig.value = mig.value;\r\n\r\n // Show modal\r\n let bsmodal = new bootstrap.Modal(document.getElementById('repair-modal-box'), {keyboard: false});\r\n bsmodal.show();\r\n}\r\n\r\n/**\r\n * Perform an ajax request in json format\r\n * \r\n * @param {String} formId Id of the form element\r\n * @param {String} task Name of the task\r\n * \r\n * @returns {Object} Result object\r\n * {success: true, status: 200, message: '', messages: {}, data: { { {success, data, continue, error, debug, warning} }}\r\n */\r\nlet ajax = async function(formId, task) {\r\n\r\n // Catch form and data\r\n let formData = new FormData(document.getElementById(formId));\r\n formData.append('format', 'json');\r\n\r\n if(task == 'migration.start') {\r\n formData.append('id', getNextMigrationID(formId));\r\n }\r\n\r\n // Set request parameters\r\n let parameters = {\r\n method: 'POST',\r\n mode: 'same-origin',\r\n cache: 'default',\r\n redirect: 'follow',\r\n referrerPolicy: 'no-referrer-when-downgrade',\r\n body: formData,\r\n };\r\n\r\n // Set the url\r\n let url = document.getElementById(formId).getAttribute('action');\r\n\r\n // Perform the fetch request\r\n let response = await fetch(url, parameters);\r\n\r\n // Resolve promise as text string\r\n let txt = await response.text();\r\n let res = null;\r\n\r\n if (!response.ok) {\r\n // Catch network error\r\n return {success: false, status: response.status, message: response.message, messages: {}, data: {error: txt, data:null}};\r\n }\r\n\r\n if(txt.startsWith('{\"success\"')) {\r\n // Response is of type json --> everything fine\r\n res = JSON.parse(txt);\r\n res.status = response.status;\r\n res.data = JSON.parse(res.data);\r\n } else if (txt.includes('Fatal error')) {\r\n // PHP fatal error occurred\r\n res = {success: false, status: response.status, message: response.statusText, messages: {}, data: {error: txt, data:null}};\r\n } else {\r\n // Response is not of type json --> probably some php warnings/notices\r\n let split = txt.split('\\n{\"');\r\n let temp = JSON.parse('{\"'+split[1]);\r\n let data = JSON.parse(temp.data);\r\n res = {success: true, status: response.status, message: split[0], messages: temp.messages, data: data};\r\n }\r\n\r\n // Make sure res.data.data.queue is of type array\r\n if(typeof res.data.data != \"undefined\" && res.data.data != null && 'queue' in res.data.data) {\r\n if(res.data.data.queue.constructor !== Array) {\r\n res.data.data.queue = Object.values(res.data.data.queue);\r\n }\r\n }\r\n\r\n return res;\r\n}\r\n\r\n/**\r\n * Perform a migration task\r\n * @param {String} formId Id of the form element\r\n * \r\n * @returns {String} Id of the database record to be migrated\r\n */\r\nlet getNextMigrationID = function(formId) {\r\n let type = formId.replace(formIdTmpl + '-', '');\r\n let form = document.getElementById(formId);\r\n\r\n let migrateable = atob(form.querySelector('[name=\"migrateable\"]').value);\r\n migrateable = JSON.parse(migrateable);\r\n\r\n // Overwrite migrateable in list\r\n migrateablesList[type] = migrateable;\r\n\r\n // Loop through queue\r\n for (let id of migrateable.queue) {\r\n if (!(id in migrateable.successful) && !(id in migrateable.failed)) {\r\n migrateablesList[type]['currentID'] = id;\r\n break;\r\n }\r\n }\r\n\r\n return migrateablesList[type]['currentID'];\r\n}\r\n\r\n/**\r\n * Handle migration response\r\n * \r\n * @param {Object} response The response object in the form of\r\n * {success: true, status: 200, message: '', messages: {}, data: { {success, data, continue, error, debug, warning} }}\r\n * \r\n * @returns void\r\n */\r\nlet responseHandler = function(type, response) {\r\n if(response.success == false) {\r\n // Ajax request failed or server responded with error code\r\n addLog('Error in server response. We will try again. ('+tryCounter+'/'+tryLimit+')', type, 'info');\r\n addLog(response.message, type, 'error');\r\n addLog(response.messages, type, 'error');\r\n addLog(response.data.error, type, 'error');\r\n \r\n\r\n // Try again...\r\n }\r\n else {\r\n // Ajax request successful\r\n if(!response.data.success)\r\n {\r\n // Migration failed\r\n addLog('[Migrator.js] Migration of '+type+' with id = '+migrateablesList[type]['currentID']+' failed.', type, 'error');\r\n logMessages(type, response.data);\r\n\r\n // Stop autimatic continuation if requested from backend\r\n if(!response.data.continue || response.data.continue == null || response.data.continue == false) {\r\n console.log('Stop automatic continuation requested from backend');\r\n continueState = false;\r\n }\r\n\r\n // Update migrateables\r\n updateMigrateables(type, response.data);\r\n }\r\n else\r\n {\r\n // Save record successful\r\n logMessages(type, response.data);\r\n addLog('[Migrator.js] Migration of '+type+' with id = '+migrateablesList[type]['currentID']+' successful.', type, 'success');\r\n\r\n // Stop autimatic continuation if requested from backend\r\n if(!response.data.continue || response.data.continue == null || response.data.continue == false) {\r\n console.log('Stop automatic continuation requested from backend');\r\n continueState = false;\r\n }\r\n\r\n // Update migrateables\r\n updateMigrateables(type, response.data);\r\n\r\n // Reset tryCounter\r\n tryCounter = 0;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Add a message to the logging output and the console\r\n * \r\n * @param {Mixed} msg One or multiple messages to be added to the log\r\n * @param {String} type The type defining the logging output to use\r\n * @param {String} msgType The type of message (available: error, warning, success, info)\r\n * @param {Boolean} console True to add the message also to the console\r\n * @param {Boolean} newLine True to add the message on a new line\r\n * @param {Integer} marginTop Number of how much margin you want on the top of the message\r\n * \r\n * @returns void\r\n */\r\nlet addLog = function(msg, type, msgType, console=false, newLine=true, marginTop=0) {\r\n if(!Boolean(msg) || msg == null || msg == '') {\r\n // Message is empty. Do nothing\r\n return;\r\n } else if(typeof msg === 'string') {\r\n // Your message is a simple string\r\n let tmp_msg = '';\r\n\r\n // Test if your string a json string\r\n try {\r\n tmp_msg = JSON.parse(msg);\r\n } catch (e) {\r\n }\r\n\r\n // Convert string to array\r\n if(tmp_msg !== '') {\r\n // remove object properties 'error' and 'code' if existent\r\n if('error' in tmp_msg) {\r\n delete tmp_msg.error;\r\n }\r\n if('code' in tmp_msg) {\r\n delete tmp_msg.code;\r\n }\r\n msg = Object.values(tmp_msg);\r\n } else {\r\n msg = [msg];\r\n }\r\n } else if(typeof msg === 'object') {\r\n // Your message is an object. Convert to array\r\n msg = Object.values(msg);\r\n }\r\n\r\n // Get logging output element\r\n let logOutput = document.getElementById('logOutput-'+type);\r\n\r\n // Loop through all messages\r\n msg.forEach((message, i) => {\r\n // Print in console\r\n if(console) {\r\n console.log(message);\r\n }\r\n\r\n // Create element\r\n let line = null;\r\n if(newLine) {\r\n line = document.createElement('p');\r\n } else {\r\n line = document.createElement('span');\r\n }\r\n\r\n // Top margin to element\r\n marginTop = parseInt(marginTop);\r\n if(marginTop > 0) {\r\n line.classList.add('mt-'+String(marginTop));\r\n }\r\n\r\n // Add text color\r\n line.classList.add('color-'+msgType);\r\n \r\n // Add message to element\r\n let msgType_txt = msgType.toLocaleUpperCase();\r\n line.textContent = '['+Joomla.JText._(msgType_txt)+'] '+String(message);\r\n\r\n // Print into logging output\r\n logOutput.appendChild(line);\r\n });\r\n}\r\n\r\n/**\r\n * Clear the logging output\r\n *\r\n * @param {String} type The type defining the logging output to clear\r\n * \r\n * @returns void\r\n */\r\nlet clearLog = function(type) {\r\n // Get logging output element\r\n let logOutput = document.getElementById('logOutput-'+type);\r\n\r\n // clear\r\n logOutput.innerHTML = '';\r\n}\r\n\r\n/**\r\n * Output all available messages from the result object\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {Object} res The result object in the form of\r\n * {success: bool, data: mixed, continue: bool, error: string|array, debug: string|array, warning: string|array}\r\n * \r\n * @returns void\r\n */\r\nlet logMessages = function(type, res) {\r\n // Available message types: error, debug, warning\r\n let available = ['error', 'debug', 'warning'];\r\n let msgTypes = {'error': 'error', 'debug': 'info', 'warning': 'warning'};\r\n\r\n available.forEach((value, index) => {\r\n if(!res[value] || !Boolean(res.data) || res.data == null) {\r\n return;\r\n }\r\n\r\n addLog(res[value], type, msgTypes[value]);\r\n });\r\n}\r\n\r\n/**\r\n * Update migrateable input field, progress bar and badges\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {Object} res The result object in the form of\r\n * {success: bool, data: mixed, continue: bool, error: string|array, debug: string|array, warning: string|array}\r\n * \r\n * @returns void\r\n */\r\nlet updateMigrateables = function(type, res) {\r\n let formId = formIdTmpl + '-' + type;\r\n let form = document.getElementById(formId);\r\n\r\n if(!res.success && (!Boolean(res.data) || res.data == null || res.data == '')) {\r\n // Migration failed, but no data available in result\r\n\r\n // Create result data based on input field\r\n let migrateable = atob(form.querySelector('[name=\"migrateable\"]').value);\r\n res.data = JSON.parse(migrateable);\r\n\r\n // See: Joomgallery\\Component\\Joomgallery\\Administrator\\Model\\MigrationModel::migrate\r\n // Remove migrated primary key from queue\r\n res.data.queue = res.data.queue.filter(function(e) { return e !== migrateablesList[type]['currentID'] })\r\n\r\n // Add migrated primary key to failed object\r\n res.data.failed[migrateablesList[type]['currentID']] = res.message;\r\n }\r\n\r\n if(!Boolean(res.data.progress) || res.data.progress == null || res.data.progress == '') {\r\n // Update progress if not delivered with result object\r\n let total = res.data.queue.lenght + Object.keys(res.data.successful).length + Object.keys(res.data.failed).length;\r\n let finished = Object.keys(res.data.successful).length + Object.keys(res.data.failed).length;\r\n res.data.progress = Math.round((100 / total) * (finished));\r\n }\r\n\r\n // Get badges\r\n let queueBadge = document.getElementById('badgeQueue-'+type);\r\n let resBadge = document.getElementById('badgeSuccessful-'+type);\r\n if(!res.success) {\r\n resBadge = document.getElementById('badgeFailed-'+type);\r\n }\r\n\r\n // Update migrateable input field\r\n let field = form.querySelector('[name=\"migrateable\"]');\r\n field.value = btoa(JSON.stringify(res.data));\r\n\r\n // Update badges\r\n queueBadge.innerHTML = parseInt(queueBadge.innerHTML) - 1;\r\n resBadge.innerHTML = parseInt(resBadge.innerHTML) + 1;\r\n\r\n // Update progress bar\r\n let bar = document.getElementById('progress-'+type);\r\n bar.setAttribute('aria-valuenow', res.data.progress);\r\n bar.style.width = res.data.progress + '%';\r\n bar.innerText = res.data.progress + '%';\r\n}\r\n\r\n/**\r\n * Update GUI to start migration\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {DOM Element} button The button beeing pressed to start the task\r\n * \r\n * @returns void\r\n */\r\nlet startTask = function(type, button) {\r\n let bar = document.getElementById('progress-'+type);\r\n let startBtn = button;\r\n let stopBtn = document.getElementById('stopBtn-'+type);\r\n\r\n // Update progress bar\r\n bar.classList.add('progress-bar-striped');\r\n bar.classList.add('progress-bar-animated');\r\n \r\n // Disable start button\r\n startBtn.classList.add('disabled');\r\n startBtn.setAttribute('disabled', 'true');\r\n\r\n // Enable stop button\r\n stopBtn.classList.remove('disabled');\r\n stopBtn.removeAttribute('disabled');\r\n\r\n // Reinitialize variables\r\n tryCounter = 0;\r\n continueState = true;\r\n forceStop = false;\r\n}\r\n\r\n/**\r\n * Update GUI to end migration\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {DOM Element} button The button beeing pressed to start the task\r\n * @param {String} formId Id of the form element\r\n * \r\n * @returns void\r\n */\r\nlet finishTask = function(type, button, formId) {\r\n let bar = document.getElementById('progress-'+type);\r\n let startBtn = button;\r\n let stopBtn = document.getElementById('stopBtn-'+type);\r\n let dependency = document.getElementById('dependent_of-'+type);\r\n\r\n // Update migrateablesList\r\n getNextMigrationID(formId);\r\n\r\n // Update progress bar\r\n bar.classList.remove('progress-bar-striped');\r\n bar.classList.remove('progress-bar-animated');\r\n \r\n // Enable start button\r\n if(!migrateablesList[type]['completed']) {\r\n // Only enable start button if migration is not finished\r\n startBtn.classList.remove('disabled');\r\n startBtn.removeAttribute('disabled');\r\n }\r\n\r\n // Disable stop button\r\n stopBtn.classList.add('disabled');\r\n stopBtn.setAttribute('disabled', 'true');\r\n\r\n // If migration is completed\r\n if(migrateablesList[type]['completed']) {\r\n dependency = JSON.parse(dependency.innerHTML);\r\n if(dependency.length > 0) {\r\n // Reload page\r\n location.reload();\r\n } else {\r\n // Update next start button\r\n enableNextBtn(type, button);\r\n // Update step 4 button\r\n updateStep4Btn();\r\n } \r\n }\r\n}\r\n\r\n/**\r\n * Enable start button of next migration content type\r\n * \r\n * @param {String} type The type defining the content type to be updated\r\n * @param {DOM Element} button The current start button\r\n * \r\n * @returns void\r\n */\r\nlet enableNextBtn = function(type, button) {\r\n let types_inputs = document.getElementsByName('type');\r\n let next_type = '';\r\n\r\n // Find next migration content type\r\n let this_type = false;\r\n for (const type_input of types_inputs) {\r\n if(this_type) {\r\n next_type = type_input.value;\r\n break;\r\n }\r\n if(Boolean(type_input.value) && type_input.value == type) {\r\n this_type = true;\r\n }\r\n }\r\n\r\n // Get next button\r\n let nextBtn = document.getElementById(buttonTmpl + '-' + next_type);\r\n\r\n // Enable button\r\n nextBtn.classList.remove('disabled');\r\n nextBtn.removeAttribute('disabled');\r\n}\r\n\r\n/**\r\n * Update button to go to step 4\r\n * \r\n * @returns void\r\n */\r\nlet updateStep4Btn = function() {\r\n let types_inputs = document.getElementsByName('type');\r\n let types = {};\r\n\r\n // Add all available migrateables to types object\r\n types_inputs.forEach((type) => {\r\n if(Boolean(type.value)) {\r\n types[type.value] = false;\r\n }\r\n });\r\n\r\n // Check if all migrateables are available and completed\r\n let tot_complete = true;\r\n Object.keys(types).forEach(type => { \r\n if(Boolean(migrateablesList[type])) {\r\n if(!migrateablesList[type]['completed'])\r\n {\r\n // Migrateable not yet completed\r\n tot_complete = false;\r\n }\r\n }\r\n else\r\n {\r\n // Migrateable does not yet exist. Thus not completed\r\n tot_complete = false;\r\n }\r\n });\r\n\r\n if(tot_complete) {\r\n // Enable step 4 button\r\n document.getElementById(step4Btn).classList.remove('disabled');\r\n document.getElementById(step4Btn).removeAttribute('disabled');\r\n }\r\n}"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/media/com_joomgallery/js/migrator/src/index.js b/media/com_joomgallery/js/migrator/src/index.js index 96b29351..b59f8f6b 100644 --- a/media/com_joomgallery/js/migrator/src/index.js +++ b/media/com_joomgallery/js/migrator/src/index.js @@ -499,9 +499,10 @@ let startTask = function(type, button) { * @returns void */ let finishTask = function(type, button, formId) { - let bar = document.getElementById('progress-'+type); - let startBtn = button; - let stopBtn = document.getElementById('stopBtn-'+type); + let bar = document.getElementById('progress-'+type); + let startBtn = button; + let stopBtn = document.getElementById('stopBtn-'+type); + let dependency = document.getElementById('dependent_of-'+type); // Update migrateablesList getNextMigrationID(formId); @@ -523,10 +524,16 @@ let finishTask = function(type, button, formId) { // If migration is completed if(migrateablesList[type]['completed']) { - // Update next start button - enableNextBtn(type, button); - // Update step 4 button - updateStep4Btn(); + dependency = JSON.parse(dependency.innerHTML); + if(dependency.length > 0) { + // Reload page + location.reload(); + } else { + // Update next start button + enableNextBtn(type, button); + // Update step 4 button + updateStep4Btn(); + } } } From 0ecffbc3a9977c362257cf8359d83ffe16f4cb9f Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Sat, 20 Jan 2024 18:42:07 +0100 Subject: [PATCH 084/121] add updateMigrateablesList() when step 3 view loads --- .../com_joomgallery/tmpl/migration/step3.php | 14 ++++++ .../js/migrator/dist/migrator.js | 50 ++++++++++++++++--- .../js/migrator/dist/migrator.js.map | 2 +- .../com_joomgallery/js/migrator/src/index.js | 47 ++++++++++++++--- 4 files changed, 99 insertions(+), 14 deletions(-) diff --git a/administrator/com_joomgallery/tmpl/migration/step3.php b/administrator/com_joomgallery/tmpl/migration/step3.php index 508e7611..a2abff75 100644 --- a/administrator/com_joomgallery/tmpl/migration/step3.php +++ b/administrator/com_joomgallery/tmpl/migration/step3.php @@ -160,4 +160,18 @@ echo HTMLHelper::_('bootstrap.renderModal', 'repair-modal-box', $options, $body); ?> + +
\ No newline at end of file diff --git a/media/com_joomgallery/js/migrator/dist/migrator.js b/media/com_joomgallery/js/migrator/dist/migrator.js index 34b65749..f547e791 100644 --- a/media/com_joomgallery/js/migrator/dist/migrator.js +++ b/media/com_joomgallery/js/migrator/dist/migrator.js @@ -42,7 +42,8 @@ __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ repairTask: () => (/* binding */ repairTask), /* harmony export */ stopTask: () => (/* binding */ stopTask), -/* harmony export */ submitTask: () => (/* binding */ submitTask) +/* harmony export */ submitTask: () => (/* binding */ submitTask), +/* harmony export */ updateMigrateablesList: () => (/* binding */ updateMigrateablesList) /* harmony export */ }); // Selectors used by this script let typeSelector = 'data-type'; @@ -76,6 +77,39 @@ var continueState = true; */ var forceStop = false; +/** + * Adds all completed migrateables to list + * + * @param {Object} event Event object + * @param {Object} element DOM element object + */ +let updateMigrateablesList = function() { + let types_inputs = document.getElementsByName('type'); + let types = {}; + + // Add all available migrateables to types object + types_inputs.forEach((type) => { + if(Boolean(type.value)) { + types[type.value] = false; + } + }); + + // Loop through all migrateables + Object.keys(types).forEach(type => { + let formId = formIdTmpl + '-' + type; + let form = document.getElementById(formId); + + let migrateable = atob(form.querySelector('[name="migrateable"]').value); + migrateable = JSON.parse(migrateable); + + if(migrateable['completed']) + { + // Add migrateable in list + migrateablesList[type] = migrateable; + } + }); +} + /** * Submit the migration task by pressing the button * @@ -607,12 +641,14 @@ let enableNextBtn = function(type, button) { } } - // Get next button - let nextBtn = document.getElementById(buttonTmpl + '-' + next_type); + if(next_type !== '') { + // Get next button + let nextBtn = document.getElementById(buttonTmpl + '-' + next_type); - // Enable button - nextBtn.classList.remove('disabled'); - nextBtn.removeAttribute('disabled'); + // Enable button + nextBtn.classList.remove('disabled'); + nextBtn.removeAttribute('disabled'); + } } /** @@ -633,7 +669,7 @@ let updateStep4Btn = function() { // Check if all migrateables are available and completed let tot_complete = true; - Object.keys(types).forEach(type => { + Object.keys(types).forEach(type => { if(Boolean(migrateablesList[type])) { if(!migrateablesList[type]['completed']) { diff --git a/media/com_joomgallery/js/migrator/dist/migrator.js.map b/media/com_joomgallery/js/migrator/dist/migrator.js.map index 393c5880..ae467e46 100644 --- a/media/com_joomgallery/js/migrator/dist/migrator.js.map +++ b/media/com_joomgallery/js/migrator/dist/migrator.js.map @@ -1 +1 @@ -{"version":3,"file":"migrator.js","mappings":";;;UAAA;UACA;;;;;WCDA;WACA;WACA;WACA;WACA,yCAAyC,wCAAwC;WACjF;WACA;WACA;;;;;WCPA;;;;;WCAA;WACA;WACA;WACA,uDAAuD,iBAAiB;WACxE;WACA,gDAAgD,aAAa;WAC7D;;;;;;;;;;;;;;ACNA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,SAAS;AAClB;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kCAAkC,oGAAoG;AACtI;AACA;AACA;AACA;AACA;AACA;AACA;AACA,QAAQ;AACR;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kFAAkF,gBAAgB;AAClG;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB;AACA,aAAa,UAAU;AACvB,aAAa,qDAAqD,aAAa;AAC/E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,gFAAgF,SAAS;AACrG;AACA;AACA,sBAAsB;AACtB;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA,WAAW,mFAAmF,SAAS;AACvG,IAAI;AACJ;AACA,8BAA8B;AAC9B,6BAA6B;AAC7B;AACA,WAAW;AACX;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB;AACA,aAAa,UAAU;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,qDAAqD,WAAW;AAC7E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB,YAAY,UAAU;AACtB,cAAc;AACd;AACA;AACA;AACA;AACA;AACA;AACA,kBAAkB;AAClB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB,YAAY,UAAU;AACtB,cAAc;AACd;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,yDAAyD,kDAAkD;AAC3G;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA,C","sources":["webpack://Migrator/webpack/bootstrap","webpack://Migrator/webpack/runtime/define property getters","webpack://Migrator/webpack/runtime/hasOwnProperty shorthand","webpack://Migrator/webpack/runtime/make namespace object","webpack://Migrator/./src/index.js"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","// Selectors used by this script\r\nlet typeSelector = 'data-type';\r\nlet formIdTmpl = 'migrationForm';\r\nlet buttonTmpl = 'migrationBtn';\r\nlet step4Btn = 'step4Btn';\r\nlet tryLimit = 3;\r\n\r\n/**\r\n * Storage for migrateables\r\n * @var {Object} migrateablesList\r\n */\r\nvar migrateablesList = {};\r\n\r\n/**\r\n * Counter of how many times the same migration was tried to perfrom\r\n * @var {Integer} tryCounter\r\n */\r\nvar tryCounter = 0;\r\n\r\n/**\r\n * State. As long as this state is set to true, the migration will be\r\n * continued automatically regarding the pending queue in the migrateablesList.\r\n * @var {Boolean} continueState\r\n */\r\nvar continueState = true;\r\n\r\n/**\r\n * State. Set this state to true to stop automatic execution as soon as the next ajax respond comes back.\r\n * @var {Boolean} forceStop\r\n */\r\nvar forceStop = false;\r\n\r\n/**\r\n * Submit the migration task by pressing the button\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let submitTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n let type = element.getAttribute(typeSelector);\r\n let formId = formIdTmpl + '-' + type;\r\n let task = element.parentNode.querySelector('[name=\"task\"]').value;\r\n\r\n if(tryCounter == 0) {\r\n startTask(type, element);\r\n } \r\n\r\n tryCounter = tryCounter + 1;\r\n\r\n ajax(formId, task)\r\n .then(res => {\r\n // Handle the successful result here\r\n responseHandler(type, res);\r\n\r\n if(tryCounter >= tryLimit) {\r\n // We reached the limit of tries --> looks like we have a network problem\r\n updateMigrateables(type, {'success': false, 'message': Joomla.JText._('COM_JOOMGALLERY_ERROR_NETWORK_PROBLEM'), 'data': false});\r\n // Stop automatic execution and update GUI\r\n forceStop = true;\r\n }\r\n \r\n if(continueState && !forceStop) {\r\n // Kick off the next task\r\n submitTask(event, element);\r\n } else {\r\n // Stop automatic task execution and update GUI\r\n finishTask(type, element, formId);\r\n }\r\n })\r\n .catch(error => {\r\n // Handle any errors here\r\n addLog(error, type, 'error');\r\n });\r\n};\r\n\r\n/**\r\n * Stop the migration task by pressing the button\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let stopTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n let type = element.getAttribute(typeSelector);\r\n let bar = document.getElementById('progress-'+type);\r\n let startBtn = document.getElementById('migrationBtn-'+type);\r\n let stopBtn = element;\r\n\r\n // Force automatic execution to stop\r\n forceStop = true;\r\n\r\n // Update progress bar\r\n bar.classList.remove('progress-bar-striped');\r\n bar.classList.remove('progress-bar-animated');\r\n \r\n // Enable start button\r\n startBtn.classList.remove('disabled');\r\n startBtn.removeAttribute('disabled');\r\n\r\n // Disable stop button\r\n stopBtn.classList.add('disabled');\r\n stopBtn.setAttribute('disabled', 'true');\r\n}\r\n\r\n/**\r\n * Manually set one record migration to true\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let repairTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n // Get relevant elements\r\n let type = element.getAttribute(typeSelector);\r\n let mig = document.getElementById('migrationForm-'+type).querySelector('[name=\"migrateable\"]');\r\n let inputType = document.getElementById('migrepairForm').querySelector('[name=\"type\"]');\r\n let inputMig = document.getElementById('migrepairForm').querySelector('[name=\"migrateable\"]');\r\n\r\n // Fill input values\r\n inputType.value = type;\r\n inputMig.value = mig.value;\r\n\r\n // Show modal\r\n let bsmodal = new bootstrap.Modal(document.getElementById('repair-modal-box'), {keyboard: false});\r\n bsmodal.show();\r\n}\r\n\r\n/**\r\n * Perform an ajax request in json format\r\n * \r\n * @param {String} formId Id of the form element\r\n * @param {String} task Name of the task\r\n * \r\n * @returns {Object} Result object\r\n * {success: true, status: 200, message: '', messages: {}, data: { { {success, data, continue, error, debug, warning} }}\r\n */\r\nlet ajax = async function(formId, task) {\r\n\r\n // Catch form and data\r\n let formData = new FormData(document.getElementById(formId));\r\n formData.append('format', 'json');\r\n\r\n if(task == 'migration.start') {\r\n formData.append('id', getNextMigrationID(formId));\r\n }\r\n\r\n // Set request parameters\r\n let parameters = {\r\n method: 'POST',\r\n mode: 'same-origin',\r\n cache: 'default',\r\n redirect: 'follow',\r\n referrerPolicy: 'no-referrer-when-downgrade',\r\n body: formData,\r\n };\r\n\r\n // Set the url\r\n let url = document.getElementById(formId).getAttribute('action');\r\n\r\n // Perform the fetch request\r\n let response = await fetch(url, parameters);\r\n\r\n // Resolve promise as text string\r\n let txt = await response.text();\r\n let res = null;\r\n\r\n if (!response.ok) {\r\n // Catch network error\r\n return {success: false, status: response.status, message: response.message, messages: {}, data: {error: txt, data:null}};\r\n }\r\n\r\n if(txt.startsWith('{\"success\"')) {\r\n // Response is of type json --> everything fine\r\n res = JSON.parse(txt);\r\n res.status = response.status;\r\n res.data = JSON.parse(res.data);\r\n } else if (txt.includes('Fatal error')) {\r\n // PHP fatal error occurred\r\n res = {success: false, status: response.status, message: response.statusText, messages: {}, data: {error: txt, data:null}};\r\n } else {\r\n // Response is not of type json --> probably some php warnings/notices\r\n let split = txt.split('\\n{\"');\r\n let temp = JSON.parse('{\"'+split[1]);\r\n let data = JSON.parse(temp.data);\r\n res = {success: true, status: response.status, message: split[0], messages: temp.messages, data: data};\r\n }\r\n\r\n // Make sure res.data.data.queue is of type array\r\n if(typeof res.data.data != \"undefined\" && res.data.data != null && 'queue' in res.data.data) {\r\n if(res.data.data.queue.constructor !== Array) {\r\n res.data.data.queue = Object.values(res.data.data.queue);\r\n }\r\n }\r\n\r\n return res;\r\n}\r\n\r\n/**\r\n * Perform a migration task\r\n * @param {String} formId Id of the form element\r\n * \r\n * @returns {String} Id of the database record to be migrated\r\n */\r\nlet getNextMigrationID = function(formId) {\r\n let type = formId.replace(formIdTmpl + '-', '');\r\n let form = document.getElementById(formId);\r\n\r\n let migrateable = atob(form.querySelector('[name=\"migrateable\"]').value);\r\n migrateable = JSON.parse(migrateable);\r\n\r\n // Overwrite migrateable in list\r\n migrateablesList[type] = migrateable;\r\n\r\n // Loop through queue\r\n for (let id of migrateable.queue) {\r\n if (!(id in migrateable.successful) && !(id in migrateable.failed)) {\r\n migrateablesList[type]['currentID'] = id;\r\n break;\r\n }\r\n }\r\n\r\n return migrateablesList[type]['currentID'];\r\n}\r\n\r\n/**\r\n * Handle migration response\r\n * \r\n * @param {Object} response The response object in the form of\r\n * {success: true, status: 200, message: '', messages: {}, data: { {success, data, continue, error, debug, warning} }}\r\n * \r\n * @returns void\r\n */\r\nlet responseHandler = function(type, response) {\r\n if(response.success == false) {\r\n // Ajax request failed or server responded with error code\r\n addLog('Error in server response. We will try again. ('+tryCounter+'/'+tryLimit+')', type, 'info');\r\n addLog(response.message, type, 'error');\r\n addLog(response.messages, type, 'error');\r\n addLog(response.data.error, type, 'error');\r\n \r\n\r\n // Try again...\r\n }\r\n else {\r\n // Ajax request successful\r\n if(!response.data.success)\r\n {\r\n // Migration failed\r\n addLog('[Migrator.js] Migration of '+type+' with id = '+migrateablesList[type]['currentID']+' failed.', type, 'error');\r\n logMessages(type, response.data);\r\n\r\n // Stop autimatic continuation if requested from backend\r\n if(!response.data.continue || response.data.continue == null || response.data.continue == false) {\r\n console.log('Stop automatic continuation requested from backend');\r\n continueState = false;\r\n }\r\n\r\n // Update migrateables\r\n updateMigrateables(type, response.data);\r\n }\r\n else\r\n {\r\n // Save record successful\r\n logMessages(type, response.data);\r\n addLog('[Migrator.js] Migration of '+type+' with id = '+migrateablesList[type]['currentID']+' successful.', type, 'success');\r\n\r\n // Stop autimatic continuation if requested from backend\r\n if(!response.data.continue || response.data.continue == null || response.data.continue == false) {\r\n console.log('Stop automatic continuation requested from backend');\r\n continueState = false;\r\n }\r\n\r\n // Update migrateables\r\n updateMigrateables(type, response.data);\r\n\r\n // Reset tryCounter\r\n tryCounter = 0;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Add a message to the logging output and the console\r\n * \r\n * @param {Mixed} msg One or multiple messages to be added to the log\r\n * @param {String} type The type defining the logging output to use\r\n * @param {String} msgType The type of message (available: error, warning, success, info)\r\n * @param {Boolean} console True to add the message also to the console\r\n * @param {Boolean} newLine True to add the message on a new line\r\n * @param {Integer} marginTop Number of how much margin you want on the top of the message\r\n * \r\n * @returns void\r\n */\r\nlet addLog = function(msg, type, msgType, console=false, newLine=true, marginTop=0) {\r\n if(!Boolean(msg) || msg == null || msg == '') {\r\n // Message is empty. Do nothing\r\n return;\r\n } else if(typeof msg === 'string') {\r\n // Your message is a simple string\r\n let tmp_msg = '';\r\n\r\n // Test if your string a json string\r\n try {\r\n tmp_msg = JSON.parse(msg);\r\n } catch (e) {\r\n }\r\n\r\n // Convert string to array\r\n if(tmp_msg !== '') {\r\n // remove object properties 'error' and 'code' if existent\r\n if('error' in tmp_msg) {\r\n delete tmp_msg.error;\r\n }\r\n if('code' in tmp_msg) {\r\n delete tmp_msg.code;\r\n }\r\n msg = Object.values(tmp_msg);\r\n } else {\r\n msg = [msg];\r\n }\r\n } else if(typeof msg === 'object') {\r\n // Your message is an object. Convert to array\r\n msg = Object.values(msg);\r\n }\r\n\r\n // Get logging output element\r\n let logOutput = document.getElementById('logOutput-'+type);\r\n\r\n // Loop through all messages\r\n msg.forEach((message, i) => {\r\n // Print in console\r\n if(console) {\r\n console.log(message);\r\n }\r\n\r\n // Create element\r\n let line = null;\r\n if(newLine) {\r\n line = document.createElement('p');\r\n } else {\r\n line = document.createElement('span');\r\n }\r\n\r\n // Top margin to element\r\n marginTop = parseInt(marginTop);\r\n if(marginTop > 0) {\r\n line.classList.add('mt-'+String(marginTop));\r\n }\r\n\r\n // Add text color\r\n line.classList.add('color-'+msgType);\r\n \r\n // Add message to element\r\n let msgType_txt = msgType.toLocaleUpperCase();\r\n line.textContent = '['+Joomla.JText._(msgType_txt)+'] '+String(message);\r\n\r\n // Print into logging output\r\n logOutput.appendChild(line);\r\n });\r\n}\r\n\r\n/**\r\n * Clear the logging output\r\n *\r\n * @param {String} type The type defining the logging output to clear\r\n * \r\n * @returns void\r\n */\r\nlet clearLog = function(type) {\r\n // Get logging output element\r\n let logOutput = document.getElementById('logOutput-'+type);\r\n\r\n // clear\r\n logOutput.innerHTML = '';\r\n}\r\n\r\n/**\r\n * Output all available messages from the result object\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {Object} res The result object in the form of\r\n * {success: bool, data: mixed, continue: bool, error: string|array, debug: string|array, warning: string|array}\r\n * \r\n * @returns void\r\n */\r\nlet logMessages = function(type, res) {\r\n // Available message types: error, debug, warning\r\n let available = ['error', 'debug', 'warning'];\r\n let msgTypes = {'error': 'error', 'debug': 'info', 'warning': 'warning'};\r\n\r\n available.forEach((value, index) => {\r\n if(!res[value] || !Boolean(res.data) || res.data == null) {\r\n return;\r\n }\r\n\r\n addLog(res[value], type, msgTypes[value]);\r\n });\r\n}\r\n\r\n/**\r\n * Update migrateable input field, progress bar and badges\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {Object} res The result object in the form of\r\n * {success: bool, data: mixed, continue: bool, error: string|array, debug: string|array, warning: string|array}\r\n * \r\n * @returns void\r\n */\r\nlet updateMigrateables = function(type, res) {\r\n let formId = formIdTmpl + '-' + type;\r\n let form = document.getElementById(formId);\r\n\r\n if(!res.success && (!Boolean(res.data) || res.data == null || res.data == '')) {\r\n // Migration failed, but no data available in result\r\n\r\n // Create result data based on input field\r\n let migrateable = atob(form.querySelector('[name=\"migrateable\"]').value);\r\n res.data = JSON.parse(migrateable);\r\n\r\n // See: Joomgallery\\Component\\Joomgallery\\Administrator\\Model\\MigrationModel::migrate\r\n // Remove migrated primary key from queue\r\n res.data.queue = res.data.queue.filter(function(e) { return e !== migrateablesList[type]['currentID'] })\r\n\r\n // Add migrated primary key to failed object\r\n res.data.failed[migrateablesList[type]['currentID']] = res.message;\r\n }\r\n\r\n if(!Boolean(res.data.progress) || res.data.progress == null || res.data.progress == '') {\r\n // Update progress if not delivered with result object\r\n let total = res.data.queue.lenght + Object.keys(res.data.successful).length + Object.keys(res.data.failed).length;\r\n let finished = Object.keys(res.data.successful).length + Object.keys(res.data.failed).length;\r\n res.data.progress = Math.round((100 / total) * (finished));\r\n }\r\n\r\n // Get badges\r\n let queueBadge = document.getElementById('badgeQueue-'+type);\r\n let resBadge = document.getElementById('badgeSuccessful-'+type);\r\n if(!res.success) {\r\n resBadge = document.getElementById('badgeFailed-'+type);\r\n }\r\n\r\n // Update migrateable input field\r\n let field = form.querySelector('[name=\"migrateable\"]');\r\n field.value = btoa(JSON.stringify(res.data));\r\n\r\n // Update badges\r\n queueBadge.innerHTML = parseInt(queueBadge.innerHTML) - 1;\r\n resBadge.innerHTML = parseInt(resBadge.innerHTML) + 1;\r\n\r\n // Update progress bar\r\n let bar = document.getElementById('progress-'+type);\r\n bar.setAttribute('aria-valuenow', res.data.progress);\r\n bar.style.width = res.data.progress + '%';\r\n bar.innerText = res.data.progress + '%';\r\n}\r\n\r\n/**\r\n * Update GUI to start migration\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {DOM Element} button The button beeing pressed to start the task\r\n * \r\n * @returns void\r\n */\r\nlet startTask = function(type, button) {\r\n let bar = document.getElementById('progress-'+type);\r\n let startBtn = button;\r\n let stopBtn = document.getElementById('stopBtn-'+type);\r\n\r\n // Update progress bar\r\n bar.classList.add('progress-bar-striped');\r\n bar.classList.add('progress-bar-animated');\r\n \r\n // Disable start button\r\n startBtn.classList.add('disabled');\r\n startBtn.setAttribute('disabled', 'true');\r\n\r\n // Enable stop button\r\n stopBtn.classList.remove('disabled');\r\n stopBtn.removeAttribute('disabled');\r\n\r\n // Reinitialize variables\r\n tryCounter = 0;\r\n continueState = true;\r\n forceStop = false;\r\n}\r\n\r\n/**\r\n * Update GUI to end migration\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {DOM Element} button The button beeing pressed to start the task\r\n * @param {String} formId Id of the form element\r\n * \r\n * @returns void\r\n */\r\nlet finishTask = function(type, button, formId) {\r\n let bar = document.getElementById('progress-'+type);\r\n let startBtn = button;\r\n let stopBtn = document.getElementById('stopBtn-'+type);\r\n let dependency = document.getElementById('dependent_of-'+type);\r\n\r\n // Update migrateablesList\r\n getNextMigrationID(formId);\r\n\r\n // Update progress bar\r\n bar.classList.remove('progress-bar-striped');\r\n bar.classList.remove('progress-bar-animated');\r\n \r\n // Enable start button\r\n if(!migrateablesList[type]['completed']) {\r\n // Only enable start button if migration is not finished\r\n startBtn.classList.remove('disabled');\r\n startBtn.removeAttribute('disabled');\r\n }\r\n\r\n // Disable stop button\r\n stopBtn.classList.add('disabled');\r\n stopBtn.setAttribute('disabled', 'true');\r\n\r\n // If migration is completed\r\n if(migrateablesList[type]['completed']) {\r\n dependency = JSON.parse(dependency.innerHTML);\r\n if(dependency.length > 0) {\r\n // Reload page\r\n location.reload();\r\n } else {\r\n // Update next start button\r\n enableNextBtn(type, button);\r\n // Update step 4 button\r\n updateStep4Btn();\r\n } \r\n }\r\n}\r\n\r\n/**\r\n * Enable start button of next migration content type\r\n * \r\n * @param {String} type The type defining the content type to be updated\r\n * @param {DOM Element} button The current start button\r\n * \r\n * @returns void\r\n */\r\nlet enableNextBtn = function(type, button) {\r\n let types_inputs = document.getElementsByName('type');\r\n let next_type = '';\r\n\r\n // Find next migration content type\r\n let this_type = false;\r\n for (const type_input of types_inputs) {\r\n if(this_type) {\r\n next_type = type_input.value;\r\n break;\r\n }\r\n if(Boolean(type_input.value) && type_input.value == type) {\r\n this_type = true;\r\n }\r\n }\r\n\r\n // Get next button\r\n let nextBtn = document.getElementById(buttonTmpl + '-' + next_type);\r\n\r\n // Enable button\r\n nextBtn.classList.remove('disabled');\r\n nextBtn.removeAttribute('disabled');\r\n}\r\n\r\n/**\r\n * Update button to go to step 4\r\n * \r\n * @returns void\r\n */\r\nlet updateStep4Btn = function() {\r\n let types_inputs = document.getElementsByName('type');\r\n let types = {};\r\n\r\n // Add all available migrateables to types object\r\n types_inputs.forEach((type) => {\r\n if(Boolean(type.value)) {\r\n types[type.value] = false;\r\n }\r\n });\r\n\r\n // Check if all migrateables are available and completed\r\n let tot_complete = true;\r\n Object.keys(types).forEach(type => { \r\n if(Boolean(migrateablesList[type])) {\r\n if(!migrateablesList[type]['completed'])\r\n {\r\n // Migrateable not yet completed\r\n tot_complete = false;\r\n }\r\n }\r\n else\r\n {\r\n // Migrateable does not yet exist. Thus not completed\r\n tot_complete = false;\r\n }\r\n });\r\n\r\n if(tot_complete) {\r\n // Enable step 4 button\r\n document.getElementById(step4Btn).classList.remove('disabled');\r\n document.getElementById(step4Btn).removeAttribute('disabled');\r\n }\r\n}"],"names":[],"sourceRoot":""} \ No newline at end of file +{"version":3,"file":"migrator.js","mappings":";;;UAAA;UACA;;;;;WCDA;WACA;WACA;WACA;WACA,yCAAyC,wCAAwC;WACjF;WACA;WACA;;;;;WCPA;;;;;WCAA;WACA;WACA;WACA,uDAAuD,iBAAiB;WACxE;WACA,gDAAgD,aAAa;WAC7D;;;;;;;;;;;;;;;ACNA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,SAAS;AAClB;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA;AACA;AACA,SAAS,UAAU;AACnB;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kCAAkC,oGAAoG;AACtI;AACA;AACA;AACA;AACA;AACA;AACA;AACA,QAAQ;AACR;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,SAAS;AACpB,WAAW,SAAS;AACpB;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kFAAkF,gBAAgB;AAClG;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB;AACA,aAAa,UAAU;AACvB,aAAa,qDAAqD,aAAa;AAC/E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,gFAAgF,SAAS;AACrG;AACA;AACA,sBAAsB;AACtB;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA,WAAW,mFAAmF,SAAS;AACvG,IAAI;AACJ;AACA,8BAA8B;AAC9B,6BAA6B;AAC7B;AACA,WAAW;AACX;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB;AACA,aAAa,UAAU;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,qDAAqD,WAAW;AAC7E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB,aAAa,UAAU;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB,YAAY,UAAU;AACtB,cAAc;AACd;AACA;AACA;AACA;AACA;AACA;AACA,kBAAkB;AAClB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,YAAY,UAAU;AACtB,YAAY,UAAU;AACtB,cAAc;AACd;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,yDAAyD,kDAAkD;AAC3G;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,YAAY,aAAa;AACzB,YAAY,aAAa;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA,C","sources":["webpack://Migrator/webpack/bootstrap","webpack://Migrator/webpack/runtime/define property getters","webpack://Migrator/webpack/runtime/hasOwnProperty shorthand","webpack://Migrator/webpack/runtime/make namespace object","webpack://Migrator/./src/index.js"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","// Selectors used by this script\r\nlet typeSelector = 'data-type';\r\nlet formIdTmpl = 'migrationForm';\r\nlet buttonTmpl = 'migrationBtn';\r\nlet step4Btn = 'step4Btn';\r\nlet tryLimit = 3;\r\n\r\n/**\r\n * Storage for migrateables\r\n * @var {Object} migrateablesList\r\n */\r\nvar migrateablesList = {};\r\n\r\n/**\r\n * Counter of how many times the same migration was tried to perfrom\r\n * @var {Integer} tryCounter\r\n */\r\nvar tryCounter = 0;\r\n\r\n/**\r\n * State. As long as this state is set to true, the migration will be\r\n * continued automatically regarding the pending queue in the migrateablesList.\r\n * @var {Boolean} continueState\r\n */\r\nvar continueState = true;\r\n\r\n/**\r\n * State. Set this state to true to stop automatic execution as soon as the next ajax respond comes back.\r\n * @var {Boolean} forceStop\r\n */\r\nvar forceStop = false;\r\n\r\n/**\r\n * Adds all completed migrateables to list\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let updateMigrateablesList = function() {\r\n let types_inputs = document.getElementsByName('type');\r\n let types = {};\r\n\r\n // Add all available migrateables to types object\r\n types_inputs.forEach((type) => {\r\n if(Boolean(type.value)) {\r\n types[type.value] = false;\r\n }\r\n });\r\n\r\n // Loop through all migrateables\r\n Object.keys(types).forEach(type => {\r\n let formId = formIdTmpl + '-' + type;\r\n let form = document.getElementById(formId);\r\n\r\n let migrateable = atob(form.querySelector('[name=\"migrateable\"]').value);\r\n migrateable = JSON.parse(migrateable);\r\n\r\n if(migrateable['completed'])\r\n {\r\n // Add migrateable in list\r\n migrateablesList[type] = migrateable;\r\n }\r\n });\r\n}\r\n\r\n/**\r\n * Submit the migration task by pressing the button\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let submitTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n let type = element.getAttribute(typeSelector);\r\n let formId = formIdTmpl + '-' + type;\r\n let task = element.parentNode.querySelector('[name=\"task\"]').value;\r\n\r\n if(tryCounter == 0) {\r\n startTask(type, element);\r\n } \r\n\r\n tryCounter = tryCounter + 1;\r\n\r\n ajax(formId, task)\r\n .then(res => {\r\n // Handle the successful result here\r\n responseHandler(type, res);\r\n\r\n if(tryCounter >= tryLimit) {\r\n // We reached the limit of tries --> looks like we have a network problem\r\n updateMigrateables(type, {'success': false, 'message': Joomla.JText._('COM_JOOMGALLERY_ERROR_NETWORK_PROBLEM'), 'data': false});\r\n // Stop automatic execution and update GUI\r\n forceStop = true;\r\n }\r\n \r\n if(continueState && !forceStop) {\r\n // Kick off the next task\r\n submitTask(event, element);\r\n } else {\r\n // Stop automatic task execution and update GUI\r\n finishTask(type, element, formId);\r\n }\r\n })\r\n .catch(error => {\r\n // Handle any errors here\r\n addLog(error, type, 'error');\r\n });\r\n};\r\n\r\n/**\r\n * Stop the migration task by pressing the button\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let stopTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n let type = element.getAttribute(typeSelector);\r\n let bar = document.getElementById('progress-'+type);\r\n let startBtn = document.getElementById('migrationBtn-'+type);\r\n let stopBtn = element;\r\n\r\n // Force automatic execution to stop\r\n forceStop = true;\r\n\r\n // Update progress bar\r\n bar.classList.remove('progress-bar-striped');\r\n bar.classList.remove('progress-bar-animated');\r\n \r\n // Enable start button\r\n startBtn.classList.remove('disabled');\r\n startBtn.removeAttribute('disabled');\r\n\r\n // Disable stop button\r\n stopBtn.classList.add('disabled');\r\n stopBtn.setAttribute('disabled', 'true');\r\n}\r\n\r\n/**\r\n * Manually set one record migration to true\r\n * \r\n * @param {Object} event Event object\r\n * @param {Object} element DOM element object\r\n */\r\nexport let repairTask = function(event, element) {\r\n event.preventDefault();\r\n\r\n // Get relevant elements\r\n let type = element.getAttribute(typeSelector);\r\n let mig = document.getElementById('migrationForm-'+type).querySelector('[name=\"migrateable\"]');\r\n let inputType = document.getElementById('migrepairForm').querySelector('[name=\"type\"]');\r\n let inputMig = document.getElementById('migrepairForm').querySelector('[name=\"migrateable\"]');\r\n\r\n // Fill input values\r\n inputType.value = type;\r\n inputMig.value = mig.value;\r\n\r\n // Show modal\r\n let bsmodal = new bootstrap.Modal(document.getElementById('repair-modal-box'), {keyboard: false});\r\n bsmodal.show();\r\n}\r\n\r\n/**\r\n * Perform an ajax request in json format\r\n * \r\n * @param {String} formId Id of the form element\r\n * @param {String} task Name of the task\r\n * \r\n * @returns {Object} Result object\r\n * {success: true, status: 200, message: '', messages: {}, data: { { {success, data, continue, error, debug, warning} }}\r\n */\r\nlet ajax = async function(formId, task) {\r\n\r\n // Catch form and data\r\n let formData = new FormData(document.getElementById(formId));\r\n formData.append('format', 'json');\r\n\r\n if(task == 'migration.start') {\r\n formData.append('id', getNextMigrationID(formId));\r\n }\r\n\r\n // Set request parameters\r\n let parameters = {\r\n method: 'POST',\r\n mode: 'same-origin',\r\n cache: 'default',\r\n redirect: 'follow',\r\n referrerPolicy: 'no-referrer-when-downgrade',\r\n body: formData,\r\n };\r\n\r\n // Set the url\r\n let url = document.getElementById(formId).getAttribute('action');\r\n\r\n // Perform the fetch request\r\n let response = await fetch(url, parameters);\r\n\r\n // Resolve promise as text string\r\n let txt = await response.text();\r\n let res = null;\r\n\r\n if (!response.ok) {\r\n // Catch network error\r\n return {success: false, status: response.status, message: response.message, messages: {}, data: {error: txt, data:null}};\r\n }\r\n\r\n if(txt.startsWith('{\"success\"')) {\r\n // Response is of type json --> everything fine\r\n res = JSON.parse(txt);\r\n res.status = response.status;\r\n res.data = JSON.parse(res.data);\r\n } else if (txt.includes('Fatal error')) {\r\n // PHP fatal error occurred\r\n res = {success: false, status: response.status, message: response.statusText, messages: {}, data: {error: txt, data:null}};\r\n } else {\r\n // Response is not of type json --> probably some php warnings/notices\r\n let split = txt.split('\\n{\"');\r\n let temp = JSON.parse('{\"'+split[1]);\r\n let data = JSON.parse(temp.data);\r\n res = {success: true, status: response.status, message: split[0], messages: temp.messages, data: data};\r\n }\r\n\r\n // Make sure res.data.data.queue is of type array\r\n if(typeof res.data.data != \"undefined\" && res.data.data != null && 'queue' in res.data.data) {\r\n if(res.data.data.queue.constructor !== Array) {\r\n res.data.data.queue = Object.values(res.data.data.queue);\r\n }\r\n }\r\n\r\n return res;\r\n}\r\n\r\n/**\r\n * Perform a migration task\r\n * @param {String} formId Id of the form element\r\n * \r\n * @returns {String} Id of the database record to be migrated\r\n */\r\nlet getNextMigrationID = function(formId) {\r\n let type = formId.replace(formIdTmpl + '-', '');\r\n let form = document.getElementById(formId);\r\n\r\n let migrateable = atob(form.querySelector('[name=\"migrateable\"]').value);\r\n migrateable = JSON.parse(migrateable);\r\n\r\n // Overwrite migrateable in list\r\n migrateablesList[type] = migrateable;\r\n\r\n // Loop through queue\r\n for (let id of migrateable.queue) {\r\n if (!(id in migrateable.successful) && !(id in migrateable.failed)) {\r\n migrateablesList[type]['currentID'] = id;\r\n break;\r\n }\r\n }\r\n\r\n return migrateablesList[type]['currentID'];\r\n}\r\n\r\n/**\r\n * Handle migration response\r\n * \r\n * @param {Object} response The response object in the form of\r\n * {success: true, status: 200, message: '', messages: {}, data: { {success, data, continue, error, debug, warning} }}\r\n * \r\n * @returns void\r\n */\r\nlet responseHandler = function(type, response) {\r\n if(response.success == false) {\r\n // Ajax request failed or server responded with error code\r\n addLog('Error in server response. We will try again. ('+tryCounter+'/'+tryLimit+')', type, 'info');\r\n addLog(response.message, type, 'error');\r\n addLog(response.messages, type, 'error');\r\n addLog(response.data.error, type, 'error');\r\n \r\n\r\n // Try again...\r\n }\r\n else {\r\n // Ajax request successful\r\n if(!response.data.success)\r\n {\r\n // Migration failed\r\n addLog('[Migrator.js] Migration of '+type+' with id = '+migrateablesList[type]['currentID']+' failed.', type, 'error');\r\n logMessages(type, response.data);\r\n\r\n // Stop autimatic continuation if requested from backend\r\n if(!response.data.continue || response.data.continue == null || response.data.continue == false) {\r\n console.log('Stop automatic continuation requested from backend');\r\n continueState = false;\r\n }\r\n\r\n // Update migrateables\r\n updateMigrateables(type, response.data);\r\n }\r\n else\r\n {\r\n // Save record successful\r\n logMessages(type, response.data);\r\n addLog('[Migrator.js] Migration of '+type+' with id = '+migrateablesList[type]['currentID']+' successful.', type, 'success');\r\n\r\n // Stop autimatic continuation if requested from backend\r\n if(!response.data.continue || response.data.continue == null || response.data.continue == false) {\r\n console.log('Stop automatic continuation requested from backend');\r\n continueState = false;\r\n }\r\n\r\n // Update migrateables\r\n updateMigrateables(type, response.data);\r\n\r\n // Reset tryCounter\r\n tryCounter = 0;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Add a message to the logging output and the console\r\n * \r\n * @param {Mixed} msg One or multiple messages to be added to the log\r\n * @param {String} type The type defining the logging output to use\r\n * @param {String} msgType The type of message (available: error, warning, success, info)\r\n * @param {Boolean} console True to add the message also to the console\r\n * @param {Boolean} newLine True to add the message on a new line\r\n * @param {Integer} marginTop Number of how much margin you want on the top of the message\r\n * \r\n * @returns void\r\n */\r\nlet addLog = function(msg, type, msgType, console=false, newLine=true, marginTop=0) {\r\n if(!Boolean(msg) || msg == null || msg == '') {\r\n // Message is empty. Do nothing\r\n return;\r\n } else if(typeof msg === 'string') {\r\n // Your message is a simple string\r\n let tmp_msg = '';\r\n\r\n // Test if your string a json string\r\n try {\r\n tmp_msg = JSON.parse(msg);\r\n } catch (e) {\r\n }\r\n\r\n // Convert string to array\r\n if(tmp_msg !== '') {\r\n // remove object properties 'error' and 'code' if existent\r\n if('error' in tmp_msg) {\r\n delete tmp_msg.error;\r\n }\r\n if('code' in tmp_msg) {\r\n delete tmp_msg.code;\r\n }\r\n msg = Object.values(tmp_msg);\r\n } else {\r\n msg = [msg];\r\n }\r\n } else if(typeof msg === 'object') {\r\n // Your message is an object. Convert to array\r\n msg = Object.values(msg);\r\n }\r\n\r\n // Get logging output element\r\n let logOutput = document.getElementById('logOutput-'+type);\r\n\r\n // Loop through all messages\r\n msg.forEach((message, i) => {\r\n // Print in console\r\n if(console) {\r\n console.log(message);\r\n }\r\n\r\n // Create element\r\n let line = null;\r\n if(newLine) {\r\n line = document.createElement('p');\r\n } else {\r\n line = document.createElement('span');\r\n }\r\n\r\n // Top margin to element\r\n marginTop = parseInt(marginTop);\r\n if(marginTop > 0) {\r\n line.classList.add('mt-'+String(marginTop));\r\n }\r\n\r\n // Add text color\r\n line.classList.add('color-'+msgType);\r\n \r\n // Add message to element\r\n let msgType_txt = msgType.toLocaleUpperCase();\r\n line.textContent = '['+Joomla.JText._(msgType_txt)+'] '+String(message);\r\n\r\n // Print into logging output\r\n logOutput.appendChild(line);\r\n });\r\n}\r\n\r\n/**\r\n * Clear the logging output\r\n *\r\n * @param {String} type The type defining the logging output to clear\r\n * \r\n * @returns void\r\n */\r\nlet clearLog = function(type) {\r\n // Get logging output element\r\n let logOutput = document.getElementById('logOutput-'+type);\r\n\r\n // clear\r\n logOutput.innerHTML = '';\r\n}\r\n\r\n/**\r\n * Output all available messages from the result object\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {Object} res The result object in the form of\r\n * {success: bool, data: mixed, continue: bool, error: string|array, debug: string|array, warning: string|array}\r\n * \r\n * @returns void\r\n */\r\nlet logMessages = function(type, res) {\r\n // Available message types: error, debug, warning\r\n let available = ['error', 'debug', 'warning'];\r\n let msgTypes = {'error': 'error', 'debug': 'info', 'warning': 'warning'};\r\n\r\n available.forEach((value, index) => {\r\n if(!res[value] || !Boolean(res.data) || res.data == null) {\r\n return;\r\n }\r\n\r\n addLog(res[value], type, msgTypes[value]);\r\n });\r\n}\r\n\r\n/**\r\n * Update migrateable input field, progress bar and badges\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {Object} res The result object in the form of\r\n * {success: bool, data: mixed, continue: bool, error: string|array, debug: string|array, warning: string|array}\r\n * \r\n * @returns void\r\n */\r\nlet updateMigrateables = function(type, res) {\r\n let formId = formIdTmpl + '-' + type;\r\n let form = document.getElementById(formId);\r\n\r\n if(!res.success && (!Boolean(res.data) || res.data == null || res.data == '')) {\r\n // Migration failed, but no data available in result\r\n\r\n // Create result data based on input field\r\n let migrateable = atob(form.querySelector('[name=\"migrateable\"]').value);\r\n res.data = JSON.parse(migrateable);\r\n\r\n // See: Joomgallery\\Component\\Joomgallery\\Administrator\\Model\\MigrationModel::migrate\r\n // Remove migrated primary key from queue\r\n res.data.queue = res.data.queue.filter(function(e) { return e !== migrateablesList[type]['currentID'] })\r\n\r\n // Add migrated primary key to failed object\r\n res.data.failed[migrateablesList[type]['currentID']] = res.message;\r\n }\r\n\r\n if(!Boolean(res.data.progress) || res.data.progress == null || res.data.progress == '') {\r\n // Update progress if not delivered with result object\r\n let total = res.data.queue.lenght + Object.keys(res.data.successful).length + Object.keys(res.data.failed).length;\r\n let finished = Object.keys(res.data.successful).length + Object.keys(res.data.failed).length;\r\n res.data.progress = Math.round((100 / total) * (finished));\r\n }\r\n\r\n // Get badges\r\n let queueBadge = document.getElementById('badgeQueue-'+type);\r\n let resBadge = document.getElementById('badgeSuccessful-'+type);\r\n if(!res.success) {\r\n resBadge = document.getElementById('badgeFailed-'+type);\r\n }\r\n\r\n // Update migrateable input field\r\n let field = form.querySelector('[name=\"migrateable\"]');\r\n field.value = btoa(JSON.stringify(res.data));\r\n\r\n // Update badges\r\n queueBadge.innerHTML = parseInt(queueBadge.innerHTML) - 1;\r\n resBadge.innerHTML = parseInt(resBadge.innerHTML) + 1;\r\n\r\n // Update progress bar\r\n let bar = document.getElementById('progress-'+type);\r\n bar.setAttribute('aria-valuenow', res.data.progress);\r\n bar.style.width = res.data.progress + '%';\r\n bar.innerText = res.data.progress + '%';\r\n}\r\n\r\n/**\r\n * Update GUI to start migration\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {DOM Element} button The button beeing pressed to start the task\r\n * \r\n * @returns void\r\n */\r\nlet startTask = function(type, button) {\r\n let bar = document.getElementById('progress-'+type);\r\n let startBtn = button;\r\n let stopBtn = document.getElementById('stopBtn-'+type);\r\n\r\n // Update progress bar\r\n bar.classList.add('progress-bar-striped');\r\n bar.classList.add('progress-bar-animated');\r\n \r\n // Disable start button\r\n startBtn.classList.add('disabled');\r\n startBtn.setAttribute('disabled', 'true');\r\n\r\n // Enable stop button\r\n stopBtn.classList.remove('disabled');\r\n stopBtn.removeAttribute('disabled');\r\n\r\n // Reinitialize variables\r\n tryCounter = 0;\r\n continueState = true;\r\n forceStop = false;\r\n}\r\n\r\n/**\r\n * Update GUI to end migration\r\n *\r\n * @param {String} type The type defining the content type to be updated\r\n * @param {DOM Element} button The button beeing pressed to start the task\r\n * @param {String} formId Id of the form element\r\n * \r\n * @returns void\r\n */\r\nlet finishTask = function(type, button, formId) {\r\n let bar = document.getElementById('progress-'+type);\r\n let startBtn = button;\r\n let stopBtn = document.getElementById('stopBtn-'+type);\r\n let dependency = document.getElementById('dependent_of-'+type);\r\n\r\n // Update migrateablesList\r\n getNextMigrationID(formId);\r\n\r\n // Update progress bar\r\n bar.classList.remove('progress-bar-striped');\r\n bar.classList.remove('progress-bar-animated');\r\n \r\n // Enable start button\r\n if(!migrateablesList[type]['completed']) {\r\n // Only enable start button if migration is not finished\r\n startBtn.classList.remove('disabled');\r\n startBtn.removeAttribute('disabled');\r\n }\r\n\r\n // Disable stop button\r\n stopBtn.classList.add('disabled');\r\n stopBtn.setAttribute('disabled', 'true');\r\n\r\n // If migration is completed\r\n if(migrateablesList[type]['completed']) {\r\n dependency = JSON.parse(dependency.innerHTML);\r\n if(dependency.length > 0) {\r\n // Reload page\r\n location.reload();\r\n } else {\r\n // Update next start button\r\n enableNextBtn(type, button);\r\n // Update step 4 button\r\n updateStep4Btn();\r\n } \r\n }\r\n}\r\n\r\n/**\r\n * Enable start button of next migration content type\r\n * \r\n * @param {String} type The type defining the content type to be updated\r\n * @param {DOM Element} button The current start button\r\n * \r\n * @returns void\r\n */\r\nlet enableNextBtn = function(type, button) {\r\n let types_inputs = document.getElementsByName('type');\r\n let next_type = '';\r\n\r\n // Find next migration content type\r\n let this_type = false;\r\n for (const type_input of types_inputs) {\r\n if(this_type) {\r\n next_type = type_input.value;\r\n break;\r\n }\r\n if(Boolean(type_input.value) && type_input.value == type) {\r\n this_type = true;\r\n }\r\n }\r\n\r\n if(next_type !== '') {\r\n // Get next button\r\n let nextBtn = document.getElementById(buttonTmpl + '-' + next_type);\r\n\r\n // Enable button\r\n nextBtn.classList.remove('disabled');\r\n nextBtn.removeAttribute('disabled');\r\n }\r\n}\r\n\r\n/**\r\n * Update button to go to step 4\r\n * \r\n * @returns void\r\n */\r\nlet updateStep4Btn = function() {\r\n let types_inputs = document.getElementsByName('type');\r\n let types = {};\r\n\r\n // Add all available migrateables to types object\r\n types_inputs.forEach((type) => {\r\n if(Boolean(type.value)) {\r\n types[type.value] = false;\r\n }\r\n });\r\n\r\n // Check if all migrateables are available and completed\r\n let tot_complete = true;\r\n Object.keys(types).forEach(type => {\r\n if(Boolean(migrateablesList[type])) {\r\n if(!migrateablesList[type]['completed'])\r\n {\r\n // Migrateable not yet completed\r\n tot_complete = false;\r\n }\r\n }\r\n else\r\n {\r\n // Migrateable does not yet exist. Thus not completed\r\n tot_complete = false;\r\n }\r\n });\r\n\r\n if(tot_complete) {\r\n // Enable step 4 button\r\n document.getElementById(step4Btn).classList.remove('disabled');\r\n document.getElementById(step4Btn).removeAttribute('disabled');\r\n }\r\n}"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/media/com_joomgallery/js/migrator/src/index.js b/media/com_joomgallery/js/migrator/src/index.js index b59f8f6b..eb891eb8 100644 --- a/media/com_joomgallery/js/migrator/src/index.js +++ b/media/com_joomgallery/js/migrator/src/index.js @@ -30,6 +30,39 @@ var continueState = true; */ var forceStop = false; +/** + * Adds all completed migrateables to list + * + * @param {Object} event Event object + * @param {Object} element DOM element object + */ +export let updateMigrateablesList = function() { + let types_inputs = document.getElementsByName('type'); + let types = {}; + + // Add all available migrateables to types object + types_inputs.forEach((type) => { + if(Boolean(type.value)) { + types[type.value] = false; + } + }); + + // Loop through all migrateables + Object.keys(types).forEach(type => { + let formId = formIdTmpl + '-' + type; + let form = document.getElementById(formId); + + let migrateable = atob(form.querySelector('[name="migrateable"]').value); + migrateable = JSON.parse(migrateable); + + if(migrateable['completed']) + { + // Add migrateable in list + migrateablesList[type] = migrateable; + } + }); +} + /** * Submit the migration task by pressing the button * @@ -561,12 +594,14 @@ let enableNextBtn = function(type, button) { } } - // Get next button - let nextBtn = document.getElementById(buttonTmpl + '-' + next_type); + if(next_type !== '') { + // Get next button + let nextBtn = document.getElementById(buttonTmpl + '-' + next_type); - // Enable button - nextBtn.classList.remove('disabled'); - nextBtn.removeAttribute('disabled'); + // Enable button + nextBtn.classList.remove('disabled'); + nextBtn.removeAttribute('disabled'); + } } /** @@ -587,7 +622,7 @@ let updateStep4Btn = function() { // Check if all migrateables are available and completed let tot_complete = true; - Object.keys(types).forEach(type => { + Object.keys(types).forEach(type => { if(Boolean(migrateablesList[type])) { if(!migrateablesList[type]['completed']) { From 4db80419a2f35759113706ee49bc41aae8efc035 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 22 Jan 2024 13:51:08 +0100 Subject: [PATCH 085/121] fix confusing pending queue for catimage --- .../src/Model/MigrationModel.php | 65 +---- .../src/Service/Migration/Migration.php | 241 +++++++++++++++++- .../Service/Migration/MigrationInterface.php | 71 ++++-- .../Service/Migration/Scripts/Jg3ToJg4.php | 106 ++++++-- .../src/Service/Migration/Type.php | 35 ++- 5 files changed, 410 insertions(+), 108 deletions(-) diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index 5ff417de..abefed05 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -215,15 +215,16 @@ public function getMigrateables() } /** - * Method to get a migrateable record. + * Method to get a migrateable record by id. * - * @param integer $pk The id of the primary key. + * @param integer $pk The id of the primary key. + * @param bool $withQueue True to load the queue if empty. * - * @return CMSObject|boolean Object on success, false on failure. + * @return object|boolean Object on success, false on failure. * * @since 4.0.0 */ - public function getItem($pk = null) + public function getItem($pk = null, $withQueue = true) { $item = parent::getItem($pk); @@ -306,7 +307,7 @@ public function getItem($pk = null) } // Add queue if empty - if(!$item->completed && (\is_null($item->queue) || empty($item->queue))) + if($withQueue && !$item->completed && (\is_null($item->queue) || empty($item->queue))) { // Load queue $item->queue = $this->getQueue($type, $item); @@ -485,55 +486,7 @@ public function getSourceDeletion(): bool */ public function getQueue($type, $table=null): array { - // Retreive script - $script = $this->getScript(); - - // Create a new query object. - list($db, $dbPrefix) = $this->component->getMigration()->getDB('source'); - $query = $db->getQuery(true); - - if(!$script) - { - return $query; - } - - if(\is_null($table)) - { - $migrateables = $this->component->getMigration()->getMigrateables(); - $migrateable = $migrateables[$type]; - } - else - { - $migrateable = $table; - } - - // Select the required fields from the table. - $query->select($db->quoteName($migrateable->get('src_pk', 'id'))) - ->from($db->quoteName($migrateable->get('src_table'))) - ->order($db->quoteName($migrateable->get('src_pk', 'id')) . ' ASC'); - - // Apply id filter (reordering queue) - if(\property_exists($migrateable, 'queue') && !empty($migrateable->queue)) - { - $queue = (array) $migrateable->get('queue', array()); - $query->where($db->quoteName($migrateable->get('src_pk', 'id')) . ' IN (' . implode(',', $queue) .')'); - } - - // Gather migration types info - if(empty($this->component->getMigration()->get('types'))) - { - $this->component->getMigration()->getSourceTableInfo($type); - } - - // Apply ordering based on level if it is a nested type - if($this->component->getMigration()->get('types')[$type]->get('nested')) - { - $query->order($db->quoteName('level') . ' ASC'); - } - - $db->setQuery($query); - - return $db->loadColumn(); + return $this->component->getMigration()->getQueue($type, $table); } /** @@ -1024,8 +977,8 @@ protected function insertRecord(string $type, array $data, bool $autoID = true) // Get table primary key name $key = $table->getKeyName(); - // Special case: Only modification not creation of record - if(!$this->component->getMigration()->get('types')[$type]->get('isMigration') && $data[$key] > 0) + // Special case: Only modification no creation of record + if(!$this->component->getMigration()->get('types')[$type]->get('insertRecord') && $data[$key] > 0) { if($table->load($data[$key])) { diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index 8322ee1d..bf72fd1e 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -163,9 +163,9 @@ public function __destruct() * @param bool $names_only True to load type names only. No migration parameters required. * * @return array The source types info - * array(tablename, primarykey, isNested, isCategorized, dependent_on, pkstoskip, ismigration, recordname) + * array(tablename, primarykey, isNested, isCategorized, dependent_on, pkstoskip, insertrecord, queuetablename, recordname) * Needed: tablename, primarykey, isNested, isCategorized - * Optional: dependent_on, pkstoskip, ismigration, recordname + * Optional: dependent_on, pkstoskip, insertrecord, queuetablename, recordname * * @since 4.0.0 */ @@ -174,6 +174,10 @@ public function defineTypes($names_only = false): array // Content type definition array // Order of the content types must correspond to the migration order // Pay attention to the dependent_on when ordering here !!! + // + // Assumption for insertrecord: + // If insertrecord == true assumes, that type is a migration; Means reading data from source db and write it to destination db (default) + // If insertrecord == false assumes, that type is an adjustment; Means reading data from destination db adjust it and write it back to destination db /* Example: $types = array( 'category' => array('#__joomgallery_catg', 'cid', true, false, array(), array(1)), @@ -209,6 +213,135 @@ public function convertData(string $type, array $data): array return $data; } + /** + * - Load a queue of ids from a specific migrateable object + * - Reload/Reorder the queue if migrateable object already has queue + * + * @param string $type Content type + * @param object $migrateable Mibrateable object + * + * @return array + * + * @since 4.0.0 + */ + public function getQueue(string $type, object $migrateable=null): array + { + if(\is_null($migrateable)) + { + if(!$migrateable = $this->getMigrateable($type)) + { + return array(); + } + } + + $this->loadTypes(); + + // Queue gets always loaded from source db + $tablename = $this->types[$type]->get('queue_tablename'); + $primarykey = $this->types[$type]->get('pk'); + + // Get db object + list($db, $prefix) = $this->getDB('source'); + + // Initialize query object + $query = $db->getQuery(true); + + // Create the query + $query->select($db->quoteName($primarykey)) + ->from($db->quoteName($tablename)) + ->order($db->quoteName($primarykey) . ' ASC'); + + // Apply id filter + // Reorder the queue if queue is not empty + if(\property_exists($migrateable, 'queue') && !empty($migrateable->queue)) + { + $queue = (array) $migrateable->get('queue', array()); + $query->where($db->quoteName($primarykey) . ' IN (' . implode(',', $queue) .')'); + } + + // Gather migration types info + if(empty($this->get('types'))) + { + $this->getSourceTableInfo($type); + } + + // Apply ordering based on level if it is a nested type + if($this->get('types')[$type]->get('nested')) + { + $query->order($db->quoteName('level') . ' ASC'); + } + + $db->setQuery($query); + + // Attempt to load the queue + try + { + return $db->loadColumn(); + } + catch(\Exception $e) + { + $this->component->setError($e->getMessage()); + + return array(); + } + } + + /** + * Returns an associative array containing the record data from source. + * + * @param string $type Name of the content type + * @param int $pk The primary key of the content type + * + * @return array Associated array of a record data + * + * @since 4.0.0 + */ + public function getData(string $type, int $pk): array + { + $this->loadTypes(); + + if($this->get('types')[$type]->get('insertRecord')) + { + // When insertRecord is set to true, we assume that data gets loaded from source table + list($tablename, $primarykey) = $this->getSourceTableInfo($type); + + // Get db object + list($db, $prefix) = $this->getDB('source'); + } + else + { + // We assume that this migration is just a data adjustment inside the destination table + $tablename = JoomHelper::$content_types[$this->get('types')[$type]->get('recordName')]; + $primarykey = 'id'; + + // Get db object + list($db, $prefix) = $this->getDB('destination'); + } + + // Initialize query object + $query = $db->getQuery(true); + + // Create the query + $query->select('*') + ->from($db->quoteName($tablename)) + ->where($db->quoteName($primarykey) . ' = ' . $db->quote($pk)); + + // Reset the query using our newly populated query object. + $db->setQuery($query); + + // Attempt to load the array + try + { + return $db->loadAssoc(); + } + catch(\Exception $e) + { + $this->component->setError($e->getMessage()); + + return array(); + } + } + /** * Performs the neccessary steps to migrate an image in the filesystem * @@ -255,7 +388,7 @@ public function migrateFolder(CategoryTable $cat, array $data): bool /** * Returns a list of content types which can be migrated. * - * @return array List of content types + * @return Migrationtable[] List of content types * * @since 4.0.0 */ @@ -273,14 +406,64 @@ public function getMigrateables(): array return $this->migrateables; } + /** + * Returns an object of a specific content type which can be migrated. + * + * @param string $type Name of the content type + * @param string $withQueue True to load the queue if not available + * + * @return Migrationtable|bool Object of the content types on success, false otherwise + * + * @since 4.0.0 + */ + public function getMigrateable(string $type, bool $withQueue = true) + { + if( !\key_exists($type, $this->migrateables) || empty($this->migrateables[$type]) || + ($withQueue && empty($this->migrateables[$type]->queue)) + ) + { + // Get MigrationModel + $model = $this->component->getMVCFactory()->createModel('migration', 'administrator'); + + // Get list of migration ids + $mig_ids = $model->getIdList(); + + if(!empty($mig_ids)) + { + // Detect id of the requested type + $id = 0; + foreach($mig_ids[$this->name] as $key => $mig) + { + if($mig->type == $type) + { + $id = $mig->id; + } + } + + // Load migrateable + if($id > 0) + { + $this->migrateables[$type] = $model->getItem($id, $withQueue); + + return $this->migrateables[$type]; + } + } + + } + + return false; + } + /** * Prepare the migration. * + * @param string $type Name of the content type + * * @return MigrationTable The currently processed migrateable * * @since 4.0.0 */ - public function prepareMigration(string $type) + public function prepareMigration(string $type): object { // Load migrateables to migration service $this->getMigrateables(); @@ -540,7 +723,7 @@ public function getSourceTableInfo(string $type): array /** * Returns a list of involved content types. * - * @return array List of type names + * @return Type[] List of type names * array('image', 'category', ...) * * @since 4.0.0 @@ -776,8 +959,20 @@ protected function checkSourceDir(Checks &$checks, string $category) $directories = $this->getSourceDirs(); $root = $this->getSourceRootPath(); + $dirs_checked = array(); foreach($directories as $dir) { + // Make sure, we check each directory only once + if(!\in_array($dir, $dirs_checked)) + { + \array_push($dirs_checked, $dir); + } + else + { + // Table already checked. Skip check. + continue; + } + $check_name = 'src_dir_' . \basename($dir); if(!\is_dir($root . $dir)) @@ -810,8 +1005,20 @@ protected function checkDestDir(Checks &$checks, string $category) // Get all imagetypes $imagetypes = JoomHelper::getRecords('imagetypes', $this->component); + $dirs_checked = array(); foreach($imagetypes as $imagetype) { + // Make sure, we check each directory only once + if(!\in_array($imagetype, $dirs_checked)) + { + \array_push($dirs_checked, $imagetype); + } + else + { + // Table already checked. Skip check. + continue; + } + $check_name = 'dest_dir_' . $imagetype->typename; $error = false; @@ -873,8 +1080,20 @@ protected function checkSourceTable(Checks &$checks, string $category) // Check required tables $tables = $this->getSourceTables(); + $tables_checked = array(); foreach($tables as $tablename) { + // Make sure, we check each table only once + if(!\in_array($tablename, $tables_checked)) + { + \array_push($tables_checked, $tablename); + } + else + { + // Table already checked. Skip check. + continue; + } + $check_name = 'src_table_' . $tablename; // Check if required tables exists @@ -976,8 +1195,20 @@ protected function checkDestTable(Checks &$checks, string $category) } // Check required tables + $tables_checked = array(); foreach($tables as $tablename) { + // Make sure, we check each table only once + if(!\in_array($tablename, $tables_checked)) + { + \array_push($tables_checked, $tablename); + } + else + { + // Table already checked. Skip check. + continue; + } + $check_name = 'dest_table_' . $tablename; // Check if required tables exists diff --git a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php index 9534411d..c74d880f 100644 --- a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php +++ b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php @@ -68,14 +68,14 @@ public function getSourceXML(); /** * A list of content type definitions depending on migration source - * (Required in migration scripts. The order of the content types must correspond to its migration order.) + * (Required in migration scripts. The order of the content types must correspond to its migration order) * * @param bool $names_only True to load type names only. No migration parameters required. * * @return array The source types info - * array(tablename, primarykey, isNested, isCategorized, dependent_on, pkstoskip, ismigration, recordname) + * array(tablename, primarykey, isNested, isCategorized, dependent_on, pkstoskip, insertrecord, queuetablename, recordname) * Needed: tablename, primarykey, isNested, isCategorized - * Optional: dependent_on, pkstoskip, ismigration, recordname + * Optional: dependent_on, pkstoskip, insertrecord, queuetablename, recordname * * @since 4.0.0 */ @@ -106,19 +106,6 @@ public function getSourceDirs(): array; */ public function getImageSource(array $data): array; - /** - * Returns an associative array containing the record data from source. - * (Required in migration scripts.) - * - * @param string $type Name of the content type - * @param int $pk The primary key of the content type - * - * @return array Record data - * - * @since 4.0.0 - */ - public function getData(string $type, int $pk): array; - /** * Converts data from source into the structure needed for JoomGallery. * (Optional in migration scripts, but highly recommended.) @@ -132,6 +119,32 @@ public function getData(string $type, int $pk): array; */ public function convertData(string $type, array $data): array; + /** + * Load the a queue of ids from a specific migrateable object + * (Optional in migration scripts, but needed if queues have to be specially threated.) + * + * @param string $type Content type + * @param object $migrateable Mibrateable object + * + * @return array + * + * @since 4.0.0 + */ + public function getQueue(string $type, object $migrateable=null): array; + + /** + * Returns an associative array containing the record data from source. + * (Optional in migration scripts, can be overwritten if required.) + * + * @param string $type Name of the content type + * @param int $pk The primary key of the content type + * + * @return array Record data + * + * @since 4.0.0 + */ + public function getData(string $type, int $pk): array; + /** * Perform pre migration checks. * (Optional in migration scripts, can be overwritten if required.) @@ -175,6 +188,18 @@ public function getDB(string $target): array; */ public function getMigrateables(): array; + /** + * Returns an object of a specific content type which can be migrated. + * + * @param string $type Name of the content type + * @param string $withQueue True to load the queue if not available + * + * @return Migrationtable|bool Object of the content types on success, false otherwise + * + * @since 4.0.0 + */ + public function getMigrateable(string $type, bool $withQueue = true); + /** * Returns tablename and primarykey name of the source table * (Optional in migration scripts, can be overwritten if required.) @@ -203,13 +228,25 @@ public function getSourceTables(): array; * Returns a list of involved content types. * (Optional in migration scripts, can be overwritten if required.) * - * @return array List of type names + * @return Type[] List of type names * array('image', 'category', ...) * * @since 4.0.0 */ public function getTypes(): array; + /** + * Returns a type object based on type name. + * (Optional in migration scripts, can be overwritten if required.) + * + * @param string $type The content type name + * + * @return Type Type object + * + * @since 4.0.0 + */ + public function getType(string $name): Type; + /** * True if the given record has to be migrated * False to skip the migration for this record diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index 702f3b28..11bd339b 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -153,9 +153,9 @@ public function getSourceDirs(): array * @param bool $names_only True to load type names only. No migration parameters required. * * @return array The source types info - * array(tablename, primarykey, isNested, isCategorized, dependent_on, pkstoskip, ismigration, recordname) + * array(tablename, primarykey, isNested, isCategorized, dependent_on, pkstoskip, insertrecord, queuetablename, recordname) * Needed: tablename, primarykey, isNested, isCategorized - * Optional: dependent_on, pkstoskip, ismigration, recordname + * Optional: dependent_on, pkstoskip, insertrecord, queuetablename, recordname * * @since 4.0.0 */ @@ -166,7 +166,7 @@ public function defineTypes($names_only = false): array // Pay attention to the dependent_on when ordering here !!! $types = array( 'category' => array('#__joomgallery_catg', 'cid', true, false, array(), array(1)), 'image' => array('#__joomgallery', 'id', false, true, array('category')), - 'catimage' => array(_JOOM_TABLE_CATEGORIES, 'id', false, false, array('category', 'image'), array(1), false, 'category') + 'catimage' => array(_JOOM_TABLE_CATEGORIES, 'cid', false, false, array('category', 'image'), array(1), false, '#__joomgallery_catg', 'category') ); if($names_only) @@ -174,14 +174,21 @@ public function defineTypes($names_only = false): array return \array_keys($types); } + // add special cases if tables in the same db with *_old at the end if($this->params->get('same_db')) { foreach($types as $key => $value) { if(\count($value) < 7 || (\count($value) > 6 && $value[6] !== false)) { + // insertrecord == true, we assume tablename is from source db $types[$key][0] = $value[0] . '_old'; } + elseif(\count($value) > 7 && !empty($value[7]) && $value[6] == false) + { + // insertrecord == false and queuetablename given, we assume queuetablename is from source db + $types[$key][7] = $value[7] . '_old'; + } } } @@ -216,7 +223,6 @@ public function convertData(string $type, array $data): array // Parameter dependet mapping fields $id = \boolval($this->params->get('source_ids', 0)) ? 'id' : false; $owner = \boolval($this->params->get('check_owner', 0)) ? 'created_by' : false; - $path = false; // Configure mapping for each content type switch($type) @@ -305,43 +311,103 @@ public function convertData(string $type, array $data): array } /** - * Returns an associative array containing the record data from source. - * - * @param string $type Name of the content type - * @param int $pk The primary key of the content type - * - * @return array Associated array of a record data + * - Load a queue of ids from a specific migrateable object + * - Reload/Reorder the queue if migrateable object already has queue * + * @param string $type Content type + * @param object $migrateable Mibrateable object + * + * @return array + * * @since 4.0.0 */ - public function getData(string $type, int $pk): array + public function getQueue(string $type, object $migrateable=null): array { - // Get source table info - list($tablename, $primarykey) = $this->getSourceTableInfo($type); + if(\is_null($migrateable)) + { + if(!$migrateable = $this->getMigrateable($type)) + { + return array(); + } + } + + $this->loadTypes(); + + // Queue gets always loaded from source db + $tablename = $this->types[$type]->get('queue_tablename'); + $primarykey = $this->types[$type]->get('pk'); // Get db object list($db, $prefix) = $this->getDB('source'); - $query = $db->getQuery(true); + + // Initialize query object + $query = $db->getQuery(true); // Create the query - $query->select('*') + $query->select($db->quoteName($primarykey)) ->from($db->quoteName($tablename)) - ->where($db->quoteName($primarykey) . ' = ' . $db->quote($pk)); + ->order($db->quoteName($primarykey) . ' ASC'); + + // Apply additional where clauses for specific content types + if($type == 'catimage') + { + $query->where($db->quoteName($primarykey) . ' > 1'); + $query->where($db->quoteName('thumbnail') . ' > 0'); + } + + // Apply id filter + // Reorder the queue if queue is not empty + if(\property_exists($migrateable, 'queue') && !empty($migrateable->queue)) + { + $queue = (array) $migrateable->get('queue', array()); + $query->where($db->quoteName($primarykey) . ' IN (' . implode(',', $queue) .')'); + } + + // Gather migration types info + if(empty($this->get('types'))) + { + $this->getSourceTableInfo($type); + } + + // Apply ordering based on level if it is a nested type + if($this->get('types')[$type]->get('nested')) + { + $query->order($db->quoteName('level') . ' ASC'); + } - // Reset the query using our newly populated query object. $db->setQuery($query); - // Attempt to load the array + // Attempt to load the queue + $queue = array(); try { - return $db->loadAssoc(); + $queue = $db->loadColumn(); } catch(\Exception $e) { $this->component->setError($e->getMessage()); + } - return array(); + // Postprocessing the queue + $needs_postprocessing = array('catimage'); + if(!empty($queue) && \in_array($type, $needs_postprocessing)) + { + if($type == 'catimage' && !\boolval($this->params->get('source_ids', 0))) + { + $mig_cat = $this->getMigrateable('category', false); + + if($mig_cat && $mig_cat->id > 0) + { + // Adjust catid with new created/migrates categories + foreach($queue as $key => $old_id) + { + $queue[$key] = $mig_cat->successful->get($old_id); + } + } + } } + + return $queue; } /** diff --git a/administrator/com_joomgallery/src/Service/Migration/Type.php b/administrator/com_joomgallery/src/Service/Migration/Type.php index 26e4628b..1904bc1f 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Type.php +++ b/administrator/com_joomgallery/src/Service/Migration/Type.php @@ -110,14 +110,23 @@ class Type protected $dependent_of = array(); /** - * Is this migration type really a migration? - * True to perform a migration, false to perform a modification at destination + * Table name used to load the queue + * + * @var string + * + * @since 4.0.0 + */ + protected $queue_tablename = '#__joomgallery'; + + /** + * Do we have to create/inert new database records for this type? + * If no this type is just an adjustment of data within the destination table. * * @var boolean * * @since 4.0.0 */ - protected $isMigration = true; + protected $insertRecord = true; /** * Constructor @@ -140,10 +149,11 @@ public function __construct($name, $list, $lists=array()) throw new Exception('Type object needs a list of at least 4 entries as the second argument.', 1); } - $this->tablename = $list[0]; - $this->pk = $list[1]; - $this->nested = $list[2]; - $this->categorized = $list[3]; + $this->tablename = $list[0]; + $this->queue_tablename = $list[0]; + $this->pk = $list[1]; + $this->nested = $list[2]; + $this->categorized = $list[3]; if(\count($list) > 4) { @@ -164,15 +174,20 @@ public function __construct($name, $list, $lists=array()) if(\count($list) > 6) { - $this->isMigration = $list[6]; + $this->insertRecord = $list[6]; } if(\count($list) > 7) { - $this->recordName = $list[7]; + $this->queue_tablename = $list[7]; + } + + if(\count($list) > 8) + { + $this->recordName = $list[8]; } - // search for types depending on this one + // search for types depending on this type if(!empty($lists)) { foreach($lists as $type_name => $type_list) From 05f9618f89e97036d4d83b75456e96b81275a76d Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 22 Jan 2024 13:53:57 +0100 Subject: [PATCH 086/121] adjust migration language key --- .../com_joomgallery/language/en-GB/com_joomgallery.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index 071b4de3..af75719a 100644 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -17,7 +17,8 @@ COM_JOOMGALLERY_CONFIGURATION="JoomGallery: Options" COM_JOOMGALLERY_LOGO="JoomGallery Logo" COM_JOOMGALLERY_HELP="Help and Information" COM_JOOMGALLERY_MAINTENANCE="Maintenance" -COM_JOOMGALLERY_MIGRATIONS="Migration" +COM_JOOMGALLERY_MIGRATION="Migration" +COM_JOOMGALLERY_MIGRATIONS="Migrations" COM_JOOMGALLERY_IMAGE="Image" COM_JOOMGALLERY_IMAGES="Images" COM_JOOMGALLERY_IMAGETYPE="Image-Type" @@ -80,7 +81,6 @@ COM_JOOMGALLERY_IMAGE_SELECTION="Image selection" COM_JOOMGALLERY_SOURCE="Source" COM_JOOMGALLERY_DESTINATION="Destination" COM_JOOMGALLERY_REPLACE="Replace" -COM_JOOMGALLERY_MIGRATION="Migration" COM_JOOMGALLERY_LOGFILE="Log file" COM_JOOMGALLERY_LOGDIRECTORY="Log directory" COM_JOOMGALLERY_CHECK="Check" From 06255389a6fbbb346d3865a1eb258ca3d7321b11 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 22 Jan 2024 15:50:51 +0100 Subject: [PATCH 087/121] some minor adjustments --- .../src/Controller/MigrationController.php | 9 ++++++++- .../src/Model/MigrationModel.php | 9 ++++++++- .../Service/Migration/Scripts/Jg3ToJg4.php | 19 +++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/administrator/com_joomgallery/src/Controller/MigrationController.php b/administrator/com_joomgallery/src/Controller/MigrationController.php index a51af732..e1d31df9 100644 --- a/administrator/com_joomgallery/src/Controller/MigrationController.php +++ b/administrator/com_joomgallery/src/Controller/MigrationController.php @@ -146,7 +146,14 @@ public function cancel() } // Get migrateables if available - $migrateables = $model->getMigrateables(); + try + { + $migrateables = $model->getMigrateables(); + } + catch(\Exception $e) + { + $migrateables = false; + } // Checkin migration records of this script if($migrateables) diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index abefed05..b6f9b894 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -287,7 +287,14 @@ public function getItem($pk = null, $withQueue = true) if(\key_exists($type, JoomHelper::$content_types)) { $item->dst_table = JoomHelper::$content_types[$type]; - } + } + // else + // { + // // We have a migrateable record whos name does not correspond to the record name + // $type_obj = $this->component->getMigration()->getType($type); + + // $item->dst_table = JoomHelper::$content_types[$type_obj->get('recordName')]; + // } $item->dst_pk = 'id'; } diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index 11bd339b..9099463a 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -916,4 +916,23 @@ protected function checkCatpath(\stdClass $cat): bool return true; } + + /** + * Returns a list of content types which can be migrated. + * + * @return Migrationtable[] List of content types + * + * @since 4.0.0 + */ + public function getMigrateables(): array + { + parent::getMigrateables(); + + if($this->params->get('source_ids', 0) == 1 && \key_exists('catimage', $this->migrateables)) + { + unset($this->migrateables['catimage']); + } + + return $this->migrateables; + } } \ No newline at end of file From 5040f697c95a954f15a2a11b2964363094ec2b76 Mon Sep 17 00:00:00 2001 From: Elfangor <39154009+Elfangor93@users.noreply.github.com> Date: Mon, 22 Jan 2024 13:54:30 +0100 Subject: [PATCH 088/121] Update joomgallery.xml --- joomgallery.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/joomgallery.xml b/joomgallery.xml index 5dd9f802..de5d3cee 100644 --- a/joomgallery.xml +++ b/joomgallery.xml @@ -53,7 +53,7 @@ JCATEGORIES COM_JOOMGALLERY_TAGS COM_JOOMGALLERY_CONFIG_SETS - COM_JOOMGALLERY_MIGRATION + COM_JOOMGALLERY_MIGRATIONS access.xml From 682f2aa38850b2e7090f071c1e27d64d54b6b338 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 22 Jan 2024 18:42:30 +0100 Subject: [PATCH 089/121] fix issue: skip unset if params are not set --- .../com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index 9099463a..d4621dce 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -928,8 +928,9 @@ public function getMigrateables(): array { parent::getMigrateables(); - if($this->params->get('source_ids', 0) == 1 && \key_exists('catimage', $this->migrateables)) + if(!\is_null($this->params) && $this->params->get('source_ids', 0) == 1 && \key_exists('catimage', $this->migrateables)) { + // Special case: When using ids from source, category images don't have to be adjusted. unset($this->migrateables['catimage']); } From 2a7bff7f911e7c657ae415182617d3a7c4c4c4fa Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 22 Jan 2024 22:27:24 +0100 Subject: [PATCH 090/121] stable params loading --- .../src/Model/MigrationModel.php | 50 +++++++++++++++++-- .../src/Service/Migration/Migration.php | 11 ++-- .../Service/Migration/MigrationInterface.php | 4 +- .../src/View/Migration/HtmlView.php | 2 +- 4 files changed, 56 insertions(+), 11 deletions(-) diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index b6f9b894..c67644de 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -96,7 +96,48 @@ public function __construct($config = array()) } /** - * Method to set the migration parameters in the migration script. + * Method to get the migration parameters from the userstate or from the database. + * + * @return array $params The migration parameters entered in the migration form + * + * @since 4.0.0 + */ + public function getParams() + { + // Try to load params from user state + $params = $this->app->getUserState(_JOOM_OPTION.'.migration.'.$this->scriptName.'.params', array()); + + // Load params from db if there are migrateables in database + $db = $this->getDbo(); + $query = $db->getQuery(true); + + // Select the required fields from the table. + $query->select('a.params'); + $query->from($db->quoteName(_JOOM_TABLE_MIGRATION, 'a')); + $query->where($db->quoteName('script') . ' = ' . $db->quote($this->scriptName)); + + $db->setQuery($query); + + try + { + $params_db = $db->loadResult(); + } + catch (\RuntimeException $e) + { + $this->component->setError($e->getMessage()); + } + + if($params_db) + { + // Override params from user state with the one from db + $params = \json_decode($params_db, true); + } + + return $params; + } + + /** + * Method to set the migration parameters in the model and the migration script. * * @param array $params The migration parameters entered in the migration form * @@ -111,8 +152,7 @@ public function setParams($params = null) if(\is_null($params)) { - // Check the session for validated migration parameters - $params = $this->app->getUserState(_JOOM_OPTION.'.migration.'.$info->name.'.params', null); + $params = $this->getParams(); } if(\is_null($params)) @@ -340,7 +380,7 @@ public function getItem($pk = null, $withQueue = true) public function getItems(): array { // Get types from migration service - $types = $this->component->getMigration()->getTypes(); + $types = $this->component->getMigration()->getTypeNames(); // Get available types from db try @@ -595,7 +635,7 @@ protected function loadFormData() $data = $this->app->getUserState($name.'.step2.data', array()); // Check the session for validated migration parameters - $params = $this->app->getUserState($name.'.params', null); + $params = $this->getParams(); return (empty($params)) ? $data : $params; } diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index bf72fd1e..31c52163 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -721,15 +721,20 @@ public function getSourceTableInfo(string $type): array } /** - * Returns a list of involved content types. + * Returns a list of content type names available in this migration script. * * @return Type[] List of type names * array('image', 'category', ...) * * @since 4.0.0 */ - public function getTypes(): array + public function getTypeNames(): array { + if(\is_null($this->params)) + { + throw new Exception('Migration parameters need to be set in order to load types.', 1); + } + $types = $this->defineTypes(true); return $types; @@ -1452,7 +1457,7 @@ protected function checkImageMapping(Checks &$checks, string $category) */ protected function checkMigrationQueues(Checks &$checks, string $category) { - $types = $this->getTypes(); + $types = $this->getTypeNames(); $migrateables = $this->getMigrateables(); // Check if all types are existent in migrateables diff --git a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php index c74d880f..b18356f3 100644 --- a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php +++ b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php @@ -225,7 +225,7 @@ public function getSourceTableInfo(string $type): array; public function getSourceTables(): array; /** - * Returns a list of involved content types. + * Returns a list of content type names available in this migration script. * (Optional in migration scripts, can be overwritten if required.) * * @return Type[] List of type names @@ -233,7 +233,7 @@ public function getSourceTables(): array; * * @since 4.0.0 */ - public function getTypes(): array; + public function getTypeNames(): array; /** * Returns a type object based on type name. diff --git a/administrator/com_joomgallery/src/View/Migration/HtmlView.php b/administrator/com_joomgallery/src/View/Migration/HtmlView.php index 75f8ee35..ecc2e47f 100644 --- a/administrator/com_joomgallery/src/View/Migration/HtmlView.php +++ b/administrator/com_joomgallery/src/View/Migration/HtmlView.php @@ -63,7 +63,7 @@ public function display($tpl = null) else { // Try to load the migration params - $this->params = $this->app->getUserState(_JOOM_OPTION.'.migration.'.$this->script->name.'.params', null); + $this->params = $this->get('Params'); // Check if migration params exist if(\is_null($this->params) && $this->layout != 'step1') From b43cb865d95c32ad932a67797cb840920a25be83 Mon Sep 17 00:00:00 2001 From: Elfangor Date: Tue, 23 Jan 2024 08:19:59 +0100 Subject: [PATCH 091/121] load params correctly --- .../src/Controller/MigrationController.php | 9 +- .../src/Model/MigrationModel.php | 229 ++++++++++-------- 2 files changed, 140 insertions(+), 98 deletions(-) diff --git a/administrator/com_joomgallery/src/Controller/MigrationController.php b/administrator/com_joomgallery/src/Controller/MigrationController.php index e1d31df9..18595c45 100644 --- a/administrator/com_joomgallery/src/Controller/MigrationController.php +++ b/administrator/com_joomgallery/src/Controller/MigrationController.php @@ -28,7 +28,6 @@ use \Joomla\CMS\Form\FormFactoryAwareInterface; use \Joomla\CMS\MVC\Factory\MVCFactoryInterface; use \Joomgallery\Component\Joomgallery\Administrator\Extension\JoomgalleryComponent; -use stdClass; /** * Migration controller class. @@ -218,6 +217,9 @@ public function resume() return false; } + // Load params + $model->setParams(); + // Get item to resume from the request. $cid = (array) $this->input->get('cid', [], 'int'); $cid = \array_filter($cid); @@ -307,6 +309,9 @@ public function delete() // Get the model. $model = $this->getModel(); + // Load params + $model->setParams(); + // Attempt to load the migration item $item = $model->getItem($cid[0]); if(!$item || $item->script != $script) @@ -901,7 +906,7 @@ public function applyState() */ protected function createRespond($data, bool $success = true, $error = null): string { - $obj = new stdClass; + $obj = new \stdClass; $obj->success = $success; $obj->data = $data; diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index c67644de..45a8e3af 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -107,31 +107,34 @@ public function getParams() // Try to load params from user state $params = $this->app->getUserState(_JOOM_OPTION.'.migration.'.$this->scriptName.'.params', array()); - // Load params from db if there are migrateables in database - $db = $this->getDbo(); - $query = $db->getQuery(true); + if(!$params || empty($params)) + { + // Load params from db if there are migrateables in database + $db = $this->getDbo(); + $query = $db->getQuery(true); - // Select the required fields from the table. - $query->select('a.params'); - $query->from($db->quoteName(_JOOM_TABLE_MIGRATION, 'a')); - $query->where($db->quoteName('script') . ' = ' . $db->quote($this->scriptName)); + // Select the required fields from the table. + $query->select('a.params'); + $query->from($db->quoteName(_JOOM_TABLE_MIGRATION, 'a')); + $query->where($db->quoteName('script') . ' = ' . $db->quote($this->scriptName)); - $db->setQuery($query); + $db->setQuery($query); - try - { - $params_db = $db->loadResult(); - } - catch (\RuntimeException $e) - { - $this->component->setError($e->getMessage()); - } + try + { + $params_db = $db->loadResult(); + } + catch (\RuntimeException $e) + { + $this->component->setError($e->getMessage()); + } - if($params_db) - { - // Override params from user state with the one from db - $params = \json_decode($params_db, true); - } + if($params_db && !empty($params_db)) + { + // Override params from user state with the one from db + $params = \json_decode($params_db, true); + } + } return $params; } @@ -246,7 +249,7 @@ public function getMigrateables() if(!$script) { - return false; + throw new \Exception('Migration script not found.'); } $this->setParams(); @@ -328,13 +331,13 @@ public function getItem($pk = null, $withQueue = true) { $item->dst_table = JoomHelper::$content_types[$type]; } - // else - // { - // // We have a migrateable record whos name does not correspond to the record name - // $type_obj = $this->component->getMigration()->getType($type); + elseif($this->params && !empty($this->params)) + { + // We have a migrateable record whos name does not correspond to the record name + $type_obj = $this->component->getMigration()->getType($type); - // $item->dst_table = JoomHelper::$content_types[$type_obj->get('recordName')]; - // } + $item->dst_table = JoomHelper::$content_types[$type_obj->get('recordName')]; + } $item->dst_pk = 'id'; } @@ -515,9 +518,11 @@ public function getSourceDeletion(): bool if(!$script) { - return false; + throw new \Exception('Migration script not found.'); } + $this->setParams(); + return $this->component->getMigration()->get('sourceDeletion', false); } @@ -536,37 +541,6 @@ public function getQueue($type, $table=null): array return $this->component->getMigration()->getQueue($type, $table); } - /** - * Build an SQL query to load the list data. - * - * @return DatabaseQuery - * - * @since 4.0.0 - */ - protected function getListQuery() - { - // Retreive script - $script = $this->getScript(); - - // Create a new query object. - $db = $this->getDbo(); - $query = $db->getQuery(true); - - if(!$script) - { - return $query; - } - - // Select the required fields from the table. - $query->select(array('a.id', 'a.type')); - $query->from($db->quoteName(_JOOM_TABLE_MIGRATION, 'a')); - - // Filter for the current script - $query->where($db->quoteName('a.script') . ' = ' . $db->quote($script->name)); - - return $query; - } - /** * Method to get the migration form. * @@ -616,30 +590,6 @@ public function getForm($data = array(), $loadData = true) return $form; } - /** - * Method to get the data that should be injected in the form. - * - * @return mixed The data for the form. - * - * @since 4.0.0 - */ - protected function loadFormData() - { - if(!$this->component->getMigration()) - { - $this->getScript(); - } - - // Check the session for previously entered form data. - $name = _JOOM_OPTION.'.migration.'.$this->component->getMigration()->get('name'); - $data = $this->app->getUserState($name.'.step2.data', array()); - - // Check the session for validated migration parameters - $params = $this->getParams(); - - return (empty($params)) ? $data : $params; - } - /** * Method to perform the pre migration checks. * @@ -651,7 +601,12 @@ protected function loadFormData() */ public function precheck($params) { - $info = $this->getScript(); + $script = $this->getScript(); + + if(!$script) + { + throw new \Exception('Migration script not found.'); + } // Set the migration parameters $this->setParams($params); @@ -668,11 +623,17 @@ public function precheck($params) * @since 4.0.0 */ public function postcheck() - { - // Prepare the migration object - $migs = $this->getMigrateables(); - $keys = \array_keys($migs); - $this->setParams($migs[$keys[0]]->params); + { + // Retreive script + $script = $this->getScript(); + + if(!$script) + { + throw new \Exception('Migration script not found.'); + } + + // Set the migration parameters + $this->setParams(); // Perform the postchecks return $this->component->getMigration()->postcheck(); @@ -690,13 +651,21 @@ public function postcheck() */ public function migrate(string $type, int $pk): object { + // Retreive script + $script = $this->getScript(); + + if(!$script) + { + throw new \Exception('Migration script not found.'); + } + // Initialise variables $new_pk = $pk; $success = true; $error_msg = ''; // Prepare migration service and return migrateable object - $this->component->createMigration($this->app->getUserStateFromRequest(_JOOM_OPTION.'.migration.script', 'script', '', 'cmd')); + $this->setParams(); $mig = $this->component->getMigration()->prepareMigration($type); // Perform the migration of the element if needed @@ -846,8 +815,16 @@ public function migrate(string $type, int $pk): object */ public function applyState(string $type, int $state, int $src_pk, int $dest_pk = 0, string $error = ''): object { + // Retreive script + $script = $this->getScript(); + + if(!$script) + { + throw new \Exception('Migration script not found.'); + } + // Prepare migration service and return migrateable object - $this->component->createMigration($this->app->getUserStateFromRequest(_JOOM_OPTION.'.migration.script', 'script', '', 'cmd')); + $this->setParams(); $mig = $this->component->getMigration()->prepareMigration($type); // Load migration data table @@ -983,15 +960,75 @@ public function applyState(string $type, int $state, int $src_pk, int $dest_pk = */ public function deleteSource() { - // Prepare the migration object - $migs = $this->getMigrateables(); - $keys = \array_keys($migs); - $this->setParams($migs[$keys[0]]->params); + // Retreive script + $script = $this->getScript(); + + if(!$script) + { + throw new \Exception('Migration script not found.'); + } + + $this->setParams(); // Delete sources return $this->component->getMigration()->deleteSource(); } + /** + * Method to get the data that should be injected in the form. + * + * @return mixed The data for the form. + * + * @since 4.0.0 + */ + protected function loadFormData() + { + if(!$this->component->getMigration()) + { + $this->getScript(); + } + + // Check the session for previously entered form data. + $name = _JOOM_OPTION.'.migration.'.$this->component->getMigration()->get('name'); + $data = $this->app->getUserState($name.'.step2.data', array()); + + // Check the session for validated migration parameters + $params = $this->getParams(); + + return (empty($params)) ? $data : $params; + } + + /** + * Build an SQL query to load the list data. + * + * @return DatabaseQuery + * + * @since 4.0.0 + */ + protected function getListQuery() + { + // Retreive script + $script = $this->getScript(); + + if(!$script) + { + throw new \Exception('Migration script not found.'); + } + + // Create a new query object. + $db = $this->getDbo(); + $query = $db->getQuery(true); + + // Select the required fields from the table. + $query->select(array('a.id', 'a.type')); + $query->from($db->quoteName(_JOOM_TABLE_MIGRATION, 'a')); + + // Filter for the current script + $query->where($db->quoteName('a.script') . ' = ' . $db->quote($script->name)); + + return $query; + } + /** * Method to insert a content type record from migration data. * From 9a86d2bcd2a6919468d7e184c877883d05d8bc8f Mon Sep 17 00:00:00 2001 From: Elfangor Date: Tue, 23 Jan 2024 08:20:51 +0100 Subject: [PATCH 092/121] exchange \stdClass --- .../com_joomgallery/src/Service/Migration/Migration.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index 31c52163..6853f857 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -29,7 +29,6 @@ use \Joomgallery\Component\Joomgallery\Administrator\Extension\ServiceTrait; use \Joomgallery\Component\Joomgallery\Administrator\Service\Migration\Checks; use \Joomgallery\Component\Joomgallery\Administrator\Service\Migration\MigrationInterface; -use stdClass; /** * Migration Base Class @@ -53,7 +52,7 @@ abstract class Migration implements MigrationInterface /** * Storage for the migration info object. * - * @var \stdClass + * @var object * * @since 4.0.0 */ @@ -488,7 +487,7 @@ public function prepareMigration(string $type): object * Step 2 * Perform pre migration checks. * - * @return \stdClass[] An array containing the precheck results. + * @return object[] An array containing the precheck results. * * @since 4.0.0 */ @@ -538,7 +537,7 @@ public function precheck(): array * Step 4 * Perform post migration checks. * - * @return void + * @return object[] An array containing the postcheck results. * * @since 4.0.0 */ From 6a3d02098234ed04e28d521072fc830954a1935b Mon Sep 17 00:00:00 2001 From: Elfangor Date: Tue, 23 Jan 2024 21:13:53 +0100 Subject: [PATCH 093/121] unset migrateable --- .../Service/Migration/Scripts/Jg3ToJg4.php | 26 +++++-------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index d4621dce..71befe67 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -169,6 +169,12 @@ public function defineTypes($names_only = false): array 'catimage' => array(_JOOM_TABLE_CATEGORIES, 'cid', false, false, array('category', 'image'), array(1), false, '#__joomgallery_catg', 'category') ); + if($this->params->get('source_ids', 0) == 1) + { + // Special case: When using ids from source, category images don't have to be adjusted. + unset($types['catimage']); + } + if($names_only) { return \array_keys($types); @@ -916,24 +922,4 @@ protected function checkCatpath(\stdClass $cat): bool return true; } - - /** - * Returns a list of content types which can be migrated. - * - * @return Migrationtable[] List of content types - * - * @since 4.0.0 - */ - public function getMigrateables(): array - { - parent::getMigrateables(); - - if(!\is_null($this->params) && $this->params->get('source_ids', 0) == 1 && \key_exists('catimage', $this->migrateables)) - { - // Special case: When using ids from source, category images don't have to be adjusted. - unset($this->migrateables['catimage']); - } - - return $this->migrateables; - } } \ No newline at end of file From d226d914223e367e5aa7bf3d6655228c90735271 Mon Sep 17 00:00:00 2001 From: Elfangor Date: Wed, 24 Jan 2024 08:16:28 +0100 Subject: [PATCH 094/121] fix issue with empty queues (pending = 0) --- .../com_joomgallery/src/Model/MigrationModel.php | 13 +++++++++++++ .../com_joomgallery/src/Table/MigrationTable.php | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index 45a8e3af..7a2c2a46 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -361,6 +361,19 @@ public function getItem($pk = null, $withQueue = true) { // Load queue $item->queue = $this->getQueue($type, $item); + + // Calculate completed state + if(!isset($item->completed) || ($item->completed == false && empty($item->queue))) + { + $table = $this->getTable(); + $table->queue = $item->queue; + $table->successful = $item->successful; + $table->failed = $item->failed; + + $table->clcProgress(); + + $item->completed = $table->completed; + } } // Add params diff --git a/administrator/com_joomgallery/src/Table/MigrationTable.php b/administrator/com_joomgallery/src/Table/MigrationTable.php index 008176f1..9d8c3f45 100644 --- a/administrator/com_joomgallery/src/Table/MigrationTable.php +++ b/administrator/com_joomgallery/src/Table/MigrationTable.php @@ -284,7 +284,7 @@ public function clcProgress() } // Update completed property - if($total === $finished) + if($total === $finished || $total == 0) { $this->completed = true; } From 7ee0ab371f9a5bdf7553419f6c22d12315b4ad9c Mon Sep 17 00:00:00 2001 From: Elfangor Date: Fri, 26 Jan 2024 08:29:14 +0100 Subject: [PATCH 095/121] fix queue ordering of nested sets (categories) --- .../src/Service/Migration/Scripts/Jg3ToJg4.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index 71befe67..f0470c9e 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -351,8 +351,7 @@ public function getQueue(string $type, object $migrateable=null): array // Create the query $query->select($db->quoteName($primarykey)) - ->from($db->quoteName($tablename)) - ->order($db->quoteName($primarykey) . ' ASC'); + ->from($db->quoteName($tablename)); // Apply additional where clauses for specific content types if($type == 'catimage') @@ -378,7 +377,12 @@ public function getQueue(string $type, object $migrateable=null): array // Apply ordering based on level if it is a nested type if($this->get('types')[$type]->get('nested')) { - $query->order($db->quoteName('level') . ' ASC'); + //$query->order($db->quoteName('level') . ' ASC'); + $query->order($db->quoteName('lft') . ' ASC'); + } + else + { + $query->order($db->quoteName($primarykey) . ' ASC'); } $db->setQuery($query); From 8e1bfa96f125e7fa61268d29f5e5eb3a342728c8 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Sat, 27 Jan 2024 11:17:12 +0100 Subject: [PATCH 096/121] fix php error: Factory not found --- .../src/Service/Migration/Migration.php | 12 +++++------- .../src/Service/Migration/Scripts/Jg3ToJg4.php | 3 ++- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index 6853f857..9c5d224a 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -13,17 +13,15 @@ // No direct access \defined('_JEXEC') or die; -use Exception; use \Joomla\CMS\Factory; -use \Joomla\CMS\Log\Log; -use \Joomla\CMS\Language\Text; use \Joomla\Registry\Registry; +use \Joomla\CMS\Language\Text; use \Joomla\CMS\Filesystem\Path; -use \Joomla\Database\DatabaseInterface; use \Joomla\Database\DatabaseFactory; +use \Joomla\Database\DatabaseInterface; use \Joomla\Component\Media\Administrator\Exception\FileNotFoundException; -use \Joomgallery\Component\Joomgallery\Administrator\Helper\JoomHelper; use \Joomgallery\Component\Joomgallery\Administrator\Table\ImageTable; +use \Joomgallery\Component\Joomgallery\Administrator\Helper\JoomHelper; use \Joomgallery\Component\Joomgallery\Administrator\Table\CategoryTable; use \Joomgallery\Component\Joomgallery\Administrator\Table\MigrationTable; use \Joomgallery\Component\Joomgallery\Administrator\Extension\ServiceTrait; @@ -667,7 +665,7 @@ protected function loadTypes() { if(\is_null($this->params)) { - throw new Exception('Migration parameters need to be set in order to load types.', 1); + throw new \Exception('Migration parameters need to be set in order to load types.', 1); } $types = $this->defineTypes(); @@ -731,7 +729,7 @@ public function getTypeNames(): array { if(\is_null($this->params)) { - throw new Exception('Migration parameters need to be set in order to load types.', 1); + throw new \Exception('Migration parameters need to be set in order to load types.', 1); } $types = $this->defineTypes(true); diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index f0470c9e..40aba4af 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -13,13 +13,14 @@ // No direct access \defined('_JEXEC') or die; +use \Joomla\CMS\Factory; use \Joomla\CMS\Language\Text; use \Joomla\CMS\Filesystem\Path; use \Joomla\CMS\Filesystem\File; use \Joomla\CMS\User\UserFactoryInterface; use \Joomla\Component\Media\Administrator\Exception\FileExistsException; -use \Joomgallery\Component\Joomgallery\Administrator\Helper\JoomHelper; use \Joomgallery\Component\Joomgallery\Administrator\Table\ImageTable; +use \Joomgallery\Component\Joomgallery\Administrator\Helper\JoomHelper; use \Joomgallery\Component\Joomgallery\Administrator\Table\CategoryTable; use \Joomgallery\Component\Joomgallery\Administrator\Service\Migration\Checks; use \Joomgallery\Component\Joomgallery\Administrator\Service\Migration\Migration; From f13620acaf754d729388a7802beb0ff6a78002cd Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 29 Jan 2024 19:56:51 +0100 Subject: [PATCH 097/121] fix created_by - adjust Table classes - add onMigrationBeforeSave event --- .../language/en-GB/com_joomgallery.ini | 2 +- .../src/Model/MigrationModel.php | 7 +++- .../src/Service/Migration/Migration.php | 4 +-- .../Service/Migration/MigrationInterface.php | 4 +-- .../Service/Migration/Scripts/Jg3ToJg4.php | 33 +++++++----------- .../src/Service/Migration/Type.php | 34 +++++++++++++------ .../src/Table/CategoryTable.php | 2 +- .../src/Table/CommentsTable.php | 2 +- .../com_joomgallery/src/Table/ConfigTable.php | 2 +- .../src/Table/GalleriesTable.php | 2 +- .../com_joomgallery/src/Table/ImageTable.php | 2 +- .../com_joomgallery/src/Table/TagTable.php | 2 +- .../com_joomgallery/src/Table/UsersTable.php | 5 +++ 13 files changed, 59 insertions(+), 42 deletions(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index af75719a..4e882859 100644 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -380,7 +380,7 @@ COM_JOOMGALLERY_FIELDS_DATABASE_PASS_LABEL="Database Password" COM_JOOMGALLERY_FIELDS_DATABASE_NAME_LABEL="Database Name" COM_JOOMGALLERY_FIELDS_DATABASE_PREFIX_LABEL="Database Tables Prefix" COM_JOOMGALLERY_FIELDS_CHECKOWNER_LABEL="Check owners" -COM_JOOMGALLERY_FIELDS_CHECKOWNER_DESC="If set to yes, the owners of the migrated categories and images will be checked. If a user ID cannot be found in the current Joomla! installation the owner will be set to 0 (no one)." +COM_JOOMGALLERY_FIELDS_CHECKOWNER_DESC="If set to yes, the owners of the migrated content are attempted to be retained. If a given owner ID cannot be found in the current Joomla! installation the owner will be set to be the fallback user of the 'Check owner plugin'." COM_JOOMGALLERY_FIELDS_IMAGEUSAGE_LABEL="Image usage" COM_JOOMGALLERY_FIELDS_IMAGEUSAGE_DESC="How do you want to use the images provided by the source?{tip}Direct usage: No migration of image files. Directly use the images in the source folder as they are. Only the three standard imagetypes available after migration.
Recreate: Use the original image from source for recreating the imagetypes. (Use detail if original is not available)
Copy: Copy the images from source and use them as imagetypes based on mapping.
Move: Move the images from source and use them as imagetypes based on mapping." COM_JOOMGALLERY_FIELDS_IMAGEMAPPING_LABEL="Image mapping" diff --git a/administrator/com_joomgallery/src/Model/MigrationModel.php b/administrator/com_joomgallery/src/Model/MigrationModel.php index 7a2c2a46..6d5c6765 100644 --- a/administrator/com_joomgallery/src/Model/MigrationModel.php +++ b/administrator/com_joomgallery/src/Model/MigrationModel.php @@ -1136,8 +1136,13 @@ protected function insertRecord(string $type, array $data, bool $autoID = true) return false; } + // Trigger the onMigrationBeforeSave event + $event = new \Joomla\Event\Event('onMigrationBeforeSave', ['com_joomgallery.'.$recordType, $table]); + $this->getDispatcher()->dispatch($event->getName(), $event); + $results = $event->getArgument('result', []); + // Store the data. - if(!$table->store()) + if(\in_array(false, $results, true) || !$table->store()) { $this->component->setError($table->getError()); diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index 9c5d224a..05f2a0e2 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -160,9 +160,9 @@ public function __destruct() * @param bool $names_only True to load type names only. No migration parameters required. * * @return array The source types info - * array(tablename, primarykey, isNested, isCategorized, dependent_on, pkstoskip, insertrecord, queuetablename, recordname) + * array(tablename, primarykey, isNested, isCategorized, owner, dependent_on, pkstoskip, insertrecord, queuetablename, recordname) * Needed: tablename, primarykey, isNested, isCategorized - * Optional: dependent_on, pkstoskip, insertrecord, queuetablename, recordname + * Optional: owner, dependent_on, pkstoskip, insertrecord, queuetablename, recordname * * @since 4.0.0 */ diff --git a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php index b18356f3..978ee898 100644 --- a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php +++ b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php @@ -73,9 +73,9 @@ public function getSourceXML(); * @param bool $names_only True to load type names only. No migration parameters required. * * @return array The source types info - * array(tablename, primarykey, isNested, isCategorized, dependent_on, pkstoskip, insertrecord, queuetablename, recordname) + * array(tablename, primarykey, isNested, isCategorized, owner, dependent_on, pkstoskip, insertrecord, queuetablename, recordname) * Needed: tablename, primarykey, isNested, isCategorized - * Optional: dependent_on, pkstoskip, insertrecord, queuetablename, recordname + * Optional: owner, dependent_on, pkstoskip, insertrecord, queuetablename, recordname * * @since 4.0.0 */ diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index 40aba4af..5dd3e1e1 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -154,9 +154,9 @@ public function getSourceDirs(): array * @param bool $names_only True to load type names only. No migration parameters required. * * @return array The source types info - * array(tablename, primarykey, isNested, isCategorized, dependent_on, pkstoskip, insertrecord, queuetablename, recordname) + * array(tablename, primarykey, isNested, isCategorized, owner, dependent_on, pkstoskip, insertrecord, queuetablename, recordname) * Needed: tablename, primarykey, isNested, isCategorized - * Optional: dependent_on, pkstoskip, insertrecord, queuetablename, recordname + * Optional: owner, dependent_on, pkstoskip, insertrecord, queuetablename, recordname * * @since 4.0.0 */ @@ -165,9 +165,9 @@ public function defineTypes($names_only = false): array // Content type definition array // Order of the content types must correspond to the migration order // Pay attention to the dependent_on when ordering here !!! - $types = array( 'category' => array('#__joomgallery_catg', 'cid', true, false, array(), array(1)), - 'image' => array('#__joomgallery', 'id', false, true, array('category')), - 'catimage' => array(_JOOM_TABLE_CATEGORIES, 'cid', false, false, array('category', 'image'), array(1), false, '#__joomgallery_catg', 'category') + $types = array( 'category' => array('#__joomgallery_catg', 'cid', true, false, 'created_by', array(), array(1)), + 'image' => array('#__joomgallery', 'id', false, true, 'created_by', array('category')), + 'catimage' => array(_JOOM_TABLE_CATEGORIES, 'cid', false, false, 'created_by', array('category', 'image'), array(1), false, '#__joomgallery_catg', 'category') ); if($this->params->get('source_ids', 0) == 1) @@ -186,15 +186,15 @@ public function defineTypes($names_only = false): array { foreach($types as $key => $value) { - if(\count($value) < 7 || (\count($value) > 6 && $value[6] !== false)) + if(\count($value) < 8 || (\count($value) > 7 && $value[7] !== false)) { // insertrecord == true, we assume tablename is from source db $types[$key][0] = $value[0] . '_old'; } - elseif(\count($value) > 7 && !empty($value[7]) && $value[6] == false) + elseif(\count($value) > 8 && !empty($value[8]) && $value[7] == false) { // insertrecord == false and queuetablename given, we assume queuetablename is from source db - $types[$key][7] = $value[7] . '_old'; + $types[$key][8] = $value[8] . '_old'; } } } @@ -224,12 +224,9 @@ public function convertData(string $type, array $data): array 3. ('create child'): True, if a child node shall be created in the destination field containing the field values. (default: false / no child) */ - // The fieldname of owner (created_by) - $ownerFieldName = 'owner'; - // Parameter dependet mapping fields $id = \boolval($this->params->get('source_ids', 0)) ? 'id' : false; - $owner = \boolval($this->params->get('check_owner', 0)) ? 'created_by' : false; + $owner = \boolval($this->params->get('check_owner', 0)) ? $this->types[$type]->get('owner') : false; // Configure mapping for each content type switch($type) @@ -302,15 +299,11 @@ public function convertData(string $type, array $data): array break; } - // Check owner - if(\boolval($this->params->get('check_owner', 0))) + // Strip owners with value zero (owner=0) + if(isset($data[$this->types[$type]->get('owner')]) && !$data[$this->types[$type]->get('owner')]) { - // Check if user with the provided userid exists - $user = Factory::getContainer()->get(UserFactoryInterface::class)->loadUserById($data[$ownerFieldName]); - if(!$user || !$user->id) - { - $data[$ownerFieldName] = 0; - } + // Owner is currently set to zero. Set it to be null + $data[$this->types[$type]->get('owner')] = null; } // Apply mapping diff --git a/administrator/com_joomgallery/src/Service/Migration/Type.php b/administrator/com_joomgallery/src/Service/Migration/Type.php index 1904bc1f..fae7e1a0 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Type.php +++ b/administrator/com_joomgallery/src/Service/Migration/Type.php @@ -62,6 +62,15 @@ class Type */ protected $pk = 'id'; + /** + * Name of the owner field + * + * @var string + * + * @since 4.0.0 + */ + protected $owner = 'created_by'; + /** * True if this content type is nested * @@ -157,34 +166,39 @@ public function __construct($name, $list, $lists=array()) if(\count($list) > 4) { - $this->dependent_on = $list[4]; + $this->owner = $list[4]; } if(\count($list) > 5) { - if(\is_array($list[5])) + $this->dependent_on = $list[5]; + } + + if(\count($list) > 6) + { + if(\is_array($list[6])) { - $this->skip = \array_merge($this->skip, $list[5]); + $this->skip = \array_merge($this->skip, $list[6]); } else { - \array_push($this->skip, $list[5]); + \array_push($this->skip, $list[6]); } } - if(\count($list) > 6) + if(\count($list) > 7) { - $this->insertRecord = $list[6]; + $this->insertRecord = $list[7]; } - if(\count($list) > 7) + if(\count($list) > 8) { - $this->queue_tablename = $list[7]; + $this->queue_tablename = $list[8]; } - if(\count($list) > 8) + if(\count($list) > 9) { - $this->recordName = $list[8]; + $this->recordName = $list[9]; } // search for types depending on this type diff --git a/administrator/com_joomgallery/src/Table/CategoryTable.php b/administrator/com_joomgallery/src/Table/CategoryTable.php index ca2d8e58..4704d43f 100644 --- a/administrator/com_joomgallery/src/Table/CategoryTable.php +++ b/administrator/com_joomgallery/src/Table/CategoryTable.php @@ -175,7 +175,7 @@ public function bind($array, $ignore = '') $array['created_time'] = $date->toSql(); } - if($array['id'] == 0 && empty($array['created_by'])) + if(!\key_exists('created_by', $array) || empty($array['created_by'])) { $array['created_by'] = Factory::getUser()->id; } diff --git a/administrator/com_joomgallery/src/Table/CommentsTable.php b/administrator/com_joomgallery/src/Table/CommentsTable.php index 635a261a..60d4aaf8 100644 --- a/administrator/com_joomgallery/src/Table/CommentsTable.php +++ b/administrator/com_joomgallery/src/Table/CommentsTable.php @@ -70,7 +70,7 @@ public function bind($array, $ignore = '') $array['created_time'] = $date->toSql(); } - if($array['id'] == 0 && empty($array['created_by'])) + if(!\key_exists('created_by', $array) || empty($array['created_by'])) { $array['created_by'] = Factory::getUser()->id; } diff --git a/administrator/com_joomgallery/src/Table/ConfigTable.php b/administrator/com_joomgallery/src/Table/ConfigTable.php index def4ff11..0996077d 100644 --- a/administrator/com_joomgallery/src/Table/ConfigTable.php +++ b/administrator/com_joomgallery/src/Table/ConfigTable.php @@ -71,7 +71,7 @@ public function bind($array, $ignore = '') } } - if($array['id'] == 0 && empty($array['created_by'])) + if(!\key_exists('created_by', $array) || empty($array['created_by'])) { $array['created_by'] = Factory::getUser()->id; } diff --git a/administrator/com_joomgallery/src/Table/GalleriesTable.php b/administrator/com_joomgallery/src/Table/GalleriesTable.php index b433d717..561b5ca9 100644 --- a/administrator/com_joomgallery/src/Table/GalleriesTable.php +++ b/administrator/com_joomgallery/src/Table/GalleriesTable.php @@ -101,7 +101,7 @@ public function bind($array, $ignore = '') $array['created_time'] = $date->toSql(); } - if($array['id'] == 0 && (!\key_exists('created_by', $array) || empty($array['created_by']))) + if(!\key_exists('created_by', $array) || empty($array['created_by'])) { $array['created_by'] = Factory::getUser()->id; } diff --git a/administrator/com_joomgallery/src/Table/ImageTable.php b/administrator/com_joomgallery/src/Table/ImageTable.php index 32b31a4f..3b5e59b3 100644 --- a/administrator/com_joomgallery/src/Table/ImageTable.php +++ b/administrator/com_joomgallery/src/Table/ImageTable.php @@ -203,7 +203,7 @@ public function bind($array, $ignore = '') $array['created_time'] = $date->toSql(); } - if($array['id'] == 0 && (!\key_exists('created_by', $array) || empty($array['created_by']))) + if(!\key_exists('created_by', $array) || empty($array['created_by'])) { $array['created_by'] = Factory::getUser()->id; } diff --git a/administrator/com_joomgallery/src/Table/TagTable.php b/administrator/com_joomgallery/src/Table/TagTable.php index 6e2ed093..4e9eb277 100644 --- a/administrator/com_joomgallery/src/Table/TagTable.php +++ b/administrator/com_joomgallery/src/Table/TagTable.php @@ -108,7 +108,7 @@ public function bind($array, $ignore = '') $array['created_time'] = $date->toSql(); } - if($array['id'] == 0 && empty($array['created_by'])) + if(!\key_exists('created_by', $array) || empty($array['created_by'])) { $array['created_by'] = Factory::getUser()->id; } diff --git a/administrator/com_joomgallery/src/Table/UsersTable.php b/administrator/com_joomgallery/src/Table/UsersTable.php index 57490544..a4f68264 100644 --- a/administrator/com_joomgallery/src/Table/UsersTable.php +++ b/administrator/com_joomgallery/src/Table/UsersTable.php @@ -59,6 +59,11 @@ public function bind($array, $ignore = '') $array['created_time'] = $date->toSql(); } + if(!\key_exists('created_by', $array) || empty($array['created_by'])) + { + $array['created_by'] = Factory::getUser()->id; + } + // Support for galleries if(!isset($this->galleries)) { From 4ac0b8ea071f0d0c610f79de0a66d202ac969fcc Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 29 Jan 2024 22:01:11 +0100 Subject: [PATCH 098/121] change default of check_owner --- .../src/Service/Migration/Scripts/Jg3ToJg4.php | 6 +++--- .../src/Service/Migration/Scripts/Jg3ToJg4.xml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index 5dd3e1e1..04d5bea4 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -226,7 +226,7 @@ public function convertData(string $type, array $data): array // Parameter dependet mapping fields $id = \boolval($this->params->get('source_ids', 0)) ? 'id' : false; - $owner = \boolval($this->params->get('check_owner', 0)) ? $this->types[$type]->get('owner') : false; + $owner = \boolval($this->params->get('check_owner', 1)) ? $this->types[$type]->get('owner') : false; // Configure mapping for each content type switch($type) @@ -299,10 +299,10 @@ public function convertData(string $type, array $data): array break; } - // Strip owners with value zero (owner=0) + // Strip zero values for owners (owner=0) if(isset($data[$this->types[$type]->get('owner')]) && !$data[$this->types[$type]->get('owner')]) { - // Owner is currently set to zero. Set it to be null + // Owner is currently set to zero or empty. Set it to be null $data[$this->types[$type]->get('owner')] = null; } diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.xml b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.xml index d004e6cb..d1e5285f 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.xml +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.xml @@ -97,7 +97,7 @@ type="radio" class="btn-group" layout="joomla.form.field.radio.switcher" - default="0" + default="1" label="COM_JOOMGALLERY_FIELDS_CHECKOWNER_LABEL" description="COM_JOOMGALLERY_FIELDS_CHECKOWNER_DESC"> From 24f8053283344f431f014d67c7f113b13b47f247 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 5 Feb 2024 11:48:14 +0100 Subject: [PATCH 099/121] Refactor loadTypes() mthod - solves PHP error in /Service/Migration/Type.php:209 --- .../src/Service/Migration/Migration.php | 76 +++++++----- .../Service/Migration/MigrationInterface.php | 37 +++++- .../Service/Migration/Scripts/Jg3ToJg4.php | 92 ++++++++++---- .../src/Service/Migration/Type.php | 112 ++++++++++-------- 4 files changed, 208 insertions(+), 109 deletions(-) diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index 05f2a0e2..a039f64e 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -157,28 +157,34 @@ public function __destruct() * A list of content type definitions depending on migration source * (Required in migration scripts. The order of the content types must correspond to its migration order) * - * @param bool $names_only True to load type names only. No migration parameters required. + * ------ + * This method is multiple times, when the migration types are loaded. The first time it is called without + * the $type param, just to retrieve the array of source types info. The next times it is called with a + * $type param to load the optional type infos like ownerFieldname. * - * @return array The source types info - * array(tablename, primarykey, isNested, isCategorized, owner, dependent_on, pkstoskip, insertrecord, queuetablename, recordname) - * Needed: tablename, primarykey, isNested, isCategorized - * Optional: owner, dependent_on, pkstoskip, insertrecord, queuetablename, recordname + * Needed: tablename, primarykey, isNested, isCategorized + * Optional: ownerFieldname, dependent_on, pkstoskip, insertRecord, queueTablename, recordName + * + * Assumption for insertrecord: + * If insertrecord == true assumes, that type is a migration; Means reading data from source db and write it to destination db (default) + * If insertrecord == false assumes, that type is an adjustment; Means reading data from destination db adjust it and write it back to destination db + * + * Attention: + * Order of the content types must correspond to the migration order + * Pay attention to the dependent_on when ordering here !!! + * + * @param bool $names_only True to load type names only. No migration parameters required. + * @param Type $type Type object to set optional definitions + * + * @return array The source types info, array(tablename, primarykey, isNested, isCategorized) * * @since 4.0.0 */ - public function defineTypes($names_only = false): array + public function defineTypes($names_only=false, &$type=null): array { - // Content type definition array - // Order of the content types must correspond to the migration order - // Pay attention to the dependent_on when ordering here !!! - // - // Assumption for insertrecord: - // If insertrecord == true assumes, that type is a migration; Means reading data from source db and write it to destination db (default) - // If insertrecord == false assumes, that type is an adjustment; Means reading data from destination db adjust it and write it back to destination db - /* Example: - $types = array( 'category' => array('#__joomgallery_catg', 'cid', true, false, array(), array(1)), - 'image' => array('#__joomgallery', 'id', false, true, array('category')) + $types = array( 'category' => array('#__joomgallery_catg', 'cid', true, false), + 'image' => array('#__joomgallery', 'id', false, true) ); */ @@ -187,9 +193,21 @@ public function defineTypes($names_only = false): array /** * Converts data from source into the structure needed for JoomGallery. + * (Optional in migration scripts, but highly recommended.) + * + * ------ + * How mappings work: + * - Key not in the mapping array: Nothing changes. Field value can be magrated as it is. + * - 'old key' => 'new key': Field name has changed. Old values will be inserted in field with the provided new key. + * - 'old key' => false: Field does not exist anymore or value has to be emptied to create new record in the new table. + * - 'old key' => array(string, string, bool): Field will be merget into another field of type json. + * 1. ('destination field name'): Name of the field to be merged into. + * 2. ('new field name'): New name of the field created in the destination field. (default: false / retain field name) + * 3. ('create child'): True, if a child node shall be created in the destination field containing the field values. (default: false / no child) * + * * @param string $type Name of the content type - * @param array $data Data received from getData() method. + * @param array $data Source data received from getData() * * @return array Converted data to save into JoomGallery * @@ -197,16 +215,6 @@ public function defineTypes($names_only = false): array */ public function convertData(string $type, array $data): array { - /* How mappings work: - - Key not in the mapping array: Nothing changes. Field value can be magrated as it is. - - 'old key' => 'new key': Field name has changed. Old values will be inserted in field with the provided new key. - - 'old key' => false: Field does not exist anymore or value has to be emptied to create new record in the new table. - - 'old key' => array(string, string, bool): Field will be merget into another field of type json. - 1. ('destination field name'): Name of the field to be merged into. - 2. ('new field name'): New name of the field created in the destination field. (default: false / retain field name) - 3. ('create child'): True, if a child node shall be created in the destination field containing the field values. (default: false / no child) - */ - return $data; } @@ -668,14 +676,26 @@ protected function loadTypes() throw new \Exception('Migration parameters need to be set in order to load types.', 1); } + // First call of defineTypes(): Retrieve the array of source types info $types = $this->defineTypes(); + // Create Types objects foreach($types as $key => $list) { - $type = new Type($key, $list, $types); + $type = new Type($key, $list); + + // Pass $type by reference + // Next calls of defineTypes(): Define optional type infos + $this->defineTypes(false, $type); $this->types[$key] = $type; } + + // Fill the dependent_of based on $types->dependent_on + foreach($types as $key => $type) + { + $type->setDependentOf($types); + } } } diff --git a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php index 978ee898..b72f3c48 100644 --- a/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php +++ b/administrator/com_joomgallery/src/Service/Migration/MigrationInterface.php @@ -70,16 +70,30 @@ public function getSourceXML(); * A list of content type definitions depending on migration source * (Required in migration scripts. The order of the content types must correspond to its migration order) * - * @param bool $names_only True to load type names only. No migration parameters required. + * ------ + * This method is multiple times, when the migration types are loaded. The first time it is called without + * the $type param, just to retrieve the array of source types info. The next times it is called with a + * $type param to load the optional type infos like ownerFieldname. * - * @return array The source types info - * array(tablename, primarykey, isNested, isCategorized, owner, dependent_on, pkstoskip, insertrecord, queuetablename, recordname) - * Needed: tablename, primarykey, isNested, isCategorized - * Optional: owner, dependent_on, pkstoskip, insertrecord, queuetablename, recordname + * Needed: tablename, primarykey, isNested, isCategorized + * Optional: ownerFieldname, dependent_on, pkstoskip, insertRecord, queueTablename, recordName + * + * Assumption for insertrecord: + * If insertrecord == true assumes, that type is a migration; Means reading data from source db and write it to destination db (default) + * If insertrecord == false assumes, that type is an adjustment; Means reading data from destination db adjust it and write it back to destination db + * + * Attention: + * Order of the content types must correspond to the migration order + * Pay attention to the dependent_on when ordering here !!! + * + * @param bool $names_only True to load type names only. No migration parameters required. + * @param Type $type Type object to set optional definitions + * + * @return array The source types info, array(tablename, primarykey, isNested, isCategorized) * * @since 4.0.0 */ - public function defineTypes($names_only = false): array; + public function defineTypes($names_only=false, &$type=null): array; /** * Returns a list of involved source directories. @@ -109,7 +123,18 @@ public function getImageSource(array $data): array; /** * Converts data from source into the structure needed for JoomGallery. * (Optional in migration scripts, but highly recommended.) + * + * ------ + * How mappings work: + * - Key not in the mapping array: Nothing changes. Field value can be magrated as it is. + * - 'old key' => 'new key': Field name has changed. Old values will be inserted in field with the provided new key. + * - 'old key' => false: Field does not exist anymore or value has to be emptied to create new record in the new table. + * - 'old key' => array(string, string, bool): Field will be merget into another field of type json. + * 1. ('destination field name'): Name of the field to be merged into. + * 2. ('new field name'): New name of the field created in the destination field. (default: false / retain field name) + * 3. ('create child'): True, if a child node shall be created in the destination field containing the field values. (default: false / no child) * + * * @param string $type Name of the content type * @param array $data Source data received from getData() * diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index 04d5bea4..398aa0af 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -151,23 +151,34 @@ public function getSourceDirs(): array * A list of content type definitions depending on migration source * (Required in migration scripts. The order of the content types must correspond to its migration order) * - * @param bool $names_only True to load type names only. No migration parameters required. + * ------ + * This method is multiple times, when the migration types are loaded. The first time it is called without + * the $type param, just to retrieve the array of source types info. The next times it is called with a + * $type param to load the optional type infos like ownerFieldname. * - * @return array The source types info - * array(tablename, primarykey, isNested, isCategorized, owner, dependent_on, pkstoskip, insertrecord, queuetablename, recordname) - * Needed: tablename, primarykey, isNested, isCategorized - * Optional: owner, dependent_on, pkstoskip, insertrecord, queuetablename, recordname + * Needed: tablename, primarykey, isNested, isCategorized + * Optional: ownerFieldname, dependent_on, pkstoskip, insertRecord, queueTablename, recordName + * + * Assumption for insertrecord: + * If insertrecord == true assumes, that type is a migration; Means reading data from source db and write it to destination db (default) + * If insertrecord == false assumes, that type is an adjustment; Means reading data from destination db adjust it and write it back to destination db + * + * Attention: + * Order of the content types must correspond to the migration order + * Pay attention to the dependent_on when ordering here !!! + * + * @param bool $names_only True to load type names only. No migration parameters required. + * @param Type $type Type object to set optional definitions + * + * @return array The source types info, array(tablename, primarykey, isNested, isCategorized) * * @since 4.0.0 */ - public function defineTypes($names_only = false): array + public function defineTypes($names_only=false, &$type=null): array { - // Content type definition array - // Order of the content types must correspond to the migration order - // Pay attention to the dependent_on when ordering here !!! - $types = array( 'category' => array('#__joomgallery_catg', 'cid', true, false, 'created_by', array(), array(1)), - 'image' => array('#__joomgallery', 'id', false, true, 'created_by', array('category')), - 'catimage' => array(_JOOM_TABLE_CATEGORIES, 'cid', false, false, 'created_by', array('category', 'image'), array(1), false, '#__joomgallery_catg', 'category') + $types = array( 'category' => array('#__joomgallery_catg', 'cid', true, false), + 'image' => array('#__joomgallery', 'id', false, true), + 'catimage' => array(_JOOM_TABLE_CATEGORIES, 'cid', false, false) ); if($this->params->get('source_ids', 0) == 1) @@ -180,6 +191,7 @@ public function defineTypes($names_only = false): array { return \array_keys($types); } + //------- First point of return: Return names only // add special cases if tables in the same db with *_old at the end if($this->params->get('same_db')) @@ -199,14 +211,58 @@ public function defineTypes($names_only = false): array } } + // Return here if type is not given + if(\is_null($type)) + { + return $types; + } + //------- Second point of return: Don't load optional type infos + + // Load the optional type infos: + // ownerFieldname, dependent_on, pkstoskip, insertRecord, queueTablename, recordName + switch($type->name) + { + case 'category': + $type->set('pkstoskip', array(1)); + break; + + case 'image': + $type->set('dependent_on', array('category')); + break; + + case 'catimage': + $type->set('dependent_on', array('category', 'image')); + $type->set('pkstoskip', array(1)); + $type->set('insertRecord', false); + $type->set('queueTablename', '#__joomgallery_catg'); + $type->set('recordName', 'category'); + break; + + default: + // No optional type infos needed + break; + } + return $types; } /** * Converts data from source into the structure needed for JoomGallery. + * (Optional in migration scripts, but highly recommended.) + * + * ------ + * How mappings work: + * - Key not in the mapping array: Nothing changes. Field value can be magrated as it is. + * - 'old key' => 'new key': Field name has changed. Old values will be inserted in field with the provided new key. + * - 'old key' => false: Field does not exist anymore or value has to be emptied to create new record in the new table. + * - 'old key' => array(string, string, bool): Field will be merget into another field of type json. + * 1. ('destination field name'): Name of the field to be merged into. + * 2. ('new field name'): New name of the field created in the destination field. (default: false / retain field name) + * 3. ('create child'): True, if a child node shall be created in the destination field containing the field values. (default: false / no child) * + * * @param string $type Name of the content type - * @param array $data Data received from getData() method. + * @param array $data Source data received from getData() * * @return array Converted data to save into JoomGallery * @@ -214,16 +270,6 @@ public function defineTypes($names_only = false): array */ public function convertData(string $type, array $data): array { - /* How mappings work: - - Key not in the mapping array: Nothing changes. Field value can be magrated as it is. - - 'old key' => 'new key': Field name has changed. Old values will be inserted in field with the provided new key. - - 'old key' => false: Field does not exist anymore or value has to be emptied to create new record in the new table. - - 'old key' => array(string, string, bool): Field will be merget into another field of type json. - 1. ('destination field name'): Name of the field to be merged into. - 2. ('new field name'): New name of the field created in the destination field. (default: false / retain field name) - 3. ('create child'): True, if a child node shall be created in the destination field containing the field values. (default: false / no child) - */ - // Parameter dependet mapping fields $id = \boolval($this->params->get('source_ids', 0)) ? 'id' : false; $owner = \boolval($this->params->get('check_owner', 1)) ? $this->types[$type]->get('owner') : false; diff --git a/administrator/com_joomgallery/src/Service/Migration/Type.php b/administrator/com_joomgallery/src/Service/Migration/Type.php index fae7e1a0..413387f4 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Type.php +++ b/administrator/com_joomgallery/src/Service/Migration/Type.php @@ -69,7 +69,7 @@ class Type * * @since 4.0.0 */ - protected $owner = 'created_by'; + protected $ownerFieldname = 'created_by'; /** * True if this content type is nested @@ -96,7 +96,7 @@ class Type * * @since 4.0.0 */ - protected $skip = array(0); + protected $pkstoskip = array(0); /** * List of types this type depends on. @@ -125,7 +125,7 @@ class Type * * @since 4.0.0 */ - protected $queue_tablename = '#__joomgallery'; + protected $queueTablename = '#__joomgallery'; /** * Do we have to create/inert new database records for this type? @@ -142,75 +142,83 @@ class Type * * @param string $name Name of this content type * @param array $list Source types info created by Migration::defineTypes() - * @param array $lists List of source types info * * @return void * * @since 4.0.0 */ - public function __construct($name, $list, $lists=array()) + public function __construct($name, $list) { $this->name = $name; $this->recordName = $name; if(\count($list) < 4) { - throw new Exception('Type object needs a list of at least 4 entries as the second argument.', 1); + throw new \Exception('Type object needs a list of at least 4 entries as the second argument.', 1); } - $this->tablename = $list[0]; - $this->queue_tablename = $list[0]; - $this->pk = $list[1]; - $this->nested = $list[2]; - $this->categorized = $list[3]; - - if(\count($list) > 4) - { - $this->owner = $list[4]; - } - - if(\count($list) > 5) - { - $this->dependent_on = $list[5]; - } + $this->tablename = $list[0]; + $this->queueTablename = $list[0]; + $this->pk = $list[1]; + $this->nested = $list[2]; + $this->categorized = $list[3]; + } - if(\count($list) > 6) + /** + * Pushes types to the dependent_of based on the provided list of Type objects. + * + * @param Type[] $types List of Type objects + * + * @return void + * + * @since 4.0.0 + */ + public function setDependentOf($types) + { + // search for types depending on this type + if(!empty($types)) { - if(\is_array($list[6])) - { - $this->skip = \array_merge($this->skip, $list[6]); - } - else + foreach($types as $type_name => $type) { - \array_push($this->skip, $list[6]); + if($type && \count($type->get('dependent_on')) > 0 && \in_array($this->name, $type->get('dependent_on'))) + { + \array_push($this->dependent_of, $type_name); + } } } + } - if(\count($list) > 7) - { - $this->insertRecord = $list[7]; - } - - if(\count($list) > 8) - { - $this->queue_tablename = $list[8]; - } - - if(\count($list) > 9) - { - $this->recordName = $list[9]; - } - - // search for types depending on this type - if(!empty($lists)) + /** + * Modifies a property of the object, creating it if it does not already exist. + * + * @param string $property The name of the property. + * @param mixed $value The value of the property to set. + * + * @return mixed Previous value of the property. + * + * @since 4.0.0 + */ + public function set($property, $value = null) + { + switch($property) { - foreach($lists as $type_name => $type_list) - { - if(\count($type_list) > 4 && !empty($type_list[4]) && in_array($name, $type_list[4])) + case 'pkstoskip': + if(\is_array($value)) { - array_push($this->dependent_of, $type_name); + $this->$property = \array_merge($this->$property, $value); } - } - } - } + else + { + \array_push($this->$property, $value); + } + break; + + default: + $previous = $this->$property ?? null; + $this->$property = $value; + break; + } + + return $previous; + } } \ No newline at end of file From bbbca2b3601ee017ef2cd7d1e1577c997f23bd22 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 5 Feb 2024 13:18:08 +0100 Subject: [PATCH 100/121] fix error: '_db' suffix for destination tables --- .../src/Service/Migration/Migration.php | 4 +-- .../Service/Migration/Scripts/Jg3ToJg4.php | 30 +++++++++---------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index a039f64e..8edbffbb 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -692,9 +692,9 @@ protected function loadTypes() } // Fill the dependent_of based on $types->dependent_on - foreach($types as $key => $type) + foreach($this->types as $key => $type) { - $type->setDependentOf($types); + $type->setDependentOf($this->types); } } } diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index 398aa0af..703a1d39 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -176,9 +176,9 @@ public function getSourceDirs(): array */ public function defineTypes($names_only=false, &$type=null): array { - $types = array( 'category' => array('#__joomgallery_catg', 'cid', true, false), - 'image' => array('#__joomgallery', 'id', false, true), - 'catimage' => array(_JOOM_TABLE_CATEGORIES, 'cid', false, false) + $types = array( 'category' => array('#__joomgallery_catg', 'cid', true, false, true), + 'image' => array('#__joomgallery', 'id', false, true, true), + 'catimage' => array(_JOOM_TABLE_CATEGORIES, 'cid', false, false, false) ); if($this->params->get('source_ids', 0) == 1) @@ -193,21 +193,19 @@ public function defineTypes($names_only=false, &$type=null): array } //------- First point of return: Return names only - // add special cases if tables in the same db with *_old at the end + // add suffix, if source tables are in the same db with *_old at the end + $source_db_suffix = ''; if($this->params->get('same_db')) { - foreach($types as $key => $value) + $source_db_suffix = '_old'; + } + + foreach($types as $key => $value) + { + if(\count($value) < 5 || (\count($value) > 4 && $value[4])) { - if(\count($value) < 8 || (\count($value) > 7 && $value[7] !== false)) - { - // insertrecord == true, we assume tablename is from source db - $types[$key][0] = $value[0] . '_old'; - } - elseif(\count($value) > 8 && !empty($value[8]) && $value[7] == false) - { - // insertrecord == false and queuetablename given, we assume queuetablename is from source db - $types[$key][8] = $value[8] . '_old'; - } + // tablename is from source db and has to be checked + $types[$key][0] = $value[0] . $source_db_suffix; } } @@ -234,7 +232,7 @@ public function defineTypes($names_only=false, &$type=null): array $type->set('dependent_on', array('category', 'image')); $type->set('pkstoskip', array(1)); $type->set('insertRecord', false); - $type->set('queueTablename', '#__joomgallery_catg'); + $type->set('queueTablename', '#__joomgallery_catg' . $source_db_suffix); $type->set('recordName', 'category'); break; From 96f26ca8b4ec0ec1cbc092b1ecf8a8c5cf5cf04f Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 5 Feb 2024 13:26:57 +0100 Subject: [PATCH 101/121] propagate changes on Type object --- .../com_joomgallery/src/Service/Migration/Migration.php | 4 ++-- .../src/Service/Migration/Scripts/Jg3ToJg4.php | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index 8edbffbb..abbd07b1 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -242,7 +242,7 @@ public function getQueue(string $type, object $migrateable=null): array $this->loadTypes(); // Queue gets always loaded from source db - $tablename = $this->types[$type]->get('queue_tablename'); + $tablename = $this->types[$type]->get('queueTablename'); $primarykey = $this->types[$type]->get('pk'); // Get db object @@ -804,7 +804,7 @@ public function needsMigration(string $type, int $pk): bool } // Specific record primary keys which can be skiped - foreach($this->types[$type]->get('skip') as $skip) + foreach($this->types[$type]->get('pkstoskip') as $skip) { if($pk == $skip) { diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index 703a1d39..09869814 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -270,7 +270,7 @@ public function convertData(string $type, array $data): array { // Parameter dependet mapping fields $id = \boolval($this->params->get('source_ids', 0)) ? 'id' : false; - $owner = \boolval($this->params->get('check_owner', 1)) ? $this->types[$type]->get('owner') : false; + $owner = \boolval($this->params->get('check_owner', 1)) ? $this->types[$type]->get('ownerFieldname') : false; // Configure mapping for each content type switch($type) @@ -344,10 +344,10 @@ public function convertData(string $type, array $data): array } // Strip zero values for owners (owner=0) - if(isset($data[$this->types[$type]->get('owner')]) && !$data[$this->types[$type]->get('owner')]) + if(isset($data[$this->types[$type]->get('ownerFieldname')]) && !$data[$this->types[$type]->get('ownerFieldname')]) { // Owner is currently set to zero or empty. Set it to be null - $data[$this->types[$type]->get('owner')] = null; + $data[$this->types[$type]->get('ownerFieldname')] = null; } // Apply mapping @@ -378,7 +378,7 @@ public function getQueue(string $type, object $migrateable=null): array $this->loadTypes(); // Queue gets always loaded from source db - $tablename = $this->types[$type]->get('queue_tablename'); + $tablename = $this->types[$type]->get('queueTablename'); $primarykey = $this->types[$type]->get('pk'); // Get db object From 29e9618a26514c9834b3d8b94b07f7d60cddf001 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 5 Feb 2024 13:28:32 +0100 Subject: [PATCH 102/121] fix php warning: undefined variable $previous --- administrator/com_joomgallery/src/Service/Migration/Type.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/administrator/com_joomgallery/src/Service/Migration/Type.php b/administrator/com_joomgallery/src/Service/Migration/Type.php index 413387f4..f463963e 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Type.php +++ b/administrator/com_joomgallery/src/Service/Migration/Type.php @@ -203,6 +203,8 @@ public function set($property, $value = null) switch($property) { case 'pkstoskip': + $previous = $this->$property ?? null; + if(\is_array($value)) { $this->$property = \array_merge($this->$property, $value); From 9373d5df348e26dc62162611279f1563cc06f284 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 5 Feb 2024 13:47:15 +0100 Subject: [PATCH 103/121] fix issue: The XML file of your source extension could not be found. --- .../language/en-GB/com_joomgallery.ini | 2 +- .../src/Service/Migration/Migration.php | 2 +- .../Service/Migration/Scripts/Jg3ToJg4.php | 27 +++++++++++++++---- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index 4e882859..b01b97eb 100644 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -370,7 +370,7 @@ COM_JOOMGALLERY_FIELDS_DEST_LBL="Configuration for destination data writing" COM_JOOMGALLERY_FIELDS_SAMEJOOMLA_LABEL="Same Joomla! installation?" COM_JOOMGALLERY_FIELDS_SAMEJOOMLA_DESC="Is the source located inside the same Joomla! installation folder?" COM_JOOMGALLERY_FIELDS_JOOMLAPATH_LABEL="Joomla! path" -COM_JOOMGALLERY_FIELDS_JOOMLAPATH_DESC="Path to the Joomla! installation of the source." +COM_JOOMGALLERY_FIELDS_JOOMLAPATH_DESC="Complete system path to the Joomla! root directory of the source.{tip}Use forward slash (/) as directoy separator. No slash at the end of the path." COM_JOOMGALLERY_FIELDS_SAMEDB_LABEL="Same database?" COM_JOOMGALLERY_FIELDS_SAMEDB_DESC="Is the source located inside the same Joomla! database?" COM_JOOMGALLERY_FIELDS_DATABASE_TYPE_LABEL="Database Type" diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index abbd07b1..950c5141 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -880,7 +880,7 @@ protected function checkSourceExtension(Checks &$checks, string $category) if(!($src_xml = $this->getSourceXML())) { // Source XML not found - $checks->addCheck($category, 'src_xml', false, false, Text::_('COM_JOOMGALLERY_FIELDS_SRC_EXTENSION_LABEL'), Text::_('COM_JOOMGALLERY_SERVICE_MIGRATION_SOURCE_XML')); + $checks->addCheck($category, 'src_xml', false, false, Text::_('COM_JOOMGALLERY_FIELDS_SRC_EXTENSION_LABEL'), Text::sprintf('COM_JOOMGALLERY_SERVICE_MIGRATION_SOURCE_XML', $src_xml)); return; } diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index 09869814..4993c1c6 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -107,7 +107,7 @@ public function getTargetinfo(string $type = 'source'): Targetinfo /** * Returns the XML object of the source extension * - * @return \SimpleXMLElement Extension XML object or False on failure + * @return \SimpleXMLElement|string Extension XML object or XML path on failure * * @since 4.0.0 */ @@ -115,17 +115,34 @@ public function getSourceXML() { if($this->params->get('same_joomla')) { - return \simplexml_load_file(Path::clean(JPATH_ADMINISTRATOR . '/components/com_joomgallery/joomgallery_old.xml')); + $path = Path::clean(JPATH_ADMINISTRATOR . '/components/com_joomgallery/joomgallery_old.xml'); + $xml = \simplexml_load_file($path); + + if($xml){return $xml;}else{return $path;} } else { - if(\file_exists(Path::clean($this->params->get('joomla_path') . '/components/com_joomgallery/joomgallery.xml'))) + $joomla_root = $this->params->get('joomla_path'); + + // Remove directory separator at the end + if(\substr($joomla_root, -1) == '/' || \substr($joomla_root, -1) == \DIRECTORY_SEPARATOR) { - return \simplexml_load_file(Path::clean($this->params->get('joomla_path') . '/components/com_joomgallery/joomgallery.xml')); + $joomla_root = \substr($joomla_root, 0, -1); + } + + if(\file_exists(Path::clean($joomla_root . '/components/com_joomgallery/joomgallery.xml'))) + { + $path = Path::clean($joomla_root . '/components/com_joomgallery/joomgallery.xml'); + $xml = \simplexml_load_file($path); + + if($xml){return $xml;}else{return $path;} } else { - return \simplexml_load_file(Path::clean($this->params->get('joomla_path') . '/components/com_joomgallery/joomgallery_old.xml')); + $path = Path::clean($joomla_root . '/components/com_joomgallery/joomgallery_old.xml'); + $xml = \simplexml_load_file($path); + + if($xml){return $xml;}else{return $path;} } } } From 29eb8a2b0ea140aaa1237e8ff8207a8878ac1b2e Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Tue, 6 Feb 2024 17:46:45 +0100 Subject: [PATCH 104/121] add administrator to XML path --- .../src/Service/Migration/Scripts/Jg3ToJg4.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index 4993c1c6..173ec8d5 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -130,16 +130,16 @@ public function getSourceXML() $joomla_root = \substr($joomla_root, 0, -1); } - if(\file_exists(Path::clean($joomla_root . '/components/com_joomgallery/joomgallery.xml'))) + if(\file_exists(Path::clean($joomla_root . '/administrator/components/com_joomgallery/joomgallery.xml'))) { - $path = Path::clean($joomla_root . '/components/com_joomgallery/joomgallery.xml'); + $path = Path::clean($joomla_root . '/administrator/components/com_joomgallery/joomgallery.xml'); $xml = \simplexml_load_file($path); if($xml){return $xml;}else{return $path;} } else { - $path = Path::clean($joomla_root . '/components/com_joomgallery/joomgallery_old.xml'); + $path = Path::clean($joomla_root . '/administrator/components/com_joomgallery/joomgallery_old.xml'); $xml = \simplexml_load_file($path); if($xml){return $xml;}else{return $path;} From aacbe19d711b700fdac2545a11ea1cfe723569ca Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Tue, 6 Feb 2024 18:05:54 +0100 Subject: [PATCH 105/121] fix issue: Table: #__joomgallery_categories failed --- .../com_joomgallery/src/Service/Migration/Migration.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index 950c5141..9e48cc16 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -714,7 +714,7 @@ public function getSourceTables(): array $tables = array(); foreach($this->types as $key => $type) { - $tables[$key] = $this->types[$key]->get('tablename'); + $tables[$key] = $this->types[$key]->get('queueTablename'); } return $tables; From 602c8fd569c636be6a53a237146b5ba37af76974 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Tue, 6 Feb 2024 18:17:09 +0100 Subject: [PATCH 106/121] fix misspelling --- .../language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini index 7298ff91..320018c9 100644 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini @@ -30,4 +30,4 @@ FILES_JOOMGALLERY_FIELDS_NEW_DIRS_DESC="Do you want to convert your migrated cat FILES_JOOMGALLERY_MIGRATION_CHECK_COMPATIBILITY_MODE="Compatibility mode" FILES_JOOMGALLERY_MIGRATION_CHECK_COMPATIBILITY_MODE_DESC="You have chosen to use the old folder structure in step 1. In order for this to work the compatibility mode has to be activated in the component configuration. Please activate 'JG3 compatibility mode' in the JoomGallery configuration." FILES_JOOMGALLERY_SERVICE_MIGRATION_MCR="Move/Copy/Recreate" -FILES_JOOMGALLERY_SERVICE_MIGRATION_MCR_ERROR="Move, Copy and Recreate is not possible if source and destination is in the same filesystem and at the same time the old folder structure should be used. You have chosen an impossible combination of migration options. Please chose other migration options. Impossible combination: [Image usage: Move|Copy|Recreate, Same Joomla! instalaltion: Yes, Use new folder structure: No, (JG4 config)Filesystem: Local]" \ No newline at end of file +FILES_JOOMGALLERY_SERVICE_MIGRATION_MCR_ERROR="Move, Copy and Recreate is not possible if source and destination is in the same filesystem and at the same time the old folder structure should be used. You have chosen an impossible combination of migration options. Please chose other migration options. Impossible combination: [Image usage: Move|Copy|Recreate, Same Joomla! installation: Yes, Use new folder structure: No, (JG4 config)Filesystem: Local]" \ No newline at end of file From b146194d606621f8441d5ef64ab258af2967eb52 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Wed, 14 Feb 2024 11:20:15 +0100 Subject: [PATCH 107/121] option for recreate old folderstructure use new folder structure = no image usage != direct usage --- .../src/Service/Migration/Scripts/Jg3ToJg4.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index 173ec8d5..0c1435e8 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -547,6 +547,7 @@ public function migrateFolder(CategoryTable $cat, array $data): bool // Rename folders if needed if($this->params->get('new_dirs', 1) == 1) { + // Create new/JG4 folder structure // Get new foldername out of path $newName = \basename($cat->path); @@ -564,8 +565,16 @@ public function migrateFolder(CategoryTable $cat, array $data): bool else { // Recreate, copy or move - // Create new folders - return $this->component->getFileManager()->createCategory($cat->alias, $cat->parent_id); + if($this->params->get('new_dirs', 1) == 1) + { + // Create new/JG4 folder structure (based on alias) + return $this->component->getFileManager()->createCategory($cat->alias, $cat->parent_id); + } + else + { + // Create old/JG3 folder structure (based on static_path) + return $this->component->getFileManager()->createCategory(\basename($cat->static_path), $cat->parent_id); + } } return true; From eff719318ff1330e23b8d379effdbdae49131644 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 22 Jan 2024 19:47:22 +0100 Subject: [PATCH 108/121] adjust category path when compatibility mode --- .../src/Service/FileManager/FileManager.php | 39 +++++++++++++++---- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/administrator/com_joomgallery/src/Service/FileManager/FileManager.php b/administrator/com_joomgallery/src/Service/FileManager/FileManager.php index e4ca0df1..39c02554 100644 --- a/administrator/com_joomgallery/src/Service/FileManager/FileManager.php +++ b/administrator/com_joomgallery/src/Service/FileManager/FileManager.php @@ -1085,9 +1085,9 @@ public function getImgPath($img, $type, $catid=false, $filename=false, $root=fal public function getCatPath($cat, $type=false, $parent=false, $alias=false, $root=false) { // We got a valid category object - if(\is_object($cat) && isset($cat->path)) + if(\is_object($cat) && \property_exists($cat, 'path')) { - $path = $cat->path; + $path = $this->catReadPath($cat); } // We got a category path elseif(\is_string($cat) && $this->is_path($cat)) @@ -1112,21 +1112,21 @@ public function getCatPath($cat, $type=false, $parent=false, $alias=false, $root return false; } - $path = $cat->path; + $path = $this->catReadPath($cat); } // We got a parent category plus alias elseif($parent && $alias) { // We got a valid parent category object - if(\is_object($parent) && isset($parent->path)) + if(\is_object($parent) && \property_exists($parent, 'path')) { - if(empty($parent->path)) + if(empty($this->catReadPath($parent))) { $path = $alias; } else { - $path = $parent->path.\DIRECTORY_SEPARATOR.$alias; + $path = $this->catReadPath($parent).\DIRECTORY_SEPARATOR.$alias; } } // We got a parent category path @@ -1152,13 +1152,13 @@ public function getCatPath($cat, $type=false, $parent=false, $alias=false, $root return false; } - if(empty($parent->path)) + if(empty($this->catReadPath($parent))) { $path = $alias; } else { - $path = $parent->path.\DIRECTORY_SEPARATOR.$alias; + $path = $this->catReadPath($parent).\DIRECTORY_SEPARATOR.$alias; } } } @@ -1348,4 +1348,27 @@ protected function is_path($string) return true; } + + /** + * Get path from category object + * + * @param object $cat Category object + * + * @return string Path to the category + * + * @since 4.0.0 + */ + protected function catReadPath(object $cat): string + { + if($this->component->getConfig()->get('jg_compatibility_mode', 0) && !empty($cat->static_path)) + { + // Compatibility mode active + return $cat->static_path; + } + else + { + // Standard method + return $cat->path; + } + } } From ece32202a17b30764943b83a40881f423a8cacf6 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Wed, 14 Feb 2024 11:25:03 +0100 Subject: [PATCH 109/121] add #__joomgallery_migration to uninstall --- administrator/com_joomgallery/sql/uninstall.mysql.utf8.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/administrator/com_joomgallery/sql/uninstall.mysql.utf8.sql b/administrator/com_joomgallery/sql/uninstall.mysql.utf8.sql index 37d5cd8f..f9ff3ff6 100644 --- a/administrator/com_joomgallery/sql/uninstall.mysql.utf8.sql +++ b/administrator/com_joomgallery/sql/uninstall.mysql.utf8.sql @@ -7,6 +7,7 @@ DROP TABLE IF EXISTS `#__joomgallery_fields`; DROP TABLE IF EXISTS `#__joomgallery_galleries`; DROP TABLE IF EXISTS `#__joomgallery_galleries_ref`; DROP TABLE IF EXISTS `#__joomgallery_img_types`; +DROP TABLE IF EXISTS `#__joomgallery_migration`; DROP TABLE IF EXISTS `#__joomgallery_tags`; DROP TABLE IF EXISTS `#__joomgallery_tags_ref`; DROP TABLE IF EXISTS `#__joomgallery_users`; From fee80a1015bd3cdf5cdba381a5510b6203977829 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Wed, 14 Feb 2024 12:10:14 +0100 Subject: [PATCH 110/121] improve catpath check --- .../en-GB/com_joomgallery.migration.Jg3ToJg4.ini | 2 +- .../src/Service/Migration/Scripts/Jg3ToJg4.php | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini index 320018c9..f4e434c4 100644 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini @@ -30,4 +30,4 @@ FILES_JOOMGALLERY_FIELDS_NEW_DIRS_DESC="Do you want to convert your migrated cat FILES_JOOMGALLERY_MIGRATION_CHECK_COMPATIBILITY_MODE="Compatibility mode" FILES_JOOMGALLERY_MIGRATION_CHECK_COMPATIBILITY_MODE_DESC="You have chosen to use the old folder structure in step 1. In order for this to work the compatibility mode has to be activated in the component configuration. Please activate 'JG3 compatibility mode' in the JoomGallery configuration." FILES_JOOMGALLERY_SERVICE_MIGRATION_MCR="Move/Copy/Recreate" -FILES_JOOMGALLERY_SERVICE_MIGRATION_MCR_ERROR="Move, Copy and Recreate is not possible if source and destination is in the same filesystem and at the same time the old folder structure should be used. You have chosen an impossible combination of migration options. Please chose other migration options. Impossible combination: [Image usage: Move|Copy|Recreate, Same Joomla! installation: Yes, Use new folder structure: No, (JG4 config)Filesystem: Local]" \ No newline at end of file +FILES_JOOMGALLERY_SERVICE_MIGRATION_MCR_ERROR="Move, Copy or Recreate within the same filesystem (same joomla, same filesystem) and by keeping the old folder structure is impossible. You have chosen an impossible combination of migration options. Please adjust your migration options. Impossible combination: [Image usage: Move|Copy|Recreate, Same Joomla! installation: Yes, Use new folder structure: No, (JG4 config)Filesystem: Local]" \ No newline at end of file diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index 0c1435e8..1cacca40 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -573,6 +573,16 @@ public function migrateFolder(CategoryTable $cat, array $data): bool else { // Create old/JG3 folder structure (based on static_path) + if( $this->params->get('same_joomla', 1) == 1 && + $this->component->getConfig()->get('jg_filesystem', 'local-images') == 'local-images' + ) + { + // Recreate, copy or move within the same filesystem by keeping the old folder structure is impossible + $this->component->setError('FILES_JOOMGALLERY_SERVICE_MIGRATION_MCR_ERROR'); + + return false; + } + return $this->component->getFileManager()->createCategory(\basename($cat->static_path), $cat->parent_id); } } @@ -959,7 +969,7 @@ protected function checkCatpath(\stdClass $cat): bool { // Prepare catpath $catpath = \basename($cat->catpath); - $parentpath = \rtrim($cat->catpath, '/'.$catpath); + $parentpath = \substr($cat->catpath, 0, -1 * \strlen('/'.$catpath)); // Prepare alias $alias = \basename($cat->alias); From f4c8d623211331a6cd463fcaad7b98d004715efd Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Wed, 14 Feb 2024 12:27:25 +0100 Subject: [PATCH 111/121] activate compatibility mode --- administrator/com_joomgallery/forms/config.xml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/administrator/com_joomgallery/forms/config.xml b/administrator/com_joomgallery/forms/config.xml index 4e2f27bf..1049a568 100644 --- a/administrator/com_joomgallery/forms/config.xml +++ b/administrator/com_joomgallery/forms/config.xml @@ -95,19 +95,20 @@ - + description="COM_JOOMGALLERY_CONFIG_FTPUPLOAD_PATH_LONG" /> --> @@ -116,10 +117,11 @@ From 1f855f0ee395eada4a3051f83486ccc08a81fd94 Mon Sep 17 00:00:00 2001 From: Elfangor Date: Thu, 15 Feb 2024 08:32:16 +0100 Subject: [PATCH 112/121] add indexing to JoomHelper::getRecords() --- .../com_joomgallery/src/Helper/JoomHelper.php | 23 +++++++++++++++++-- .../Service/Migration/Scripts/Jg3ToJg4.php | 4 ++-- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/administrator/com_joomgallery/src/Helper/JoomHelper.php b/administrator/com_joomgallery/src/Helper/JoomHelper.php index 0b2680c1..48db6c16 100644 --- a/administrator/com_joomgallery/src/Helper/JoomHelper.php +++ b/administrator/com_joomgallery/src/Helper/JoomHelper.php @@ -281,12 +281,13 @@ public static function getParent($name, $id) * * @param string $name The name of the record (available: categories,images,tags,imagetypes) * @param Object $com_obj JoomgalleryComponent object if available + * @param string $key Index the returning array by key * * @return array|bool Array on success, false on failure. * * @since 4.0.0 */ - public static function getRecords($name, $com_obj=null) + public static function getRecords($name, $com_obj=null, $key=null) { $availables = array('categories', 'images', 'tags', 'imagetypes'); @@ -297,7 +298,7 @@ public static function getRecords($name, $com_obj=null) return false; } - // get the JoomgalleryComponent object if needed + // Get the JoomgalleryComponent object if needed if(!isset($com_obj) || !\strpos('JoomgalleryComponent', \get_class($com_obj)) === false) { $com_obj = Factory::getApplication()->bootComponent('com_joomgallery'); @@ -313,6 +314,24 @@ public static function getRecords($name, $com_obj=null) // Attempt to load the record. $return = $model->getItems(); + // Indexing the array if needed + if($return && !\is_null($key)) + { + $ind_array = array(); + foreach($return as $obj) + { + if(\property_exists($obj, $key)) + { + $ind_array[$obj->{$key}] = $obj; + } + } + + if(\count($ind_array) > 0) + { + $return = $ind_array; + } + } + return $return; } diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index 1cacca40..dccb3224 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -703,8 +703,8 @@ protected function reuseImages(ImageTable $img, array $sources, bool $copy = fal $this->component->createFileManager(); $this->component->createFilesystem($this->component->getConfig()->get('jg_filesystem','local-images')); - // Fetch available imagetypes - $imagetypes = $this->get('imagetypes_dict'); + // Fetch available imagetypes from destination + $imagetypes = JoomHelper::getRecords('imagetypes', $this->component, 'typename'); // Check the source mapping if(\count(\array_diff_key($imagetypes, $sources)) !== 0 || \count(\array_diff_key($sources, $imagetypes)) !== 0) From 7c69b0b3a540d295aace909274f8ae886c1de761 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Sat, 17 Feb 2024 13:11:15 +0100 Subject: [PATCH 113/121] remove mapping check when recreate --- .../com_joomgallery/src/Service/Migration/Migration.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index 9e48cc16..6c278ea4 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -528,7 +528,7 @@ public function precheck(): array $this->checkDestTable($checks, 'destination'); // Check image mapping - if($this->params->get('image_usage', 1) > 0) + if($this->params->get('image_usage', 1) > 1) { $this->checkImageMapping($checks, 'destination'); } From 0885caa2cc4eb1099dc3e9f34096ddb5a3e14aa5 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Sat, 17 Feb 2024 13:12:51 +0100 Subject: [PATCH 114/121] change default for image_usage --- .../src/Service/Migration/Migration.php | 2 +- .../src/Service/Migration/Scripts/Jg3ToJg4.php | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/administrator/com_joomgallery/src/Service/Migration/Migration.php b/administrator/com_joomgallery/src/Service/Migration/Migration.php index 6c278ea4..c7c409d6 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Migration.php +++ b/administrator/com_joomgallery/src/Service/Migration/Migration.php @@ -528,7 +528,7 @@ public function precheck(): array $this->checkDestTable($checks, 'destination'); // Check image mapping - if($this->params->get('image_usage', 1) > 1) + if($this->params->get('image_usage', 0) > 1) { $this->checkImageMapping($checks, 'destination'); } diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index dccb3224..050fe9bc 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -492,15 +492,15 @@ public function migrateFiles(ImageTable $img, array $data): bool // Set old catid for image creation $img->catid = $data['catid']; - if($this->params->get('image_usage', 1) == 1) + if($this->params->get('image_usage', 0) == 1) { // Recreate imagetypes based on given image $res = $this->createImages($img, $img_source[0]); } - elseif($this->params->get('image_usage', 1) == 2 || $this->params->get('image_usage', 1) == 3) + elseif($this->params->get('image_usage', 0) == 2 || $this->params->get('image_usage', 0) == 3) { $copy = false; - if($this->params->get('image_usage', 1) == 2) + if($this->params->get('image_usage', 0) == 2) { $copy = true; } @@ -533,7 +533,7 @@ public function migrateFolder(CategoryTable $cat, array $data): bool // Create file manager service $this->component->createFileManager(); - if($this->params->get('image_usage', 1) == 0) + if($this->params->get('image_usage', 0) == 0) { // Direct usage if($this->params->get('same_joomla', 1) == 0) @@ -901,7 +901,7 @@ public function scriptSpecificChecks(string $type, Checks &$checks, string $cate //------------------------ // Check use case: Direct usage - if($this->params->get('image_usage', 1) == 0) + if($this->params->get('image_usage', 0) == 0) { if($this->params->get('same_joomla', 1) == 0) { From 47ed4690adeaa38a04fcd14dfd39b40a214cc32c Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Sat, 17 Feb 2024 15:47:33 +0100 Subject: [PATCH 115/121] Add help button --- administrator/com_joomgallery/language/en-GB/com_joomgallery.ini | 1 + administrator/com_joomgallery/src/View/Migration/HtmlView.php | 1 + 2 files changed, 2 insertions(+) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index b01b97eb..6b9db78c 100644 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -103,6 +103,7 @@ COM_JOOMGALLERY_CONFIRM="Confirm" COM_JOOMGALLERY_CONFIRM_ERROR_MESSAGE="Error message" COM_JOOMGALLERY_DIRECT_USAGE="Direct usage" COM_JOOMGALLERY_STEP_X="Step %s" +COM_JOOMGALLERY_WEBSITE_HELP_URL="https://www.joomgalleryfriends.net/help/%s" ;Control panel COM_JOOMGALLERY_CONTROL_PANEL="Control Panel" diff --git a/administrator/com_joomgallery/src/View/Migration/HtmlView.php b/administrator/com_joomgallery/src/View/Migration/HtmlView.php index ecc2e47f..6982fc20 100644 --- a/administrator/com_joomgallery/src/View/Migration/HtmlView.php +++ b/administrator/com_joomgallery/src/View/Migration/HtmlView.php @@ -53,6 +53,7 @@ public function display($tpl = null) { $this->app->input->set('hidemainmenu', true); ToolbarHelper::cancel('migration.cancel', 'COM_JOOMGALLERY_MIGRATION_INERRUPT_MIGRATION'); + ToolbarHelper::help('', false, Text::sprintf('COM_JOOMGALLERY_WEBSITE_HELP_URL', 'migration/'. \strtolower($this->script->name) . '?tmpl=component')); // Check if requested script exists if(!\in_array($this->script->name, \array_keys($this->scripts))) From a6418eb68b71e0d445d908ab3f07ffbea7e56aac Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Sun, 18 Feb 2024 15:37:53 +0100 Subject: [PATCH 116/121] add precheck: compatibility mode deactivated If Use new folder structure=yes, compatibility mode has to be deactivated --- .../language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini | 3 ++- .../src/Service/Migration/Scripts/Jg3ToJg4.php | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini index f4e434c4..fab6d79d 100644 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini @@ -28,6 +28,7 @@ FILES_JOOMGALLERY_MIGRATION_CHECK_CATEGORY_CATPATH_DESC="There are inconsistent FILES_JOOMGALLERY_FIELDS_NEW_DIRS_LABEL="Use new folder structure" FILES_JOOMGALLERY_FIELDS_NEW_DIRS_DESC="Do you want to convert your migrated categories to the new folder structure style (filesystem)?{tip}New path style (JG4+): root-of-filesystem/joomgallery/imagetype/parent-category-path/alias/
Old path style (JG3-): joomla-root/images/joomgallery/imagetype/parent-category-path/name_cid/
In JG3-, in most cases the alias is derivable from the category name. This might be wrong if you renamed category titles without regenrating the alias. In this case a convertion of the old JG3- folder structure to the new one is not possible.
If you choose to keep the old folder structure, you have to switch on the 'JG3 compatibility mode' in the components global configuration in order for the component to find images stored in the old folder structure style." FILES_JOOMGALLERY_MIGRATION_CHECK_COMPATIBILITY_MODE="Compatibility mode" -FILES_JOOMGALLERY_MIGRATION_CHECK_COMPATIBILITY_MODE_DESC="You have chosen to use the old folder structure in step 1. In order for this to work the compatibility mode has to be activated in the component configuration. Please activate 'JG3 compatibility mode' in the JoomGallery configuration." +FILES_JOOMGALLERY_MIGRATION_CHECK_COMPATIBILITY_MODE_ON_DESC="You have chosen to use the old folder structure in step 1. In order for this to work the compatibility mode has to be activated in the component configuration. Please activate 'JG3 compatibility mode' in the JoomGallery configuration." +FILES_JOOMGALLERY_MIGRATION_CHECK_COMPATIBILITY_MODE_OFF_DESC="You have chosen to use the new folder structure in step 1. In order for this to work the compatibility mode has to be deactivated in the component configuration. Please deactivate 'JG3 compatibility mode' in the JoomGallery configuration." FILES_JOOMGALLERY_SERVICE_MIGRATION_MCR="Move/Copy/Recreate" FILES_JOOMGALLERY_SERVICE_MIGRATION_MCR_ERROR="Move, Copy or Recreate within the same filesystem (same joomla, same filesystem) and by keeping the old folder structure is impossible. You have chosen an impossible combination of migration options. Please adjust your migration options. Impossible combination: [Image usage: Move|Copy|Recreate, Same Joomla! installation: Yes, Use new folder structure: No, (JG4 config)Filesystem: Local]" \ No newline at end of file diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index 050fe9bc..a52ec9dc 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -887,6 +887,12 @@ public function scriptSpecificChecks(string $type, Checks &$checks, string $cate { $checks->addCheck($category, 'src_table_cat_path', false, false, Text::_('FILES_JOOMGALLERY_MIGRATION_CHECK_CATEGORY_CATPATH'), Text::sprintf('FILES_JOOMGALLERY_MIGRATION_CHECK_CATEGORY_CATPATH_DESC', \implode(', ', $inconsistent))); } + + // Check if compatibility mode is deactivated + if($this->component->getConfig()->get('jg_compatibility_mode', 0) == 1) + { + $checks->addCheck($category, 'compatibility_mode', false, false, Text::_('FILES_JOOMGALLERY_MIGRATION_CHECK_COMPATIBILITY_MODE'), Text::_('FILES_JOOMGALLERY_MIGRATION_CHECK_COMPATIBILITY_MODE_OFF_DESC')); + } } else { @@ -894,7 +900,7 @@ public function scriptSpecificChecks(string $type, Checks &$checks, string $cate // Check if compatibility mode is activated if($this->component->getConfig()->get('jg_compatibility_mode', 0) == 0) { - $checks->addCheck($category, 'compatibility_mode', false, false, Text::_('FILES_JOOMGALLERY_MIGRATION_CHECK_COMPATIBILITY_MODE'), Text::_('FILES_JOOMGALLERY_MIGRATION_CHECK_COMPATIBILITY_MODE_DESC')); + $checks->addCheck($category, 'compatibility_mode', false, false, Text::_('FILES_JOOMGALLERY_MIGRATION_CHECK_COMPATIBILITY_MODE'), Text::_('FILES_JOOMGALLERY_MIGRATION_CHECK_COMPATIBILITY_MODE_ON_DESC')); } } From 3fe390dcf5c31e8d062bb493dbeba6a4fdeff010 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Sun, 18 Feb 2024 15:48:52 +0100 Subject: [PATCH 117/121] change jg_compatibility_mode to sensitive param Make the compatibility mode a sensitive parameter --- administrator/com_joomgallery/forms/config.xml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/administrator/com_joomgallery/forms/config.xml b/administrator/com_joomgallery/forms/config.xml index 1049a568..bd2de4bc 100644 --- a/administrator/com_joomgallery/forms/config.xml +++ b/administrator/com_joomgallery/forms/config.xml @@ -117,11 +117,10 @@
From ad39ff9890f92cf75ee2227909523f8d2b5755f7 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 19 Feb 2024 09:05:58 +0100 Subject: [PATCH 118/121] fix issue: wrong category path when compatibility is enabled When migrating the path of the category are not generated correctly when the compatibility mode is enabled and Use new folder struct = no. --- .../src/Service/FileManager/FileManager.php | 32 ++++++++++--------- .../src/Table/CategoryTable.php | 4 ++- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/administrator/com_joomgallery/src/Service/FileManager/FileManager.php b/administrator/com_joomgallery/src/Service/FileManager/FileManager.php index 39c02554..064bfa6b 100644 --- a/administrator/com_joomgallery/src/Service/FileManager/FileManager.php +++ b/administrator/com_joomgallery/src/Service/FileManager/FileManager.php @@ -1071,23 +1071,24 @@ public function getImgPath($img, $type, $catid=false, $filename=false, $root=fal /** * Returns the path to a category without root path. * - * @param object|int|string $cat Category object, category ID or category alias (new categories: ID=0) - * @param string|bool $type Imagetype if needed - * @param object|int|string|bool $parent Parent category object, parent category ID, parent category alias or parent category path (default: false) - * @param string|bool $alias The category alias (default: false) - * @param boolean $root True to add the system root to the path + * @param object|int|string $cat Category object, category ID or category alias (new categories: ID=0) + * @param string|bool $type Imagetype if needed + * @param object|int|string|bool $parent Parent category object, parent category ID, parent category alias or parent category path (default: false) + * @param string|bool $alias The category alias (default: false) + * @param boolean $root True to add the system root to the path + * @param boolean $compatibility Take into account the compatibility mode when creating the path * * * @return mixed Path to the category on success, false otherwise * * @since 4.0.0 */ - public function getCatPath($cat, $type=false, $parent=false, $alias=false, $root=false) + public function getCatPath($cat, $type=false, $parent=false, $alias=false, $root=false, $compatibility=true) { // We got a valid category object if(\is_object($cat) && \property_exists($cat, 'path')) { - $path = $this->catReadPath($cat); + $path = $this->catReadPath($cat, $compatibility); } // We got a category path elseif(\is_string($cat) && $this->is_path($cat)) @@ -1112,7 +1113,7 @@ public function getCatPath($cat, $type=false, $parent=false, $alias=false, $root return false; } - $path = $this->catReadPath($cat); + $path = $this->catReadPath($cat, $compatibility); } // We got a parent category plus alias elseif($parent && $alias) @@ -1120,13 +1121,13 @@ public function getCatPath($cat, $type=false, $parent=false, $alias=false, $root // We got a valid parent category object if(\is_object($parent) && \property_exists($parent, 'path')) { - if(empty($this->catReadPath($parent))) + if(empty($this->catReadPath($parent, $compatibility))) { $path = $alias; } else { - $path = $this->catReadPath($parent).\DIRECTORY_SEPARATOR.$alias; + $path = $this->catReadPath($parent, $compatibility).\DIRECTORY_SEPARATOR.$alias; } } // We got a parent category path @@ -1152,13 +1153,13 @@ public function getCatPath($cat, $type=false, $parent=false, $alias=false, $root return false; } - if(empty($this->catReadPath($parent))) + if(empty($this->catReadPath($parent, $compatibility))) { $path = $alias; } else { - $path = $this->catReadPath($parent).\DIRECTORY_SEPARATOR.$alias; + $path = $this->catReadPath($parent, $compatibility).\DIRECTORY_SEPARATOR.$alias; } } } @@ -1352,15 +1353,16 @@ protected function is_path($string) /** * Get path from category object * - * @param object $cat Category object + * @param object $cat Category object + * @param boolean $compatibility Take into account the compatibility mode * * @return string Path to the category * * @since 4.0.0 */ - protected function catReadPath(object $cat): string + protected function catReadPath(object $cat, bool $compatibility): string { - if($this->component->getConfig()->get('jg_compatibility_mode', 0) && !empty($cat->static_path)) + if($compatibility && $this->component->getConfig()->get('jg_compatibility_mode', 0) && !empty($cat->static_path)) { // Compatibility mode active return $cat->static_path; diff --git a/administrator/com_joomgallery/src/Table/CategoryTable.php b/administrator/com_joomgallery/src/Table/CategoryTable.php index 4704d43f..a6693bff 100644 --- a/administrator/com_joomgallery/src/Table/CategoryTable.php +++ b/administrator/com_joomgallery/src/Table/CategoryTable.php @@ -353,7 +353,9 @@ public function check() // Create new path based on alias and parent category $manager = JoomHelper::getService('FileManager'); - $this->path = $manager->getCatPath(0, false, $this->parent_id, $this->alias); + $filesystem = JoomHelper::getService('Filesystem'); + $this->path = $manager->getCatPath($this->id, false, $this->parent_id, $this->alias, false, false); + $this->path = $filesystem->cleanPath($this->path, '/'); // Support for subform field params if(empty($this->params)) From 35dbc79d8da209e53e28aa8b55b610e9f60bbd45 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 19 Feb 2024 10:09:08 +0100 Subject: [PATCH 119/121] add help popup for category path More help for the case of incosistent category path in source database. --- .../com_joomgallery/language/en-GB/com_joomgallery.ini | 1 + .../language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini | 5 +++-- .../src/Service/Migration/Scripts/Jg3ToJg4.php | 2 +- administrator/com_joomgallery/tmpl/migration/step2.php | 4 +++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index 6b9db78c..e8e3463d 100644 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -761,6 +761,7 @@ COM_JOOMGALLERY_SERVICE_MIGRATION_ERRORS_ERROR="Error during migration of type ' COM_JOOMGALLERY_SERVICE_MIGRATION_ERRORS_SUCCESS="No errors detected from migration manager." COM_JOOMGALLERY_SERVICE_MIGRATION_SOURCE_DATA_DELETE_SUCCESSFUL="Source data successfully removed." COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_DUMMY_RECORD="Dummy record of type %s with ID=%s could not be inserted into database. Does a record with this id already exist in the table?" +COM_JOOMGALLERY_SERVICE_MIGRATION_COMMON_CHECK_HELP="If you face any problems, call for help at the Joom::Gallery Forum" ;Menu COM_JOOMGALLERY_MENU_CATEGORY_VIEW_OPTIONS="Category View" diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini index fab6d79d..088e5d4a 100644 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini @@ -17,14 +17,15 @@ FILES_JOOMGALLERY_MIGRATION_CATEGORY_TITLE="Migration: Categories" FILES_JOOMGALLERY_MIGRATION_CATIMAGE_TITLE="Adjustment: Category thumbnails" FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_TITLE="Image filenames" FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_DESC="There are different filenames for thumbnail and detail image defined in the database of your JG3 tables. Number of affected images: %s" -FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_HELP="It was detected that for some images in the database table of the JG3 (source, #__com_joomgallery) different filenames are stored in the rows 'imgfilename' and 'imgthumbname'. This script could result in errorous migration results. We strongly advise to clean up the database manually before starting the migration. For all the affected Images you have to do the following:
  • Rename the thumbnail image in the filesystem to be the one set in the old JoomGallery image table at row 'imgfilename'.
  • In the old JoomGallery image table, set the value of the row 'imgthumbname' to be the same as in row 'imgfilename'.

ID's of affected images: (%s)

If you face any problems, call for help in our forum at https://www.forum.en.joomgalleryfriends.net" +FILES_JOOMGALLERY_MIGRATION_CHECK_IMAGE_FILENAMES_HELP="It was detected that for some images in the database table of the JG3 (source, #__com_joomgallery) different filenames are stored in the rows 'imgfilename' and 'imgthumbname'. This script could result in errorous migration results. We strongly advise to clean up the database manually before starting the migration. For all the affected Images you have to do the following:
  • Rename the thumbnail image in the filesystem to be the one set in the old JoomGallery image table at row 'imgfilename'.
  • In the old JoomGallery image table, set the value of the row 'imgthumbname' to be the same as in row 'imgfilename'.

ID's of affected images: (%s)" FILES_JOOMGALLERY_MIGRATION_ERROR_TYPE_PREREQUIREMENT="Prerequirement for this content type is not fulfilled. Please make shure the following content types are completely migrated and none of them failed." FILES_JOOMGALLERY_SERVICE_MIGRATION_DIRECT_USAGE_ERROR="Its not possible to directly use images, if source is outside this Joomla! installation. You can not set 'Same Joomla! installation' to yes and 'Image usage' to 'Direct usage' together in step 1." FILES_JOOMGALLERY_SERVICE_MIGRATION_DIRECT_USAGE_IMGTYPES_ERROR="Direct usage of images is only possible with the three default imagetypes (original, detail, thumbnail) activated. Please remove all other imagetypes from the JoomGallery configuration." FILES_JOOMGALLERY_SERVICE_MIGRATION_DIRECT_USAGE_LOCAL_ERROR="Direct usage of images is only possible when using the local filesystem. Please switch to the local filesystem in the JoomGallery configuration if you want to use 'Direct usage'." FILES_JOOMGALLERY_SERVICE_MIGRATION_DIRECT_USAGE_ORIGINAL_WARNING="You have chosen direct usage of images in step 1. Please make sure the 'original' imagetype is deactivated if it was deactivated in your source/previous Joomla version." FILES_JOOMGALLERY_MIGRATION_CHECK_CATEGORY_CATPATH="Category paths" -FILES_JOOMGALLERY_MIGRATION_CHECK_CATEGORY_CATPATH_DESC="There are inconsistent category folder paths defined in the database of your JG3 tables. Please adjust affected category paths manually, or switch off 'Use new folder structure' in step 1 to use the old static catpath instead. ID's of affected categories: %s" +FILES_JOOMGALLERY_MIGRATION_CHECK_CATEGORY_CATPATH_DESC="There are inconsistent category folder paths defined in the database of your JG3 tables. Please adjust affected category paths manually, or switch off 'Use new folder structure' in step 1 to use the old static catpath instead" +FILES_JOOMGALLERY_MIGRATION_CHECK_CATEGORY_CATPATH_HELP="Some categories in the database table of the JG3 (source, #__com_joomgallery_catg) do not have consistent data entries for cid, name, alias, and catpath. This happened because of a bug in previous JoomGallery versions which was fixed in JoomGallery v3.7.0. Thus an inconsistent data basis in the database was created when renaming or moving categories without clearing the alias field. This inconsistency prevents a migration into the new folder structure and without compatibility mode activated.

There are two options to solve that:
  • Clean up the source database manually or by using the maintenance manager of JoomGallery v3.7.0. (recommended)
  • Disable 'Use new folder structure' in the migration options and activate 'JG3 compatibility mode' in the component configuration. (not recommended)
We strongly advise to clean up the database before starting the migration. This way JoomGallery can be migrated into the proper, new structure which corresponds to the Joomla way of doing it. This will prevent your system for errors and illogical behaviour patterns.

A consistent JG3 category table (#__com_joomgallery_catg) fulfils the following conditions:
  • alias = parentalias/title
    where title is converted into an URL safe string (lowercase, no special characters and umlauts, no whitespaces, ...)
  • path = parentpath/title_cid
    where title is converted just like the alias and cid is the category id.

ID's of affected categories: (%s)" FILES_JOOMGALLERY_FIELDS_NEW_DIRS_LABEL="Use new folder structure" FILES_JOOMGALLERY_FIELDS_NEW_DIRS_DESC="Do you want to convert your migrated categories to the new folder structure style (filesystem)?{tip}New path style (JG4+): root-of-filesystem/joomgallery/imagetype/parent-category-path/alias/
Old path style (JG3-): joomla-root/images/joomgallery/imagetype/parent-category-path/name_cid/
In JG3-, in most cases the alias is derivable from the category name. This might be wrong if you renamed category titles without regenrating the alias. In this case a convertion of the old JG3- folder structure to the new one is not possible.
If you choose to keep the old folder structure, you have to switch on the 'JG3 compatibility mode' in the components global configuration in order for the component to find images stored in the old folder structure style." FILES_JOOMGALLERY_MIGRATION_CHECK_COMPATIBILITY_MODE="Compatibility mode" diff --git a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php index a52ec9dc..241a7a95 100644 --- a/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php +++ b/administrator/com_joomgallery/src/Service/Migration/Scripts/Jg3ToJg4.php @@ -885,7 +885,7 @@ public function scriptSpecificChecks(string $type, Checks &$checks, string $cate if(\count($inconsistent) > 0) { - $checks->addCheck($category, 'src_table_cat_path', false, false, Text::_('FILES_JOOMGALLERY_MIGRATION_CHECK_CATEGORY_CATPATH'), Text::sprintf('FILES_JOOMGALLERY_MIGRATION_CHECK_CATEGORY_CATPATH_DESC', \implode(', ', $inconsistent))); + $checks->addCheck($category, 'src_table_cat_path', false, false, Text::_('FILES_JOOMGALLERY_MIGRATION_CHECK_CATEGORY_CATPATH'), Text::_('FILES_JOOMGALLERY_MIGRATION_CHECK_CATEGORY_CATPATH_DESC'), Text::sprintf('FILES_JOOMGALLERY_MIGRATION_CHECK_CATEGORY_CATPATH_HELP', \implode(', ', $inconsistent))); } // Check if compatibility mode is deactivated diff --git a/administrator/com_joomgallery/tmpl/migration/step2.php b/administrator/com_joomgallery/tmpl/migration/step2.php index 5fea3126..7c81b943 100644 --- a/administrator/com_joomgallery/tmpl/migration/step2.php +++ b/administrator/com_joomgallery/tmpl/migration/step2.php @@ -156,8 +156,10 @@ function openModal(event, element) let modalTitle = modal.querySelector('.modal-title'); let modalBody = modal.querySelector('.modal-body'); + let helpForum = '

' + modalTitle.innerHTML = element.getAttribute('data-title'); - modalBody.innerHTML = element.getAttribute('data-text'); + modalBody.innerHTML = element.getAttribute('data-text')+helpForum; let bsmodal = new bootstrap.Modal(modal, {keyboard: false}); bsmodal.show(); From 84c4b238d40da8637b6e6bb577a0a883231618c9 Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Mon, 19 Feb 2024 10:42:04 +0100 Subject: [PATCH 120/121] change skript desc --- .../language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini index 088e5d4a..ccd70189 100644 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.migration.Jg3ToJg4.ini @@ -8,7 +8,7 @@ ; FILES_JOOMGALLERY_MIGRATION_JG3TOJG4="JoomGallery 3.x to JoomGallery 4.x migration script" FILES_JOOMGALLERY_MIGRATION_JG3TOJG4_TITLE="JoomGallery 3.x to JoomGallery 4.x" -FILES_JOOMGALLERY_MIGRATION_JG3TOJG4_DESC="Migration of content types (images, categories) from JoomGallery version 3.x to JoomGallery version 4.x." +FILES_JOOMGALLERY_MIGRATION_JG3TOJG4_DESC="Migration of the main content types (images, categories) from JoomGallery version 3.x to JoomGallery version 4.x." FILES_JOOMGALLERY_MIGRATION_JG3TOJG4_ORIGPATH_DESC="Path to original image" FILES_JOOMGALLERY_MIGRATION_JG3TOJG4_DETAILPATH_DESC="Path to detail image" FILES_JOOMGALLERY_MIGRATION_JG3TOJG4_THUMBPATH_DESC="Path to thumbnail image" From 41b405cd5bedba80652dff47dfebb809d4a7adbd Mon Sep 17 00:00:00 2001 From: 19leunam93 Date: Wed, 21 Feb 2024 20:42:48 +0100 Subject: [PATCH 121/121] rearrange language strings --- .../com_joomgallery/language/en-GB/com_joomgallery.ini | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini index e8e3463d..96e08670 100644 --- a/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini +++ b/administrator/com_joomgallery/language/en-GB/com_joomgallery.ini @@ -762,6 +762,11 @@ COM_JOOMGALLERY_SERVICE_MIGRATION_ERRORS_SUCCESS="No errors detected from migrat COM_JOOMGALLERY_SERVICE_MIGRATION_SOURCE_DATA_DELETE_SUCCESSFUL="Source data successfully removed." COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_DUMMY_RECORD="Dummy record of type %s with ID=%s could not be inserted into database. Does a record with this id already exist in the table?" COM_JOOMGALLERY_SERVICE_MIGRATION_COMMON_CHECK_HELP="If you face any problems, call for help at the Joom::Gallery Forum" +COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_APPLYSTATE_FORMCHECK="Record migration state couldn\'t be modified. Please fill out form correctly." +COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_APPLYSTATE_SUCCESSFUL_0="Record of type %s with id=%s successfully marked as failed" +COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_APPLYSTATE_SUCCESSFUL_1="Record of type %s with id=%s successfully marked as successful" +COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_APPLYSTATE_SUCCESSFUL_2="Record of type %s with id=%s successfully marked as pending" +COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_APPLYSTATE_NOT_AVAILABLE="Record with id=%s not available. State can not be changed." ;Menu COM_JOOMGALLERY_MENU_CATEGORY_VIEW_OPTIONS="Category View" @@ -783,11 +788,6 @@ COM_JOOMGALLERY_MENU_JUSTIFIED_GAP="Gap" COM_JOOMGALLERY_MENU_JUSTIFIED_GAP_NOTE_DESC="Set row and column gap." COM_JOOMGALLERY_MENU_CATEGORY_VIEW_LIGHTBOX_DESC="Use Lightbox Gallery" COM_JOOMGALLERY_MENU_CATEGORY_VIEW_LIGHTBOX_LABEL="Use Lightbox Gallery" -COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_APPLYSTATE_FORMCHECK="Record migration state couldn\'t be modified. Please fill out form correctly." -COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_APPLYSTATE_SUCCESSFUL_0="Record of type %s with id=%s successfully marked as failed" -COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_APPLYSTATE_SUCCESSFUL_1="Record of type %s with id=%s successfully marked as successful" -COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_APPLYSTATE_SUCCESSFUL_2="Record of type %s with id=%s successfully marked as pending" -COM_JOOMGALLERY_SERVICE_MIGRATION_ERROR_APPLYSTATE_NOT_AVAILABLE="Record with id=%s not available. State can not be changed." ; Plugin events COM_JOOMGALLERY_PLUGIN_ERROR_RETURN_VALUE="Return value of the plugin event '%s' must be of type %s and contain : %s"