* @git Joomla Component Builder * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // No direct access to this file defined('_JEXEC') or die('Restricted access'); use Joomla\CMS\MVC\Model\BaseDatabaseModel; use Joomla\CMS\Filesystem\File; use Joomla\CMS\Filesystem\Folder; use Joomla\Utilities\ArrayHelper; use PhpOffice\PhpSpreadsheet\IOFactory; use VDM\Joomla\Componentbuilder\Package\Factory as PackageFactory; use VDM\Joomla\Utilities\ArrayHelper as JCBArrayHelper; use VDM\Joomla\Utilities\ObjectHelper; use VDM\Joomla\Utilities\StringHelper; use VDM\Joomla\Utilities\JsonHelper; use VDM\Joomla\Utilities\FileHelper; use VDM\Joomla\Utilities\GetHelper; use VDM\Joomla\Utilities\GuidHelper; use Joomla\CMS\Language\Text; /** * Componentbuilder Import_joomla_components Base Database Model */ class ComponentbuilderModelImport_joomla_components extends BaseDatabaseModel { // set uploading values protected $use_streams = false; protected $allow_unsafe = false; protected $safeFileOptions = array(); /** * @var object JTable object */ protected $_table = null; /** * @var object JTable object */ protected $_url = null; /** * Model context string. * * @var string */ protected $_context = 'com_componentbuilder.import_joomla_components'; /** * Import Settings */ protected $getType = NULL; protected $dataType = NULL; /** * Method to auto-populate the model state. * * Note. Calling getState in this method will result in recursion. * * @return void * */ protected function populateState() { $app = JFactory::getApplication('administrator'); $this->setState('message', $app->getUserState('com_componentbuilder.message')); $app->setUserState('com_componentbuilder.message', ''); // Recall the 'Import from Directory' path. $path = $app->getUserStateFromRequest($this->_context . '.import_directory', 'import_directory', $app->get('tmp_path')); $this->setState('import.directory', $path); parent::populateState(); } public $canmerge = 1; public $importGuidOnly = 1; public $postfix = false; public $forceUpdate = 0; public $hasKey = 0; public $sleutle = ''; public $data = false; public $app; protected $dir = false; protected $target = false; protected $newID = array(); protected $packageInfo = null; protected $noopmaker; protected $updateAfter = array('field' => array(), 'adminview' => array()); protected $divergedDataMover = array(); protected $fieldTypes = array(); protected $isMultiple = array(); protected $tableColumns = array(); protected $fieldImportErrors = array(); protected $specialValue = false; protected $checksum = null; protected $checksumURLs = array('vdm' => 'https://raw.githubusercontent.com/vdm-io/JCB-Packages/master/', 'jcb' => 'https://raw.githubusercontent.com/vdm-io/JCB-Community-Packages/master/'); protected $mustMerge = array('validation_rule', 'fieldtype', 'snippet', 'language', 'language_translation', 'class_extends', 'class_property', 'class_method', 'joomla_plugin_group'); /** * Import an spreadsheet from either folder, url or upload. * * @return boolean result of import * */ public function import() { // set the state to import $this->setState('action', 'import'); // get App $this->app = JFactory::getApplication(); // get session $session = JFactory::getSession(); // some defaults $package = null; $continue = false; // get import type $this->getType = $this->app->input->getString('gettype', NULL); // do checksum validation $this->checksum = $this->app->input->getString('checksum', NULL); // get import type $this->dataType = $session->get('dataType_VDM_IMPORTINTO', NULL); // if we have no package if ($package === null) { // we must allow unsafe with smart import since it is code being imported. if ($this->dataType === 'smart_package') { $this->allow_unsafe = true; } // action based on get Type switch ($this->getType) { case 'folder': // Remember the 'Import from Directory' path. $this->app->getUserStateFromRequest($this->_context . '.import_directory', 'import_directory'); $package = $this->_getPackageFromFolder(); break; case 'upload': $package = $this->_getPackageFromUpload(); break; case 'url': $url = JFactory::getApplication()->input->getString('import_url'); if ($url === base64_encode(base64_decode($url, true))) { JFactory::getApplication()->input->set('import_url', base64_decode($url)); } $package = $this->_getPackageFromUrl(); break; case 'continue-ext': $continue = true; $package = $session->get('package', null); $package = json_decode($package, true); // clear session $session->clear('package'); $session->clear('dataType'); $session->clear('hasPackage'); // before we clear this, we add it locally $packageInfo = $session->get('smart_package_info', null); // now clear it $session->clear('smart_package_info'); // convert to an array if found if ($packageInfo && JsonHelper::check($packageInfo)) { $this->packageInfo = json_decode($packageInfo, true); } break; default: $package = $session->get('package', null); if (JsonHelper::check($package)) { // get package $package = json_decode($package, true); // remove zip file if (isset($package['packagename'])) { $this->remove($package['packagename']); } // check if dir is set if (isset($package['dir'])) { // set auto loader ComponentbuilderHelper::autoLoader('smart'); // get install folder $dir = JFile::stripExt($package['dir']); // remove unziped folder ComponentbuilderHelper::removeFolder($dir); } } // clear session $session->clear('package'); $session->clear('dataType'); $session->clear('hasPackage'); $session->clear('smart_package_info'); $back = $session->get('backto_VDM_IMPORT', NULL); $session->clear('backto_VDM_IMPORT'); if ($back) { $this->app->setUserState('com_componentbuilder.redirect_url', 'index.php?option=com_componentbuilder&view=' . $back); } $session->clear('backto_VDM_IMPORT'); return false; break; } } // Was the package valid? if (!$package || !$package['type']) { if (in_array($this->getType, array('upload', 'url'))) { $this->remove($package['packagename']); } $this->app->setUserState('com_componentbuilder.message', Text::_('COM_COMPONENTBUILDER_IMPORT_UNABLE_TO_FIND_IMPORT_PACKAGE')); return false; } // first link data to table headers if(!$continue) { // check if this a smart package, if true then get info if ($this->dataType !== 'smart_package' || !$this->getInfo($package, $session)) { $this->app->setUserState('com_componentbuilder.message', Text::_('COM_COMPONENTBUILDER_THERE_WAS_AN_ERROR_GETTING_THE_PACKAGE_INFO')); return false; } // set package to session $package = json_encode($package); $session->set('package', $package); $session->set('dataType', $this->dataType); $session->set('hasPackage', true); return true; } // force update $this->forceUpdate = $this->app->input->getInt('force_update', 0); // show more information $this->moreInfo = $this->app->input->getInt('more_info', 0); // allow merge $this->canmerge = $this->app->input->getInt('canmerge', 1); // forch GUID only search $this->importGuidOnly = $this->app->input->getInt('import_guid_only', 0); // has a key $this->hasKey = $this->app->input->getInt('haskey', 0); // die sleutle $this->sleutle = $this->app->input->getString('sleutle', ''); // try to store/set data if (!$this->setData($package)) { // There was an error importing the package $msg = Text::_('COM_COMPONENTBUILDER_IMPORT_ERROR'); $msgType = 'error'; $back = $session->get('backto_VDM_IMPORT', NULL); if ($back) { $this->app->setUserState('com_componentbuilder.redirect_url', 'index.php?option=com_componentbuilder&view='.$back); $session->clear('backto_VDM_IMPORT'); } $result = false; } else { // Package imported sucessfully $msg = JText::sprintf('COM_COMPONENTBUILDER_IMPORT_SUCCESS', $package['packagename']); $msgType = 'success'; $back = $session->get('backto_VDM_IMPORT', NULL); if ($back) { $this->app->setUserState('com_componentbuilder.redirect_url', 'index.php?option=com_componentbuilder&view='.$back); $session->clear('backto_VDM_IMPORT'); } $result = true; } // Set message to qeue $this->app->enqueueMessage($msg, $msgType); // remove file after import $this->remove($package['packagename']); return $result; } protected function getInfo(&$package, &$session) { // set the data if(isset($package['dir'])) { // set auto loader ComponentbuilderHelper::autoLoader('smart'); // extract the package if (JFile::exists($package['dir'])) { // does this package pass a checksum $checksum = false; $checksumStatus = 'warning'; $checksumMessage = Text::_('COM_COMPONENTBUILDER_PLEASE_NOTE_THAT_THIS_PACKAGE_BHAS_NOB_CHECKSUM_VALIDATION'); // do hash validation here for git repos if (StringHelper::check($this->checksum) && isset($this->checksumURLs[$this->checksum])) { // get packages checksums $checksums = FileHelper::getContent($this->checksumURLs[$this->checksum].'checksum.json'); if (JsonHelper::check($checksums)) { // convert to array $checksums = json_decode($checksums, true); // get package name $packageName = basename($package['dir']); // check if package is found if (isset($checksums[$packageName])) { // validate checksum if ($checksums[$packageName] === sha1_file($package['dir'])) { $validator = $this->checksumURLs[$this->checksum].str_replace('.zip', '.sha', $packageName); $checksumMessage = JText::sprintf('COM_COMPONENTBUILDER_THIS_PACKAGE_BPASSEDB_THE_CHECKSUM_VALIDATIONBR_BR_SMALLMANUALLY_ALSO_VALIDATE_THAT_THE_CORRECT_CHECKSUM_WAS_USEDSMALLBR_THIS_CHECKSUM_BSB_MUST_BE_THE_SAME_AS_THE_ONE_FOUND_A_S_SA', $checksums[$packageName], 'href="'.$validator.'" target="_blank" title="verify checksum">', $validator); $checksumStatus = 'Success'; $checksum = true; } } // set error if (!$checksum) { $checksumMessage = Text::_('COM_COMPONENTBUILDER_BBEST_TO_NOT_CONTINUEBBR_YOU_CAN_REFRESH_AND_TRY_AGAINBR_BUT_NOTE_THAT_THIS_PACKAGE_BFAILEDB_CHECKSUM_VALIDATION_THIS_COULD_BE_A_SERIOUS_SECURITY_BREACH_DO_NOT_CONTINUE'); $checksumStatus = 'error'; } } // set error else { $checksumMessage = Text::_('COM_COMPONENTBUILDER_BBEST_TO_NOT_CONTINUEBBR_WE_COULD_NOT_LOAD_THE_CHECKSUM_FOR_THIS_PACKAGE_AND_SO_NO_VALIDATION_WAS_POSSIBLE_THIS_MAY_BE_DUE_TO_YOUR_NETWORK_OR_A_CHANGE_TO_THAT_PACKAGE_NAME'); $checksumStatus = 'error'; } } // do not have check sum validation $this->app->enqueueMessage($checksumMessage, $checksumStatus); // get the zip adapter $zip = JArchive::getAdapter('zip'); // set the directory name $this->dir = JFile::stripExt($package['dir']); // unzip the package $zip->extract($package['dir'], $this->dir); // check for database file $infoFile = $this->dir . '/info.vdm'; if (JFile::exists($infoFile)) { // load the data if ($info = @file_get_contents($infoFile)) { // unlock the info data if ((($info_ = PackageFactory::_('Crypt')->decrypt($info, 'local')) !== null && JsonHelper::check($info_)) || (($info_ = PackageFactory::_('Crypt')->decrypt($info, 'local.legacy')) !== null && JsonHelper::check($info_)) || (($info_ = PackageFactory::_('Crypt')->decrypt($info, 'local.fof')) !== null && JsonHelper::check($info_))) { // we only continue if info could be opened $session->set('smart_package_info', $info_); return true; } // do not have check sum validation $this->app->enqueueMessage(Text::_('COM_COMPONENTBUILDER_HTWOWE_COULD_NOT_OPEN_THE_PACKAGEHTWOTHIS_COULD_BE_DUE_TO_THE_FOFENCRYPTION_THAT_IS_NO_LONGER_SUPPORTED_IN_JOOMLA_PLEASE_EXPORT_YOUR_PACKAGES_WITH_JCB_VTHREEZEROELEVENPRO_OR_VTWOONETWOSEVENTEENPUBLIC_OR_HIGHER_TO_BE_ABLE_TO_IMPORT_IT_INTO_THIS_VERSION_OF_JCB'), 'error'); } } ComponentbuilderHelper::removeFolder($this->dir); } } return false; } /** * Works out an importation spreadsheet from a HTTP upload * * @return spreadsheet definition or false on failure */ protected function _getPackageFromUpload() { // Get the uploaded file information $app = JFactory::getApplication(); $input = $app->input; // Do not change the filter type 'raw'. We need this to let files containing PHP code to upload. See JInputFiles::get. $userfile = $input->files->get('import_package', null, 'raw'); // Make sure that file uploads are enabled in php if (!(bool) ini_get('file_uploads')) { $app->enqueueMessage(JText::_('COM_COMPONENTBUILDER_IMPORT_MSG_WARNIMPORTFILE'), 'warning'); return false; } // If there is no uploaded file, we have a problem... if (!is_array($userfile)) { $app->enqueueMessage(JText::_('COM_COMPONENTBUILDER_IMPORT_MSG_NO_FILE_SELECTED'), 'warning'); return false; } // Check if there was a problem uploading the file. if ($userfile['error'] || $userfile['size'] < 1) { $app->enqueueMessage(JText::_('COM_COMPONENTBUILDER_IMPORT_MSG_WARNIMPORTUPLOADERROR'), 'warning'); return false; } // Build the appropriate paths $config = JFactory::getConfig(); $tmp_dest = $config->get('tmp_path') . '/' . $userfile['name']; $tmp_src = $userfile['tmp_name']; // Move uploaded file $p_file = File::upload($tmp_src, $tmp_dest, $this->use_streams, $this->allow_unsafe, $this->safeFileOptions); // Was the package downloaded? if (!$p_file) { $session = JFactory::getSession(); $session->clear('package'); $session->clear('dataType'); $session->clear('hasPackage'); // was not uploaded return false; } // check that this is a valid spreadsheet $package = $this->check($userfile['name']); return $package; } /** * Import an spreadsheet from a directory * * @return array Spreadsheet details or false on failure * */ protected function _getPackageFromFolder() { $app = JFactory::getApplication(); $input = $app->input; // Get the path to the package to import $p_dir = $input->getString('import_directory'); $p_dir = JPath::clean($p_dir); // Did you give us a valid path? if (!file_exists($p_dir)) { $app->enqueueMessage(JText::_('COM_COMPONENTBUILDER_IMPORT_MSG_PLEASE_ENTER_A_PACKAGE_DIRECTORY'), 'warning'); return false; } // Detect the package type $type = $this->getType; // Did you give us a valid package? if (!$type) { $app->enqueueMessage(JText::_('COM_COMPONENTBUILDER_IMPORT_MSG_PATH_DOES_NOT_HAVE_A_VALID_PACKAGE'), 'warning'); } // check the extention if(!$this->checkExtension($p_dir)) { // set error message $app->enqueueMessage(JText::_('COM_COMPONENTBUILDER_IMPORT_MSG_DOES_NOT_HAVE_A_VALID_FILE_TYPE'), 'warning'); return false; } $package['packagename'] = null; $package['dir'] = $p_dir; $package['type'] = $type; return $package; } /** * Import an spreadsheet from a URL * * @return Package details or false on failure * */ protected function _getPackageFromUrl() { $app = JFactory::getApplication(); $input = $app->input; // Get the URL of the package to import $url = $input->getString('import_url'); // Did you give us a URL? if (!$url) { $app->enqueueMessage(JText::_('COM_COMPONENTBUILDER_IMPORT_MSG_ENTER_A_URL'), 'warning'); return false; } // Download the package at the URL given $p_file = JInstallerHelper::downloadPackage($url); // Was the package downloaded? if (!$p_file) { $app->enqueueMessage(JText::_('COM_COMPONENTBUILDER_IMPORT_MSG_INVALID_URL'), 'warning'); return false; } // check that this is a valid spreadsheet $package = $this->check($p_file); return $package; } /** * Check a file and verifies it as a spreadsheet file * Supports .csv .xlsx .xls and .ods * * @param string $p_filename The uploaded package filename or import directory * * @return array of elements * */ protected function check($archivename) { $app = JFactory::getApplication(); // Clean the name $archivename = JPath::clean($archivename); // check the extention if(!$this->checkExtension($archivename)) { // Cleanup the import files $this->remove($archivename); $app->enqueueMessage(JText::_('COM_COMPONENTBUILDER_IMPORT_MSG_DOES_NOT_HAVE_A_VALID_FILE_TYPE'), 'warning'); return false; } $config = JFactory::getConfig(); // set Package Name $check['packagename'] = $archivename; // set directory $check['dir'] = $config->get('tmp_path'). '/' .$archivename; // set type $check['type'] = $this->getType; return $check; } /** * Check the extension * * @param string $file Name of the uploaded file * * @return boolean True on success * */ protected function checkExtension($file) { // check the extension switch(strtolower(pathinfo($file, PATHINFO_EXTENSION))) { case 'zip': if ($this->dataType === 'smart_package') { return true; } break; } return false; } /** * Clean up temporary uploaded spreadsheet * * @param string $package Name of the uploaded spreadsheet file * * @return boolean True on success * */ protected function remove($package) { jimport('joomla.filesystem.file'); $config = JFactory::getConfig(); $package = $config->get('tmp_path'). '/' .$package; // Is the package file a valid file? if (is_file($package)) { File::delete($package); } elseif (is_file(JPath::clean($package))) { // It might also be just a base filename File::delete(JPath::clean($package)); } } /** * Set the data from the spreadsheet to the database * * @param string $package Paths to the uploaded package file * * @return boolean false on failure * **/ protected function setData($package) { $jinput = JFactory::getApplication()->input; // set the data based on the type of import being done if ('continue-ext' === $this->getType) { // set the data if(isset($package['dir'])) { // set auto loader ComponentbuilderHelper::autoLoader('smart'); // extract the package if (JFile::exists($package['dir'])) { // set the directory name $this->dir = JFile::stripExt($package['dir']); // check for database file $dbFile = $this->dir . '/db.vdm'; if (!JFile::exists($dbFile)) { // get the zip adapter $zip = JArchive::getAdapter('zip'); // unzip the package $zip->extract($package['dir'], $this->dir); } // check again if (JFile::exists($dbFile)) { // load the data if ($data = @file_get_contents($dbFile)) { // prep the data if ($this->extractData($data)) { if (isset($this->data['joomla_component']) && JCBArrayHelper::check($this->data['joomla_component'])) { // save the smart data if ($this->saveSmartComponents()) { ComponentbuilderHelper::removeFolder($this->dir); return true; } } } } } ComponentbuilderHelper::removeFolder($this->dir); } } } return false; } /** * Extract the data from the string * * @param string $data The data string * * @return object false on failure * **/ protected function extractData($data) { // remove all line breaks if (($data = $this->decrypt($data, $this->sleutle)) !== false) { // final check if we have success $data = @unserialize($data); if (JCBArrayHelper::check($data)) { // set the data global $this->data = $data; return true; } $this->app->enqueueMessage(Text::_('COM_COMPONENTBUILDER_HTWODATA_IS_CORRUPTHTWOTHIS_COULD_BE_DUE_TO_BKEY_ERRORB_OR_BROKEN_PACKAGE'), 'error'); return false; } $this->app->enqueueMessage(Text::_('COM_COMPONENTBUILDER_HTWODATA_IS_CORRUPTHTWOTHIS_COULD_BE_DUE_TO_BROKEN_PACKAGE'), 'error'); return false; } /** * Save the smart components * * @return boolean false on failure * **/ protected function saveSmartComponents() { // get user object $this->user = JFactory::getUser(); // set some defaults $this->today = JFactory::getDate()->toSql(); // if we can't merge add postfix to name if (!$this->canmerge) { // set some postfix $this->postfix = ' ('.StringHelper::random(2).')'; } // the array of tables to store $tables = array( 'power', 'validation_rule', 'fieldtype', 'field', 'admin_view', 'snippet', 'dynamic_get', 'custom_admin_view', 'site_view', 'template', 'layout', 'joomla_component', 'language', 'custom_code', 'placeholder', 'class_extends', 'joomla_module', 'joomla_module_files_folders_urls', 'joomla_module_updates', 'joomla_plugin_group', 'class_property', 'class_method', 'joomla_plugin', 'joomla_plugin_files_folders_urls', 'joomla_plugin_updates', 'admin_fields', 'admin_fields_conditions', 'admin_fields_relations', 'admin_custom_tabs', 'component_admin_views', 'component_site_views', 'component_custom_admin_views', 'component_updates', 'component_mysql_tweaks', 'component_custom_admin_menus', 'component_config', 'component_dashboard', 'component_files_folders', 'component_placeholders', 'component_modules', 'component_plugins', 'language_translation' ); // get prefix $prefix = $this->_db->getPrefix(); // get local tables $localTables = $this->_db->getTableList(); // smart table loop foreach ($tables as $table) { // only continue the import if the table is available locally if (in_array($prefix . 'componentbuilder_' . $table, $localTables)) { // save the table to database if (!$this->saveSmartItems($table)) { return false; } } else { // check if it is the power table if ($table === 'power') { $this->app->enqueueMessage(Text::sprintf('COM_COMPONENTBUILDER_TABLE_BSB_NOT_FOUND_IN_THE_LOCAL_DATABASE_SO_ITS_VALUES_COULD_NOT_BE_IMPORTED_THE_WHOLE_POWERS_FEATURE_IS_ONLY_AVAILABLE_TO_A_HREFSPRO_MEMBERSA_AT_THIS_TIME', '#__componentbuilder_' . $table, '"https://vdm.bz/get-jcb-pro-membership" target="_blank" title="Join PRO Membership today!"'), 'warning'); } else { $this->app->enqueueMessage(Text::sprintf('COM_COMPONENTBUILDER_TABLE_BSB_NOT_FOUND_IN_THE_LOCAL_DATABASE_SO_ITS_VALUES_COULD_NOT_BE_IMPORTED_PLEASE_UPDATE_YOUR_JCB_INSTALL_AND_TRY_AGAIN', '#__componentbuilder_' . $table), 'warning'); } } } // do a after all run on all items that need it $this->updateAfterAll(); // finally move the old datasets $this->moveDivergedData(); // lets move all the files to its correct location if (!$this->moveSmartStuff()) { return false; } return true; } /** * Save the smart items * * @param string $table The table * * @return boolean false on failure * **/ public function saveSmartItems($table) { if (isset($this->data[$table]) && JCBArrayHelper::check($this->data[$table])) { // add pre import event $this->preImportEvent($table); // get global action permissions $canDo = ComponentbuilderHelper::getActions($table); $canEdit = $canDo->get('core.edit'); $canState = $canDo->get('core.edit.state'); $canCreate = $canDo->get('core.create'); // check if we should allow merge of local values $canmerge = $this->allowMerge($table); // set id keeper if (!isset($this->newID[$table])) { $this->newID[$table] = array(); } foreach ($this->data[$table] as $item) { $oldID = (int) $item->id; // first check if exist if ($canmerge && ($local = $this->getLocalItem($item, $table, 1)) !== false) { // display more import info if ($this->moreInfo) { $this->app->enqueueMessage(Text::sprintf('COM_COMPONENTBUILDER_BSB_WAS_FOUND', $table.' id:'.$oldID . '->' . $local->id), 'success'); } $dbDate = strtotime($item->modified); $localDate = strtotime($local->modified); // okay we have it local (check if the version is newer) if ($this->forceUpdate == 1 || $dbDate > $localDate) { // add some local values to item to combine if ($table === 'language_translation') { $item->localComponents = $local->components; $item->localModules = $local->modules; $item->localPlugins = $local->plugins; $item->localTranslation = $local->translation; } // make sure we have the correct ID set $item->id = $local->id; // yes it is newer, lets update (or is being forced) if ($canEdit && ($id = $this->updateLocalItem($item, $table, $canState)) !== false) { // we had success in $this->newID[$table][$oldID] = (int) $local->id; // display more import info if ($this->moreInfo) { $this->app->enqueueMessage(Text::sprintf('COM_COMPONENTBUILDER_BSB_HAS_BEEN_UPDATED', $table.' id:'.$oldID . '->' . $id), 'success'); } } else { $notice = '!'; if (!$canEdit) { $notice = Text::sprintf("COM_COMPONENTBUILDER__SINCE_YOU_DONT_HAVE_PERMISSION_TO_EDIT_S", $table); } $this->app->enqueueMessage(Text::sprintf('COM_COMPONENTBUILDER_BSB_COULD_NOT_BE_IMPORTEDS', $table.' id:'.$oldID, $notice), 'error'); } } // insure to load the local ID to link imported values with it if (!isset($this->newID[$table][$oldID])) { $this->newID[$table][$oldID] = (int) $local->id; } } elseif ($canCreate && ($id = $this->addLocalItem($item, $table)) !== false) { // not found in local db so add $this->newID[$table][$oldID] = (int) $id; // display more import info if ($this->moreInfo) { $this->app->enqueueMessage(Text::sprintf('COM_COMPONENTBUILDER_BSB_HAS_BEEN_IMPORTED', $table.' id:'.$oldID . '->' . $id), 'success'); } } else { $notice = '!'; if (!$canCreate) { $notice = Text::sprintf("COM_COMPONENTBUILDER__SINCE_YOU_DONT_HAVE_PERMISSION_TO_CREATE_S", $table); } $this->app->enqueueMessage(Text::sprintf('COM_COMPONENTBUILDER_BSB_COULD_NOT_BE_IMPORTEDS', $table.' id:'.$oldID, $notice), 'error'); } } } return true; } /** * Check if this table needs some house cleaning before we import the data * * @return void * **/ protected function preImportEvent($table) { // if this is custom code table // remove all custom code linked to these components // since some code may have been removed and changed // best unpublish all and let the import publish those still active if ('custom_code' === $table && isset($this->newID['joomla_component']) && JCBArrayHelper::check($this->newID['joomla_component'])) { $query = $this->_db->getQuery(true); // Field to update. $fields = array( $this->_db->quoteName('published') . ' = 0' ); // Conditions for which records should be updated. $conditions = array( $this->_db->quoteName('component') . ' IN (' . implode(', ', array_values($this->newID['joomla_component'])) . ')' ); $query->update($this->_db->quoteName('#__componentbuilder_custom_code'))->set($fields)->where($conditions); $this->_db->setQuery($query); $this->_db->execute(); } } /** * Check if we should allow merge for this table * * @return boolean * **/ protected function allowMerge($table) { if ($this->canmerge == 1 || in_array($table, $this->mustMerge)) { return true; } return false; } /** * Move the smart content (files & folders) into place * * @return boolean false on failure * **/ protected function moveSmartStuff() { // make sure to first unlock files $this->unLockFiles(); // set params $params = JComponentHelper::getParams('com_componentbuilder'); // set custom folder path $customPath = str_replace('//', '/', $params->get('custom_folder_path', JPATH_COMPONENT_ADMINISTRATOR.'/custom')); $imagesPath = str_replace('//', '/', JPATH_SITE . '/images'); $success = true; // check if we have custom files $customDir = str_replace('//', '/', $this->dir . '/custom'); if (JFolder::exists($customDir)) { // great we have some custom stuff lets move it if (!JFolder::copy($customDir, $customPath,'',true)) { $this->app->enqueueMessage(JText::_('COM_COMPONENTBUILDER_BCUSTOM_FILESB_NOT_MOVED_TO_CORRECT_LOCATION'), 'error'); $success = false; } // display more import info elseif ($this->moreInfo) { $this->app->enqueueMessage(Text::sprintf('COM_COMPONENTBUILDER_SOME_BCUSTOM_FILESB_WERE_MOVED_TO_BSB', $customPath), 'success'); } } // check if we have images $imageDir = str_replace('//', '/', $this->dir . '/images'); if (JFolder::exists($imageDir)) { // great we have some images lets move them if (!JFolder::copy($imageDir, $imagesPath,'',true)) { $this->app->enqueueMessage(JText::_('COM_COMPONENTBUILDER_BIMAGESB_NOT_MOVED_TO_CORRECT_LOCATION'), 'error'); $success = false; } // display more import info elseif ($this->moreInfo) { $this->app->enqueueMessage(Text::sprintf('COM_COMPONENTBUILDER_SOME_BIMAGESB_WERE_MOVED_TO_BSB', $imagesPath), 'success'); } } // now move the dynamic files if found $dynamicDir = str_replace('//', '/', $this->dir . '/dynamic'); if (JFolder::exists($dynamicDir)) { // get a list of folders $folders = JFolder::folders($dynamicDir); // check if we have files if(JCBArrayHelper::check($folders)) { foreach ($folders as $folder) { $destination = $this->setDynamicPath($folder); $fullPath = str_replace('//', '/', $dynamicDir . '/' . $folder); if (!JFolder::exists($fullPath) || !JFolder::copy($fullPath, $destination,'',true)) { $this->app->enqueueMessage(Text::sprintf('COM_COMPONENTBUILDER_FOLDER_BSB_WAS_NOT_MOVED_TO_BSB', $folder, $destination), 'error'); $success = false; } // display more import info elseif ($this->moreInfo) { $this->app->enqueueMessage(Text::sprintf('COM_COMPONENTBUILDER_FOLDER_BSB_WAS_MOVED_TO_BSB', $folder, $destination), 'success'); } } } // get a list of files $files = JFolder::files($dynamicDir); // check if we have files if(JCBArrayHelper::check($files)) { foreach ($files as $file) { $destination = $this->setDynamicPath($file); $fullPath = str_replace('//', '/', $dynamicDir . '/' . $file); if (!JFile::exists($fullPath) || !JFile::copy($fullPath, $destination)) { $this->app->enqueueMessage(Text::sprintf('COM_COMPONENTBUILDER_FILE_BSB_WAS_NOT_MOVED_TO_BSB', $file, $destination), 'error'); $success = false; } // display more import info elseif ($this->moreInfo) { $this->app->enqueueMessage(Text::sprintf('COM_COMPONENTBUILDER_FILE_BSB_WAS_MOVED_TO_BSB', $file, $destination), 'success'); } } } } return $success; } /** * Method to unlock all files in all folders of this package * * @return void */ protected function unLockFiles() { // lock the data if set if(StringHelper::check($this->sleutle) && strlen($this->sleutle) == 32) { // we must first store the current working directory $joomla = getcwd(); // to avoid that it decrypt the db and info file again we must move per/folder $folders = array('images', 'custom', 'dynamic'); // loop the folders foreach ($folders as $folder) { $subPath = str_replace('//', '/', $this->dir . '/' . $folder); // go to the package sub folder if found if (JFolder::exists($subPath)) { $this->unLockFile($subPath); } } // change back to working dir chdir($joomla); } } /** * The unlocking files * * @return void */ protected function unLockFile(&$tmpPath) { // we are changing the working directory to the tmp path (important) chdir($tmpPath); // get a list of files in the current directory tree (all) $files = JFolder::files('.', '.', true, true); // read in the file content foreach ($files as $file) { // open the file content if (($data = $this->decrypt(file_get_contents($file), $this->sleutle)) !== false) { // write the decrypted data back to file if (!FileHelper::write($file, $data)) { // in case file could not be unlocked $this->app->enqueueMessage(Text::sprintf('COM_COMPONENTBUILDER_FILE_BSB_COULD_NOT_BE_UNLOCKED', $file), 'error'); } // display more import info elseif ($this->moreInfo) { $this->app->enqueueMessage(Text::sprintf('COM_COMPONENTBUILDER_FILE_BSB_WAS_SUCCESSFULLY_UNLOCKED', $file), 'success'); } } else { // in case file could not be unlocked $this->app->enqueueMessage(Text::sprintf('COM_COMPONENTBUILDER_FILE_BSB_COULD_NOT_BE_UNLOCKED', $file), 'error'); } } } /** * decrypt data * * @param string $data The data string * @param string $password The key to unlock * @param bool $force Should we force phpseclib decryption * * @return mixed the data, or false * * @since 3.0.11 **/ protected function decrypt(string $data, string $password, bool $force = false) { // remove all line breaks $data = $this->removeLineBreaks($data); // make sure we have base64 if ($this->isBase64Encoded($data)) { // open the data if($this->isValidPassword($password)) { // try phpseclib version 3 if(($this->isPhpseclib3Enabled() || $force) && ($data_ = $this->decryptWithPhpseclib3($data, $password)) !== false) { return $data_; } // try phpseclib version 2 if(($this->isPhpseclib2Enabled() || $force) && ($data_ = $this->decryptWithPhpseclib2($data, $password)) !== false) { return $data_; } // try FOF the outdated method if (($data_ = $this->decryptWithFof($data, $password)) !== false) { return $data_; } $this->app->enqueueMessage(Text::_('COM_COMPONENTBUILDER_HTWOWE_COULD_NOT_OPEN_THE_ENCRYPT_DATAHTWO_THIS_COULD_BE_DUE_TO_THE_FOFENCRYPTION_THAT_IS_NO_LONGER_SUPPORTED_IN_JOOMLABR_PLEASE_EXPORT_YOUR_PACKAGES_WITH_JCB_VTHREEONEONENINE_OR_HIGHER_TO_BE_ABLE_TO_IMPORT_IT_INTO_THIS_VERSION_OF_JCB'), 'error'); } else { return base64_decode($data); } } return false; } /** * Removes all line breaks from a string * * @param string $data * * @return string */ private function removeLineBreaks(string $data): string { return str_replace("\n", '', $data); } /** * Check if the data is base64 encoded * * @param string $data * * @return bool */ private function isBase64Encoded(string $data): bool { return $data === base64_encode(base64_decode($data, true)); } /** * Check if a password is set and has a length of 32 characters * * @param string $password * * @return bool */ private function isValidPassword(string $password): bool { return StringHelper::check($password) && strlen($password) == 32; } /** * Check if we can use phpseclib library version 2 * * @return bool */ private function isPhpseclib2Enabled(): bool { return (bool) isset($this->packageInfo['phpseclib']) && $this->packageInfo['phpseclib']; } /** * Decrypts data using the phpseclib library version 2 * * @param string $data * @param string $password * * @return string|bool */ private function decryptWithPhpseclib2(string $data, string $password) { $data_ = PackageFactory::_('Crypt')->decrypt($data, 'legacy', $password); return strcmp($data, $data_) !== 0 ? $data_ : false; } /** * Check if we can use phpseclib library version 3 * * @return bool */ private function isPhpseclib3Enabled(): bool { return (bool) isset($this->packageInfo['phpseclib3']) && $this->packageInfo['phpseclib3']; } /** * Decrypts data using the phpseclib library version 3 * * @param string $data * @param string $password * * @return string|bool */ private function decryptWithPhpseclib3(string $data, string $password) { $data_ = PackageFactory::_('Crypt')->decrypt($data, 'aes', $password); return strcmp($data, $data_) !== 0 ? $data_ : false; } /** * Decrypts data using the FOF library (outdated method) * * @param string $data * @param string $password * * @return string|bool */ private function decryptWithFof(string $data, string $password) { $data_ = PackageFactory::_('Crypt')->decrypt($data, 'fof', $password); return strcmp($data, $data_) !== 0 ? $data_ : false; } /** * Update some items after all * * @return void * **/ public function updateAfterAll() { // update the fields if (isset($this->updateAfter['field']) && JCBArrayHelper::check($this->updateAfter['field'])) { // update repeatable foreach ($this->updateAfter['field'] as $field => $action) { // if added we must get the ID now, yet if update the id is already set if ('add' === $action && isset($this->newID['field'][$field])) { $field = $this->newID['field'][$field]; } // get the field from db if ($xml = GetHelper::var('field', $field, 'id', 'xml')) { if (JsonHelper::check($xml)) { $xml = json_decode($xml); $fields = GetHelper::between($xml, 'fields="', '"'); $fieldsSets = array(); if (strpos($fields, ',') !== false) { // multiple fields $fieldsSets = array_map('trim', (array) explode(',', $fields)); } elseif (is_numeric($fields)) { // single field $fieldsSets[] = (int) $fields; } // update the fields if (JCBArrayHelper::check($fieldsSets)) { $bucket = array(); foreach ($fieldsSets as $id) { if (isset($this->newID['field'][(int) $id])) { $bucket[] = $this->newID['field'][(int) $id]; } else { $this->app->enqueueMessage(Text::sprintf('COM_COMPONENTBUILDER_BMULTIPLE_FIELD_REPEATABLE_MODEB_IDS_MISMATCH_IN_BFIELDSB_AND_WAS_EMREMOVEDEM_FROM_THE_FIELD', $id, $field), 'warning'); } } if (JCBArrayHelper::check($bucket)) { $string = implode(',', $bucket); $xml = json_encode(str_replace('fields="' . $fields . '"', 'fields="' . $string . '"', $xml)); } else { $xml = ''; } $object = new stdClass; $object->id = $field; $object->xml = $xml; // update the field $this->_db->updateObject('#__componentbuilder_field', $object, 'id'); } } } } } // do a after all run on admin views that need it if (isset($this->updateAfter['adminview']) && JCBArrayHelper::check($this->updateAfter['adminview'])) { // update the addlinked_views foreach ($this->updateAfter['adminview'] as $adminview => $action) { // if added we must get the ID now, yet if update the id is already set if ('add' === $action && isset($this->newID['admin_view'][(int) $adminview])) { $adminview = $this->newID['admin_view'][(int) $adminview]; } // get the field from db if ($addlinked_views = GetHelper::var('admin_view', $adminview, 'id', 'addlinked_views')) { if (JsonHelper::check($addlinked_views)) { $addlinked_views = json_decode($addlinked_views, true); // convert Repeatable Fields if (JCBArrayHelper::check($addlinked_views) && isset($addlinked_views['adminview'])) { $addlinked_views = ComponentbuilderHelper::convertRepeatable($addlinked_views, 'addlinked_views'); } // update the view IDs if (JCBArrayHelper::check($addlinked_views)) { // only update the view IDs $addlinked_views = $this->updateIDs($addlinked_views, 'admin_view', array('adminview' => 'admin_view')); } // update the fields $object = new stdClass; $object->id = $adminview; $object->addlinked_views = json_encode($addlinked_views, JSON_FORCE_OBJECT); // update the admin view $this->_db->updateObject('#__componentbuilder_admin_view', $object, 'id'); } } } } // update the joomla_component dashboard if (isset($this->updateAfter['joomla_component']) && JCBArrayHelper::check($this->updateAfter['joomla_component'])) { // update dashboard of the components foreach ($this->updateAfter['joomla_component'] as $component => $action) { // if added we must get the ID now, yet if update the id is already set if ('add' === $action && isset($this->newID['joomla_component'][(int) $component])) { $component = $this->newID['joomla_component'][(int) $component]; } // get the dashboard from db if ($dashboard = GetHelper::var('joomla_component', $component, 'id', 'dashboard')) { if (StringHelper::check($dashboard)) { // get id $id = (int) preg_replace("/[^0-9]/", "", $dashboard); // update the value $update = false; // admin_view if ((strpos($dashboard, 'A') !== false || strpos($dashboard, 'a') !== false) && isset($this->newID['admin_view'][$id])) { // set the new value $dashboard = 'A_' . $this->newID['admin_view'][$id]; // update the value $update = true; } // custom_admin_view elseif ((strpos($dashboard, 'C') !== false || strpos($dashboard, 'c') !== false) && isset($this->newID['custom_admin_view'][$id])) { // set the new value $dashboard = 'C_' . $this->newID['custom_admin_view'][$id]; // update the value $update = true; } // did we get a new value if ($update) { // now update the joomla_component dashboard value $object = new stdClass; $object->id = (int) $component; $object->dashboard = $dashboard; // update the admin view $this->_db->updateObject('#__componentbuilder_joomla_component', $object, 'id'); } } } } } // update the admin_fields_relations if (isset($this->updateAfter['relations']) && JCBArrayHelper::check($this->updateAfter['relations'])) { // update repeatable foreach ($this->updateAfter['relations'] as $relation => $action) { // check if we must update this relation $update = false; // if added we must get the ID now, yet if update the id is already set if ('add' === $action && isset($this->newID['admin_fields_relations'][$relation])) { $relation = $this->newID['admin_fields_relations'][$relation]; } // get the set relation from db if ($addrelations = GetHelper::var('admin_fields_relations', $relation, 'id', 'addrelations')) { if (JsonHelper::check($addrelations)) { $addrelations = json_decode($addrelations, true); if (JCBArrayHelper::check($addrelations)) { foreach ($addrelations as $nr => &$value) { // reset the buckets $bucket = array(); // get fields $found = GetHelper::allBetween($value['set'], '[field=', ']'); // if found if (JCBArrayHelper::check($found)) { $bucket[] = $found; } // get fields $found = GetHelper::allBetween($value['set'], '$item->{', '}'); // if found if (JCBArrayHelper::check($found)) { $bucket[] = $found; } // check if we have values if (JCBArrayHelper::check($bucket)) { $fields = JCBArrayHelper::merge($bucket); // reset the buckets $bucket = array(); if (JCBArrayHelper::check($fields)) { foreach ($fields as $field) { if (isset($this->newID['field'][(int) $field])) { $bucket['[field=' . (int) $field . ']'] = '[field=' . (int) $this->newID['field'][(int) $field] . ']'; $bucket['$item->{' . (int) $field . '}'] = '$item->{' . (int) $this->newID['field'][(int) $field] . '}'; } else { $this->app->enqueueMessage(Text::sprintf('COM_COMPONENTBUILDER_BADMIN_FIELDS_RELATIONSB_IDS_MISMATCH_IN_BFIELDSB_AND_WAS_NOT_UPDATED_IN_THE_CUSTOM_CODE', $relation, $field), 'warning'); } } // check if we have a bucket of values to update if (JCBArrayHelper::check($bucket)) { $value['set'] = str_replace(array_keys($bucket), array_values($bucket), $value['set']); $update = true; } } } } // update only if needed if ($update) { $object = new stdClass; $object->id = $relation; $object->addrelations = json_encode($addrelations, JSON_FORCE_OBJECT); // update the field $this->_db->updateObject('#__componentbuilder_admin_fields_relations', $object, 'id'); } } } } } } // update the verious power linked to these powers if (isset($this->updateAfter['power']) && JCBArrayHelper::check($this->updateAfter['power'])) { // update repeatable foreach ($this->updateAfter['power'] as $power => $action) { // check if we must update this power $update = false; // if added we must get the ID now, yet if update the id is already set if ('add' === $action && isset($this->newID['power'][$power])) { $power = $this->newID['power'][$power]; } // get the power $query = $this->_db->getQuery(true); $query->select(array('a.id', 'a.property_selection', 'a.method_selection')); $query->from($this->_db->quoteName('#__componentbuilder_power', 'a')); $query->where($this->_db->quoteName('a.id') . ' = '. (int) $power); // see if we get an item $this->_db->setQuery($query); $this->_db->execute(); if ($this->_db->getNumRows()) { $item = $this->_db->loadObject(); // subform fields to target $updaterT = array( // subformfield => array( field => type_value ) 'property_selection' => array('property' => 'class_property'), 'method_selection' => array('method' => 'class_method') ); // update the subform ids $this->updateSubformsIDs($item, 'power', $updaterT); // update the power $this->_db->updateObject('#__componentbuilder_power', $item, 'id'); } } } } /** * Moving of diverged data * * @return void * **/ public function moveDivergedData() { // check if there is data to move if (JCBArrayHelper::check($this->divergedDataMover)) { foreach($this->divergedDataMover as $table => $values) { foreach($values as $value) { // first check if exist (only add if it does not) if (!$this->getLocalItem($value, $table, 1, 1, true)) { // add the diverged data $this->addLocalItem($value, $table, true); } } } } } /** * Update Many Params IDs * * @param array $values The values to update the IDs in * @param string $table The table these values belong to * @param array $targets The target to update and its type * * @return void */ private function updateParamIDs(&$item, $table, $targets) { // update the params fields if exist if (isset($item->params) && JsonHelper::check($item->params)) { $paramsArray = json_decode($item->params, true); if (JCBArrayHelper::check($paramsArray)) { foreach ($targets as $field => $targetArray) { if (isset($paramsArray[$field])) { $target_array = array(); foreach ($targetArray as $key => $field_table) { // sub form in params if (strpos($key, '.') !==false) { $key_array = explode('.', $key); // set the target $target_array[$key_array[0]][$key_array[1]] = $field_table; } elseif (isset($paramsArray[$field][$key])) { // load it back $paramsArray[$field] = $this->setNewID($paramsArray[$field], $key, $field_table, $table); } } // check if we had some subforms if (JCBArrayHelper::check($target_array)) { $paramsArray[$field] = $this->updateIDs($paramsArray[$field], $table, $target_array); } } } } // add the params back $item->params = json_encode($paramsArray, JSON_FORCE_OBJECT); } } /** * Update Many Subform IDs * * @param array $values The values to update the IDs in * @param string $table The table these values belong to * @param array $targets The target to update and its type * * @return void */ private function updateSubformsIDs(&$item, $table, $targets) { // update the repeatable fields foreach ($targets as $field => $targetArray) { if (isset($item->{$field}) && JsonHelper::check($item->{$field})) { $updateArray = json_decode($item->{$field}, true); if (JCBArrayHelper::check($updateArray)) { // load it back $item->{$field} = json_encode($this->updateIDs($updateArray, $table, $targetArray), JSON_FORCE_OBJECT); } } } } /** * Update IDs * * @param array $values The values to update the IDs in * @param string $table The table these values belong to * @param array $targets The target to update and its type * * @return void */ private function updateIDs($values, $table, $targets) { $isJson = false; if (JsonHelper::check($values)) { $values = json_decode($values, true); $isJson = true; } // now update the fields if (JCBArrayHelper::check($values)) { foreach ($values as $nr => &$value) { foreach ($targets as $target => $target_type) { if (isset($value[$target])) { $value = $this->setNewID($value, $target, $target_type, $table); } } } } if ($isJson) { return json_encode($values, JSON_FORCE_OBJECT); } return $values; } /** * Set the new ID * * @param array $item The values to update the IDs in * @param string $target The target field * @param string $type The table of that field * @param string $table The table these values belong to * * @return boolean True on success * */ private function setNewID($item, $target, $type, $table) { $isJson = false; if (JsonHelper::check($item)) { $item = json_decode($item, true); $isJson = true; } if (JCBArrayHelper::check($item) && isset($item[$target])) { // set item ID $itemId = (isset($item['id'])) ? $item['id'] : 'id_unknow'; // check if it is json $isJsonTarget = false; if (JsonHelper::check($item[$target])) { $item[$target] = json_decode($item[$target], true); $isJsonTarget = true; } // update the target if (StringHelper::check($item[$target]) || is_numeric($item[$target])) { if ($item[$target] <= 0) { if ($item[$target] == 0) { $item[$target] = ''; } } elseif (isset($this->newID[$type][(int) $item[$target]])) { $item[$target] = $this->newID[$type][(int) $item[$target]]; } else { $this->enqueueIdMismatchMessage($item[$target], $itemId, $target, $type, $table); $item[$target] = ''; } } elseif (JCBArrayHelper::check($item[$target])) { // the bucket to load the items back $bucket = array(); foreach ($item[$target] as $nr => $id) { if ($id <= 0) { if ($id == 0) { continue; } // we add negative numbers back $bucket[] = $id; } elseif ((StringHelper::check($id) || is_numeric($id)) && isset($this->newID[$type][(int) $id])) { $bucket[] = $this->newID[$type][(int) $id]; } else { $this->enqueueIdMismatchMessage($id, $itemId, $target, $type, $table); } } // set ids back if (JCBArrayHelper::check($bucket)) { $item[$target] = $bucket; } } // convert back to json if ($isJsonTarget) { $item[$target] = json_encode($item[$target], JSON_FORCE_OBJECT); } } elseif (ObjectHelper::check($item) && isset($item->{$target})) { // set item ID $itemId = (isset($item->id)) ? $item->id : 'id_unknow'; // check if it is json $isJsonTarget = false; if (JsonHelper::check($item->{$target})) { $item->{$target} = json_decode($item->{$target}, true); $isJsonTarget = true; } // update the target if (StringHelper::check($item->{$target}) || is_numeric($item->{$target})) { if ($item->{$target} <= 0) { if ($item->{$target} == 0) { $item->{$target} = ''; } } elseif (isset($this->newID[$type][(int) $item->{$target}])) { $item->{$target} = $this->newID[$type][(int) $item->{$target}]; } else { $this->enqueueIdMismatchMessage($item->{$target}, $itemId, $target, $type, $table); $item->{$target} = ''; } } elseif (JCBArrayHelper::check($item->{$target})) { // the bucket to load the items back $bucket = array(); foreach ($item->{$target} as $id) { if ($id <= 0) { if ($id == 0) { continue; } // we add negative numbers back $bucket[] = $id; } elseif ((StringHelper::check($id) || is_numeric($id)) && isset($this->newID[$type][(int) $id])) { $bucket[] = $this->newID[$type][(int) $id]; } else { $this->enqueueIdMismatchMessage($id, $itemId, $target, $type, $table); $bucket[] = ''; } } // set ids back if (JCBArrayHelper::check($bucket)) { $item->{$target} = $bucket; } } // convert back to json if ($isJsonTarget) { $item->{$target} = json_encode($item->{$target}, JSON_FORCE_OBJECT); } } // return as json if received as json if ($isJson) { return json_encode($item); } return $item; } /** * Set the new ID * * @param int $id The field ID * @param int $itemId The item ID * @param string $target The target field * @param string $type The table of that field * @param string $table The table these values belong to * * @return void * */ private function enqueueIdMismatchMessage($id, $itemId, $target, $type, $table) { $this->app->enqueueMessage(Text::sprintf('COM_COMPONENTBUILDER_BSBS_IN_BSB_HAS_ID_MISMATCH_SO_THE_BSB_WAS_REMOVED', StringHelper::safe($type, 'Ww'), StringHelper::safe($target, 'Ww') , StringHelper::safe($table, 'w').':'.$itemId, $type . ':' . $id), 'warning'); } /** * Prep the item * * @param object $item The item to prep * @param string $type The type of values * @param string $action The action (add/update) * @param bool $diverged The diverged data switch * * @return mixed false on failure * object on success * **/ private function prepItem($item, $type, $action, $diverged = false) { // remove access if (isset($item->access)) { unset($item->access); } // remove metadata if (isset($item->metadata)) { unset($item->metadata); } // remove metadesc if (isset($item->metadesc)) { unset($item->metadesc); } // remove metakey if (isset($item->metakey)) { unset($item->metakey); } // remove not_required if (isset($item->not_required)) { unset($item->not_required); } // actions to effect all if (isset($item->asset_id)) { unset($item->asset_id); } if (isset($item->checked_out)) { $item->checked_out = 0; } if (isset($item->checked_out_time)) { $item->checked_out_time = '0000-00-00 00:00:00'; } // do the id fix for the new ids switch($type) { case 'fieldtype': // repeatable fields to update $updaterR = array( // repeatablefield => checker 'properties' => 'name' ); // update the repeatable fields $item = ComponentbuilderHelper::convertRepeatableFields($item, $updaterR); break; case 'field': // update the fieldtype if (isset($item->fieldtype) && is_numeric($item->fieldtype) && $item->fieldtype > 0 && isset($this->newID['fieldtype'][(int) $item->fieldtype])) { $item->fieldtype = $this->newID['fieldtype'][(int) $item->fieldtype]; // update multi field values if ($this->checkMultiFields($item->fieldtype)) { $this->updateAfter['field'][(int) $item->id] = $action; // multi field } } elseif (!is_numeric($item->fieldtype) || $item->fieldtype == 0) { $this->app->enqueueMessage(Text::sprintf('COM_COMPONENTBUILDER_BFIELD_TYPEB_NOT_SET_FOR_BSB', StringHelper::safe($type, 'w').':'.$item->id), 'warning'); unset($item->fieldtype); } else { $this->app->enqueueMessage(Text::sprintf('COM_COMPONENTBUILDER_BFIELD_TYPEB_IDS_MISMATCH_IN_BSB', $item->fieldtype, StringHelper::safe($type, 'w').':'.$item->id), 'error'); return false; } // if we can't merge add postfix to name if ($this->postfix) { $item->name = $item->name.$this->postfix; } break; case 'dynamic_get': // update the view_table_main ID if (isset($item->main_source) && $item->main_source == 1) { $item = $this->setNewID($item, 'view_table_main', 'admin_view', $type); } // repeatable fields to update $updaterR = array( // repeatablefield => checker 'join_view_table' => 'view_table', 'join_db_table' => 'db_table', 'order' => 'table_key', 'where' => 'table_key', 'global' => 'name', 'filter' => 'filter_type' ); // update the repeatable fields $item = ComponentbuilderHelper::convertRepeatableFields($item, $updaterR); // subform fields to target $updaterT = array( // subformfield => field => type_value 'join_view_table' => array('view_table' => 'admin_view') ); // update the subform ids $this->updateSubformsIDs($item, 'dynamic_get', $updaterT); // if we can't merge add postfix to name if ($this->postfix) { $item->name = $item->name.$this->postfix; } break; case 'layout': case 'template': // update the dynamic_get $item = $this->setNewID($item, 'dynamic_get', 'dynamic_get', $type); // update the snippet $item = $this->setNewID($item, 'snippet', 'snippet', $type); // if we can't merge add postfix to name if ($this->postfix) { $item->name = $item->name.$this->postfix; } break; case 'custom_admin_view': case 'site_view': // update the main_get $item = $this->setNewID($item, 'main_get', 'dynamic_get', $type); // update the dynamic_get $item = $this->setNewID($item, 'dynamic_get', 'dynamic_get', $type); // update the custom_get $item = $this->setNewID($item, 'custom_get', 'dynamic_get', $type); // update the snippet $item = $this->setNewID($item, 'snippet', 'snippet', $type); // repeatable fields to update $updaterR = array( // repeatablefield => checker 'ajax_input' => 'value_name', 'custom_button' => 'name' ); // update the repeatable fields $item = ComponentbuilderHelper::convertRepeatableFields($item, $updaterR); // if we can't merge add postfix to name if ($this->postfix) { $item->system_name = $item->system_name.$this->postfix; } break; case 'admin_view': // set the getters anchors $getter = array('admin_view' => $item->id); // we must clear the demo content (since it was not moved as far as we know) TODO if ($item->add_sql == 1 && $item->source == 1) { // only if it is mapped to a table unset($item->add_sql); unset($item->source); unset($item->addtables); } // update the addfields (old dataset) if (isset($item->addfields) && JsonHelper::check($item->addfields)) { // move the old data $this->setDivergedDataMover($item->addfields, 'admin_fields', 'addfields', $getter); } // remove from this dataset unset($item->addfields); // update the addlinked_views if (isset($item->addlinked_views) && JsonHelper::check($item->addlinked_views)) { $this->updateAfter['adminview'][(int) $item->id] = $action; // addlinked_views } elseif (isset($item->addlinked_views)) { unset($item->addlinked_views); } // update the addconditions (old dataset) if (isset($item->addconditions) && JsonHelper::check($item->addconditions)) { // move the old data $this->setDivergedDataMover($item->addconditions, 'admin_fields_conditions', 'addconditions', $getter); } // remove from this dataset unset($item->addconditions); // repeatable fields to update $updaterR = array( // repeatablefield => checker 'ajax_input' => 'value_name', 'custom_button' => 'name', 'addtables' => 'table', 'addlinked_views' => 'adminview', 'addtabs' => 'name', 'addpermissions' => 'action' ); // update the repeatable fields $item = ComponentbuilderHelper::convertRepeatableFields($item, $updaterR); // param fields to target $updaterP = array( // param_field => field* => field_table 'fieldordering' => array( 'admin_ordering_fields.field' => 'field', 'linked_ordering_fields.field' => 'field' ), 'privacy' => array( 'anonymize_fields.field' => 'field', 'other_user_fields' => 'field' ) ); // update the params ids $this->updateParamIDs($item, $type, $updaterP); // if we can't merge add postfix to name if ($this->postfix) { $item->system_name = $item->system_name.$this->postfix; } break; case 'joomla_component': // update custom dash after if (isset($item->dashboard_type) && 2 == $item->dashboard_type) { // update the custom dash ID $this->updateAfter['joomla_component'][(int) $item->id] = $action; // dashboard } // set the anchors getters $getter = array('joomla_component' => $item->id); // update the addconfig if (isset($item->addconfig) && JsonHelper::check($item->addconfig)) { // move the old data $this->setDivergedDataMover($item->addconfig, 'component_config', 'addconfig', $getter); } // remove from this dataset unset($item->addconfig); // update the addadmin_views if (isset($item->addadmin_views) && JsonHelper::check($item->addadmin_views)) { // move the old data $this->setDivergedDataMover($item->addadmin_views, 'component_admin_views', 'addadmin_views', $getter); } // remove from this dataset unset($item->addadmin_views); // update the addcustom_admin_views if (isset($item->addcustom_admin_views) && JsonHelper::check($item->addcustom_admin_views)) { // move the old data $this->setDivergedDataMover($item->addcustom_admin_views, 'component_custom_admin_views', 'addcustom_admin_views', $getter); } // remove from this dataset unset($item->addcustom_admin_views); // update the addsite_views if (isset($item->addsite_views) && JsonHelper::check($item->addsite_views)) { // move the old data $this->setDivergedDataMover($item->addsite_views, 'component_site_views', 'addsite_views', $getter); } // remove from this dataset unset($item->addsite_views); // update the version_update if (isset($item->version_update) && JsonHelper::check($item->version_update)) { // move the old data $this->setDivergedDataMover($item->version_update, 'component_updates', 'version_update', $getter); } // remove from this dataset unset($item->version_update); // update the sql_tweak if (isset($item->sql_tweak) && JsonHelper::check($item->sql_tweak)) { // move the old data $this->setDivergedDataMover($item->sql_tweak, 'component_mysql_tweaks', 'sql_tweak', $getter); } // remove from this dataset unset($item->sql_tweak); // update the addcustommenus if (isset($item->addcustommenus) && JsonHelper::check($item->addcustommenus)) { // move the old data $this->setDivergedDataMover($item->addcustommenus, 'component_custom_admin_menus', 'addcustommenus', $getter); } // remove from this dataset unset($item->addcustommenus); // update the dashboard_tab if (isset($item->dashboard_tab) && JsonHelper::check($item->dashboard_tab)) { // move the old data $this->setDivergedDataMover($item->dashboard_tab, 'component_dashboard', 'dashboard_tab', $getter); } // remove from this dataset unset($item->dashboard_tab); // update the php_dashboard_methods if (isset($item->php_dashboard_methods)) { // move the old data $this->setDivergedDataMover($item->php_dashboard_methods, 'component_dashboard', 'php_dashboard_methods', $getter); } // remove from this dataset unset($item->php_dashboard_methods); unset($item->add_php_dashboard_methods); // update the addfiles if (isset($item->addfiles) && JsonHelper::check($item->addfiles)) { // move the old data $this->setDivergedDataMover($item->addfiles, 'component_files_folders', 'addfiles', $getter); } // remove from this dataset unset($item->addfiles); // update the addfolders if (isset($item->addfolders) && JsonHelper::check($item->addfolders)) { // move the old data $this->setDivergedDataMover($item->addfolders, 'component_files_folders', 'addfolders', $getter); } // remove from this dataset unset($item->addfolders); // update the add_css if (isset($item->add_css)) { $item->add_css_admin = $item->add_css; } // remove from this dataset unset($item->add_css); // update the css if (isset($item->css) && StringHelper::check($item->css)) { $item->css_admin = $item->css; } // remove from this dataset unset($item->css); // rename sales_server_ftp field if (isset($item->sales_server_ftp)) { $item->sales_server = $item->sales_server_ftp; unset($item->sales_server_ftp); } // rename update_server_ftp field if (isset($item->update_server_ftp)) { $item->update_server = $item->update_server_ftp; unset($item->update_server_ftp); } // rename export_package_link field if (isset($item->export_package_link)) { $item->joomla_source_link = $item->export_package_link; unset($item->export_package_link); } // repeatable fields to update $updaterR = array( // repeatablefield => checker 'addcontributors' => 'name' ); // update the repeatable fields $item = ComponentbuilderHelper::convertRepeatableFields($item, $updaterR); // if we can't merge add postfix to name if ($this->postfix) { $item->system_name = $item->system_name.$this->postfix; } break; case 'component_admin_views': // diverged id already updated if (!$diverged) { // update the joomla_component ID where needed $item = $this->setNewID($item, 'joomla_component', 'joomla_component', $type); } // repeatable fields to update $updaterR = array( // repeatablefield => checker 'addadmin_views' => 'adminview' ); // update the repeatable fields $item = ComponentbuilderHelper::convertRepeatableFields($item, $updaterR); // subform fields to target $updaterT = array( // subformfield => array( field => type_value ) 'addadmin_views' => array('adminview' => 'admin_view') ); // update the subform ids $this->updateSubformsIDs($item, 'component_admin_views', $updaterT); break; case 'component_site_views': // diverged id already updated if (!$diverged) { // update the joomla_component ID where needed $item = $this->setNewID($item, 'joomla_component', 'joomla_component', $type); } // repeatable fields to update $updaterR = array( // repeatablefield => checker 'addsite_views' => 'siteview' ); // update the repeatable fields $item = ComponentbuilderHelper::convertRepeatableFields($item, $updaterR); // subform fields to target $updaterT = array( // subformfield => array( field => type_value ) 'addsite_views' => array('siteview' => 'site_view') ); // update the subform ids $this->updateSubformsIDs($item, 'component_site_views', $updaterT); break; case 'component_custom_admin_views': // diverged id already updated if (!$diverged) { // update the joomla_component ID where needed $item = $this->setNewID($item, 'joomla_component', 'joomla_component', $type); } // repeatable fields to update $updaterR = array( // repeatablefield => checker 'addcustom_admin_views' => 'customadminview' ); // update the repeatable fields $item = ComponentbuilderHelper::convertRepeatableFields($item, $updaterR); // subform fields to target $updaterT = array( // subformfield => array( field => type_value ) 'addcustom_admin_views' => array('customadminview' => 'custom_admin_view', 'adminviews' => 'admin_view', 'before' => 'admin_view') ); // update the subform ids $this->updateSubformsIDs($item, 'component_custom_admin_views', $updaterT); break; case 'component_updates': // diverged id already updated if (!$diverged) { // update the joomla_component ID where needed $item = $this->setNewID($item, 'joomla_component', 'joomla_component', $type); } // repeatable fields to update $updaterR = array( // repeatablefield => checker 'version_update' => 'version' ); // update the repeatable fields $item = ComponentbuilderHelper::convertRepeatableFields($item, $updaterR); break; case 'component_mysql_tweaks': // diverged id already updated if (!$diverged) { // update the joomla_component ID where needed $item = $this->setNewID($item, 'joomla_component', 'joomla_component', $type); } // repeatable fields to update $updaterR = array( // repeatablefield => checker 'sql_tweak' => 'adminview' ); // update the repeatable fields $item = ComponentbuilderHelper::convertRepeatableFields($item, $updaterR); // subform fields to target $updaterT = array( // subformfield => array( field => type_value ) 'sql_tweak' => array('adminview' => 'admin_view') ); // update the subform ids $this->updateSubformsIDs($item, 'component_mysql_tweaks', $updaterT); break; case 'component_custom_admin_menus': // diverged id already updated if (!$diverged) { // update the joomla_component ID where needed $item = $this->setNewID($item, 'joomla_component', 'joomla_component', $type); } // repeatable fields to update $updaterR = array( // repeatablefield => checker 'addcustommenus' => 'name' ); // update the repeatable fields $item = ComponentbuilderHelper::convertRepeatableFields($item, $updaterR); // subform fields to target $updaterT = array( // subformfield => array( field => type_value ) 'addcustommenus' => array('before' => 'admin_view') ); // update the subform ids $this->updateSubformsIDs($item, 'component_custom_admin_menus', $updaterT); break; case 'component_config': // diverged id already updated if (!$diverged) { // update the joomla_component ID where needed $item = $this->setNewID($item, 'joomla_component', 'joomla_component', $type); } // repeatable fields to update $updaterR = array( // repeatablefield => checker 'addconfig' => 'field' ); // update the repeatable fields $item = ComponentbuilderHelper::convertRepeatableFields($item, $updaterR); // subform fields to target $updaterT = array( // subformfield => array( field => type_value ) 'addconfig' => array('field' => 'field') ); // update the subform ids $this->updateSubformsIDs($item, 'component_config', $updaterT); break; case 'component_dashboard': // diverged id already updated if (!$diverged) { // update the joomla_component ID where needed $item = $this->setNewID($item, 'joomla_component', 'joomla_component', $type); } // repeatable fields to update $updaterR = array( // repeatablefield => checker 'dashboard_tab' => 'name' ); // update the repeatable fields $item = ComponentbuilderHelper::convertRepeatableFields($item, $updaterR); break; case 'component_placeholders': // diverged id already updated if (!$diverged) { // update the joomla_component ID where needed $item = $this->setNewID($item, 'joomla_component', 'joomla_component', $type); } break; case 'component_modules': // diverged id already updated if (!$diverged) { // update the joomla_component ID where needed $item = $this->setNewID($item, 'joomla_component', 'joomla_component', $type); } // subform fields to target $updaterT = array( // subformfield => array( field => type_value ) 'addjoomla_modules' => array('module' => 'joomla_module') ); // update the subform ids $this->updateSubformsIDs($item, 'component_modules', $updaterT); break; case 'component_plugins': // diverged id already updated if (!$diverged) { // update the joomla_component ID where needed $item = $this->setNewID($item, 'joomla_component', 'joomla_component', $type); } // subform fields to target $updaterT = array( // subformfield => array( field => type_value ) 'addjoomla_plugins' => array('plugin' => 'joomla_plugin') ); // update the subform ids $this->updateSubformsIDs($item, 'component_plugins', $updaterT); break; case 'component_files_folders': // diverged id already updated if (!$diverged) { // update the joomla_component ID where needed $item = $this->setNewID($item, 'joomla_component', 'joomla_component', $type); } // repeatable fields to update $updaterR = array( // repeatablefield => checker 'addfiles' => 'file', 'addfolders' => 'folder' ); // update the repeatable fields $item = ComponentbuilderHelper::convertRepeatableFields($item, $updaterR); break; case 'joomla_module': // update the custom_get $item = $this->setNewID($item, 'custom_get', 'dynamic_get', $type); // if we can't merge add postfix to name if ($this->postfix) { $item->system_name = $item->system_name.$this->postfix; } // subform fields to target $updaterT = array( // subformfield => array( field => type_value ) 'fields' => array('field' => 'field') ); // update the subform ids $this->updateSubformsIDs($item, 'joomla_module', $updaterT); break; case 'joomla_module_files_folders_urls': case 'joomla_module_updates': // diverged id already updated if (!$diverged) { // update the joomla_module ID where needed $item = $this->setNewID($item, 'joomla_module', 'joomla_module', $type); } break; case 'joomla_plugin_group': // diverged id already updated if (!$diverged) { // update the class_extends ID where needed $item = $this->setNewID($item, 'class_extends', 'class_extends', $type); } break; case 'class_method': case 'class_property': // diverged id already updated if (!$diverged) { // update the joomla_plugin_group ID where needed $item = $this->setNewID($item, 'joomla_plugin_group', 'joomla_plugin_group', $type); } break; case 'joomla_plugin': // diverged id already updated if (!$diverged) { // update the class_extends ID where needed $item = $this->setNewID($item, 'class_extends', 'class_extends', $type); // update the joomla_plugin_group ID where needed $item = $this->setNewID($item, 'joomla_plugin_group', 'joomla_plugin_group', $type); } // if we can't merge add postfix to name if ($this->postfix) { $item->system_name = $item->system_name.$this->postfix; } // subform fields to target $updaterT = array( // subformfield => array( field => type_value ) 'fields' => array('field' => 'field'), 'property_selection' => array('property' => 'class_property'), 'method_selection' => array('method' => 'class_method') ); // update the subform ids $this->updateSubformsIDs($item, 'joomla_plugin', $updaterT); break; case 'joomla_plugin_files_folders_urls': case 'joomla_plugin_updates': // diverged id already updated if (!$diverged) { // update the joomla_plugin ID where needed $item = $this->setNewID($item, 'joomla_plugin', 'joomla_plugin', $type); } break; case 'power': // update the powers after.... since no power is yet found $this->updateAfter['power'][(int) $item->id] = $action; break; case 'custom_code': // update the component ID where needed $item = $this->setNewID($item, 'component', 'joomla_component', $type); break; case 'language_translation': $langKeys = array( array('target' => 'components', 'parent' => 'joomla_component', 'local' => 'localComponents'), array('target' => 'modules', 'parent' => 'joomla_module', 'local' => 'localModules'), array('target' => 'plugins', 'parent' => 'joomla_plugin', 'local' => 'localPlugins') ); // we have a few to check so we loop them foreach ($langKeys as $lang) { // update the target ID where needed $item = $this->setNewID($item, $lang['target'], 'joomla_component', $type); // load the local targets if found if (isset($item->{$lang['local']}) && JsonHelper::check($item->{$lang['local']})) { $targets = array(); if (isset($item->{$lang['target']}) && JsonHelper::check($item->{$lang['target']})) { $targets = json_decode($item->{$lang['target']}, true); } $localComponents = json_decode($item->{$lang['local']}, true); foreach ($localComponents as $lid) { if (!is_numeric($lid)) { continue; } // add if not already there if (!in_array($lid, $targets)) { $targets[] = $lid; } } } // remove the localComponents if (isset($item->{$lang['local']})) { unset($item->{$lang['local']}); } // load it back if (isset($targets) && JCBArrayHelper::check($targets)) { // load it back $item->{$lang['target']} = json_encode(array_values($targets), JSON_FORCE_OBJECT); } } // merge the translations where needed if (isset($item->translation) && isset($item->localTranslation) && JsonHelper::check($item->translation) && JsonHelper::check($item->localTranslation)) { $newTranslations = json_decode($item->translation, true); $localTranslations = json_decode($item->localTranslation, true); // always the new format $translations = array(); $pointer = 0; $checker = array(); // okay we have the old format lets merge on that basis if (isset($newTranslations['translation'])) { foreach ($localTranslations as $value) { // only keep old translation if the new does not have this translation & language if (!in_array($value['language'], $newTranslations['language'])) { $translations['translation' . $pointer] = array('translation' => $value['translation'], 'language' => $value['language']); $pointer++; } } foreach ($newTranslations['translation'] as $nr => $newTrans) { // now convert the new translation array $translations['translation' . $pointer] = array('translation' => $newTrans, 'language' => $newTranslations['language'][$nr]); $pointer++; } } // okay this is the new format lets merge on that basis elseif (JCBArrayHelper::check($newTranslations)) { $translations = $newTranslations; $pointer = count($translations); foreach ($localTranslations as $value) { $keepLocal = true; foreach ($newTranslations as $newValue) { // only keep old translation if the new does not have this translation & language if ($value['language'] === $newValue['language']) { $keepLocal = false; } } if ($keepLocal) { $translations['translation' . $pointer] = array('translation' => $value['translation'], 'language' => $value['language']); $pointer++; } } } // okay seem to only have local translations elseif (JCBArrayHelper::check($localTranslations)) { $translations = $localTranslations; } // only update if we have translations if (JCBArrayHelper::check($translations)) { $item->translation = json_encode($translations, JSON_FORCE_OBJECT); } } elseif (isset($item->localTranslation) && JsonHelper::check($item->localTranslation)) { $item->translation = $item->localTranslation; } // remove the localTranslation if (isset($item->localTranslation)) { unset($item->localTranslation); } // move entranslation to source if (isset($item->entranslation)) { $item->source = $item->entranslation; // also remove the old field unset($item->entranslation); } break; case 'admin_fields': case 'admin_fields_conditions': case 'admin_fields_relations': case 'admin_custom_tabs': // diverged id already updated if (!$diverged) { // update the admin_view ID where needed $item = $this->setNewID($item, 'admin_view', 'admin_view', $type); } $updaterR = array(); // set the updater if ('admin_fields' === $type) { // repeatable fields to update $updaterR = array( // repeatablefield => checker 'addfields' => 'field' ); // subform fields to target $updaterT = array( // subformfield => field => type_value 'addfields' => array('field' => 'field') ); // little tweak... oops if (isset($item->addconditions)) { unset($item->addconditions); } } elseif ('admin_fields_conditions' === $type) { // repeatable fields to update $updaterR = array( // repeatablefield => checker 'addconditions' => 'target_field' ); // subform fields to target $updaterT = array( // subformfield => field => type_value 'addconditions' => array('target_field' => 'field', 'match_field' => 'field') ); } elseif ('admin_fields_relations' === $type) { // subform fields to target $updaterT = array( // subformfield => field => type_value 'addrelations' => array('listfield' => 'field', 'joinfields' => 'field') ); // special fix for custom code $this->updateAfter['relations'][(int) $item->id] = $action; // addrelations->set } // update the repeatable fields if (isset($updaterR) && JCBArrayHelper::check($updaterR)) { $item = ComponentbuilderHelper::convertRepeatableFields($item, $updaterR); } // update the subform ids if (isset($updaterT) && JCBArrayHelper::check($updaterT)) { $this->updateSubformsIDs($item, $type, $updaterT); } break; } // remove all fields/columns not part of the current table $this->removingFields($type, $item); // final action prep switch($action) { case 'update': // set values to follow the update conventions if (isset($item->created_by)) { unset($item->created_by); } $item->modified_by = $this->user->id; $item->modified = $this->today; // return the item return $item; break; case 'add': // remove the ID if (isset($item->id)) { unset($item->id); } // set values to follow the adding conventions $item->created_by = $this->user->id; $item->modified_by = $this->user->id; $item->modified = $this->today; // return the item return $item; break; } return false; } /** * remove all fields/columns not part of the current table * * @param string $type The table this item belongs to * @param object $item The item to clean * * @return viod */ private function removingFields($type, &$item) { // get the columns $columns = $this->getTableColumns("#__componentbuilder_" . $type); if (JCBArrayHelper::check($columns)) { foreach ($item as $name => $value) { if (!isset($columns[$name])) { // we must show a warning that this field was not imported (but just once) if (!isset($this->fieldImportErrors[$type.$name])) { $this->app->enqueueMessage(Text::sprintf('COM_COMPONENTBUILDER_FIELD_BSB_NOT_FOUND_IN_LOCAL_DATABASE_TABLE_S_SO_IMPORTED_OF_ITS_VALUES_FAILED_PLEASE_UPDATE_YOUR_JCB_INSTALL_AND_TRY_AGAIN', $name, '#__componentbuilder_' . $type), 'warning'); // make sure the message is not loaded again $this->fieldImportErrors[$type.$name] = true; } // remove the field & value unset($item->{$name}); } } } } /** * get table columns * * @param string $table The table * * @return array */ private function getTableColumns($table) { // check if the columns are in memory if (!isset($this->tableColumns[$table])) { // get the columns $this->tableColumns[$table] = $this->_db->getTableColumns($table); } return $this->tableColumns[$table]; } /** * Set the data that should be moved * * @param array/json $values The values/data to move * @param string $table The table to move the values to * @param string $type The type of values * @param array $getters The get values used to anchor the values to the new table * * @return bool */ private function setDivergedDataMover($values, $table, $type, $getters) { // we need to move this to the new $table based on anchors if (JCBArrayHelper::check($getters)) { if (!isset($this->divergedDataMover[$table])) { $this->divergedDataMover[$table] = array(); } // set unique key $uniqueKey = md5(serialize($getters)); if (!isset($this->divergedDataMover[$table][$uniqueKey])) { $this->divergedDataMover[$table][$uniqueKey] = new stdClass; foreach ($getters as $name => $value) { $this->divergedDataMover[$table][$uniqueKey]->{$name} = $value; } } // add the data to the mover $this->divergedDataMover[$table][$uniqueKey]->{$type} = $values; // display more import info if ($this->moreInfo) { $this->app->enqueueMessage(Text::sprintf('COM_COMPONENTBUILDER_WE_SUCCESSFULLY_MOVED_BSB', StringHelper::safe($type, 'Ww') . ' to ('.StringHelper::safe($table, 'w').')'), 'success'); } // success return true; } $this->app->enqueueMessage(Text::sprintf('COM_COMPONENTBUILDER_WE_FAILED_TO_MOVE_BSB', StringHelper::safe($type, 'Ww') . ' to ('.StringHelper::safe($table, 'w').')'), 'warning'); // failure return false; } /** * Check if a field has multiple fields * * @param string $typeID The type ID * * @return bool true on success * */ private function checkMultiFields($typeID) { if(isset($this->isMultiple[$typeID])) { return $this->isMultiple[$typeID]; } elseif (($type = $this->getFieldType($typeID)) !== false) { if ('repeatable' === $type || 'subform' === $type ) { $this->isMultiple[$typeID] = true; } else { $this->isMultiple[$typeID] = false; } return $this->isMultiple[$typeID]; } return false; } /** * Get the field type * * @param string $id The field type id * * @return string field type * */ private function getFieldType($id) { if (!isset($this->fieldTypes[$id])) { $properties = GetHelper::var('fieldtype', $id, 'id', 'properties'); if (JsonHelper::check($properties)) { $properties = json_decode($properties, true); // check if this is old values for repeatable fields if (isset($properties['name'])) { $properties = ComponentbuilderHelper::convertRepeatable($properties, 'properties'); } // now check to find type foreach ($properties as $property) { if ('type' === $property['name']) { if (isset($property['example']) && StringHelper::check($property['example'])) { $this->fieldTypes[$id] = $property['example']; break; } } } } // if not found if (!isset($this->fieldTypes[$id]) && $name = GetHelper::var('fieldtype', $id, 'id', 'name')) { $this->fieldTypes[$id] = StringHelper::safe($name); } } // return the type if (isset($this->fieldTypes[$id])) { return $this->fieldTypes[$id]; } return false; } /** * Update the local item * * @param object $item The item to update * @param string $type The type of values * @param bool $canState The switch to set state * * @return mixed false on failure * ID int on success * **/ private function updateLocalItem(&$item, $type, &$canState) { // prep the item if ($update = $this->prepItem($item, $type, 'update')) { // remove the published state if not allowed to edit it if (!$canState && isset($update->published)) { unset($update->published); } // update the item if ($result = $this->_db->updateObject('#__componentbuilder_' . $type, $update, 'id')) { // return success return $update->id; } } return false; } /** * add the local item * * @param object $item The item to update * @param string $type The type of values * @param bool $diverged The diverged data switch * * @return mixed false on failure * ID int on success * **/ private function addLocalItem(&$item, $type, $diverged = false) { // prep the item if ($add = $this->prepItem($item, $type, 'add', $diverged)) { // insert/add the item if ($result = $this->_db->insertObject('#__componentbuilder_' . $type, $add)) { $aId = $this->_db->insertid(); // make sure the access of asset is set ComponentbuilderHelper::setAsset($aId, $type); // set new ID return $aId; } } return false; } /** * Get the local item * * @param object $item The item to get * @param string $type The type of values * @param bool $retry The retry switch * @param bool $get The query get switch * @param bool $diverged The diverged data switch * * @return mixed false on failure * ID int on success * **/ private function getLocalItem($item, $type, $retry = false, $get = 1, $diverged = false) { $query = $this->_db->getQuery(true); $query->select('a.*'); $query->from($this->_db->quoteName('#__componentbuilder_' . $type, 'a')); // only run query if where is set $runQuery = false; // we first try to get the item by GUID if ($get == 1 && isset($item->guid)) { if (($item = GuidHelper::item($item->guid, $type, 'a.*')) !== false) { return $item; } // check if we should continue the search // we only link powers by GUID elseif ($this->importGuidOnly == 1 || $type === 'power') { return false; } } // continue search if ($get == 1 && isset($item->created) && isset($item->id) && (isset($item->name) || isset($item->system_name))) { // to prefent crazy mismatch with old IDs (I know very weired) if (isset($item->system_name)) { $query->where($this->_db->quoteName('a.system_name') . ' = '. $this->_db->quote($item->system_name)); } // to prefent crazy mismatch with old IDs (I know very weired) if (isset($item->name)) { $query->where($this->_db->quoteName('a.name') . ' = '. $this->_db->quote($item->name)); } // load the created and id $query->where($this->_db->quoteName('a.created') . ' = '. $this->_db->quote($item->created)); $query->where($this->_db->quoteName('a.id') .' = '. (int) $item->id); // set to run query $runQuery = true; } elseif (JCBArrayHelper::check($get)) { foreach ($get as $field) { // set to run query $runQuery = true; if (isset($item->{$field})) { // set the value $value = $item->{$field}; // check if we have special value if ($this->specialValue && JCBArrayHelper::check($this->specialValue) && isset($this->specialValue[$field])) { $value = $this->specialValue[$field]; } // load to query if (is_numeric($value) && is_int($value)) { $query->where($this->_db->quoteName('a.' . $field) . ' = '. (int) $value); } elseif (is_numeric($value) && is_float($value)) { $query->where($this->_db->quoteName('a.' . $field) . ' = '. (float) $value); } elseif(StringHelper::check($value)) // do not allow empty strings (since it could be major mis match) { $query->where($this->_db->quoteName('a.' . $field) . ' = '. $this->_db->quote($value)); } else { // do not run query $runQuery = false; } } else { // do not run query $runQuery = false; } } } elseif (isset($item->{$get})) { // set to run query $runQuery = true; // set the value $value = $item->{$get}; // check if we have special value if ($this->specialValue && JCBArrayHelper::check($this->specialValue) && isset($this->specialValue[$get])) { $value = $this->specialValue[$get]; } // load to query if (is_numeric($value) && is_int($value)) { $query->where($this->_db->quoteName('a.' . $get) . ' = '. (int) $value); } elseif (is_numeric($value) && is_float($value)) { $query->where($this->_db->quoteName('a.' . $get) . ' = '. (float) $value); } elseif(StringHelper::check($value)) // do not allow empty strings (since it could be major mis match) { $query->where($this->_db->quoteName('a.' . $get) . ' = '. $this->_db->quote($value)); } else { $runQuery = false; // really not needed but who knows for sure... } } // since where has been set run the query if ($runQuery) { // see if we get an item $this->_db->setQuery($query); $this->_db->execute(); if ($this->_db->getNumRows()) { return $this->_db->loadObject(); } } // retry to get the item if ($retry) { $retryAgain = false; $this->specialValue = false; // set the getter switch ($type) { case 'admin_fields': case 'admin_fields_conditions': case 'admin_fields_relations': case 'admin_custom_tabs': // get by admin_view (since there should only be one of each name) $getter = array('admin_view'); $this->specialValue = array(); // Yet if diverged it makes sense that the ID is updated. if ($diverged) { $this->specialValue['admin_view'] = (int) $item->admin_view; } elseif (isset($this->newID['admin_view'][(int) $item->admin_view])) { $this->specialValue['admin_view'] = $this->newID['admin_view'][(int) $item->admin_view]; } // (TODO) I have seen this happen, seems dangerous! else { return false; } break; case 'validation_rule': case 'fieldtype': // get by name (since there should only be one of each name) $getter = 'name'; break; case 'field': // get by name and xml to target correct field if ($retry == 2) { // get by name + xml... $getter = array('name','datatype','store','indexes','null_switch','xml'); $retryAgain = 3; } elseif ($retry == 3) { // get by name + created... $getter = array('name','datatype','created'); } else { // get by name + xml or type.. $getter = array('name','datatype','store','indexes','null_switch'); // lets try to add the fieldtype if (isset($item->fieldtype) && is_numeric($item->fieldtype) && $item->fieldtype > 0 && isset($this->newID['fieldtype'][(int) $item->fieldtype]) && $this->newID['fieldtype'][(int) $item->fieldtype] > 0) { $getter[] = 'fieldtype'; $this->specialValue = array(); $this->specialValue['fieldtype'] = $this->newID['fieldtype'][(int) $item->fieldtype]; $retryAgain = 2; } else { $getter[] = 'xml'; $retryAgain = 3; } } break; case 'site_view': case 'custom_admin_view': // get by name, system_name and codename $getter = array('name', 'system_name', 'codename'); // lets try to add the main_get if (isset($item->main_get) && is_numeric($item->main_get) && $item->main_get > 0 && isset($this->newID['dynamic_get'][(int) $item->main_get]) && $this->newID['dynamic_get'][(int) $item->main_get] > 0) { $getter[] = 'main_get'; $this->specialValue = array(); $this->specialValue['main_get'] = $this->newID['dynamic_get'][(int) $item->main_get]; } break; case 'template': case 'layout': // get by code name (since there should only be one) $getter = 'alias'; break; case 'snippet': // get by snippet (since there should only be one snippet like that) if ($retry == 2) { $getter = array('name', 'snippet', 'url', 'type', 'heading'); } else { // get by id name.. $getter = array('id', 'name', 'snippet', 'url', 'type', 'heading'); $retryAgain = 2; } break; case 'placeholder': // search for placeholder (since there should only be one) $getter = 'target'; break; case 'custom_code': // search for custom code $getter = array('comment_type', 'target'); $this->specialValue = array(); // search for Hash (automation) if (isset($item->target) && $item->target == 1) { $getter[] = 'path'; $getter[] = 'hashtarget'; $getter[] = 'component'; // Yet if diverged it makes sense that the ID is updated. if ($diverged) { // set a special value $this->specialValue['component'] = (int) $item->component; } elseif (isset($this->newID['joomla_component'][(int) $item->component])) { // set a special value $this->specialValue['component'] = $this->newID['joomla_component'][(int) $item->component]; } // (TODO) I have seen this happen, seems dangerous! else { return false; } } // search for JCB (manual) elseif (isset($item->target) && $item->target == 2) { $getter[] = 'function_name'; } else { return false; } break; case 'dynamic_get': if ($retry == 2) { // get by name ... $getter = array('name', 'gettype', 'main_source'); // risky will look at this again // add some more advanced search if (isset($item->main_source) && $item->main_source == 1 && isset($item->view_selection) && StringHelper::check($item->view_selection)) { $getter[] = 'view_selection'; } elseif (isset($item->main_source) && $item->main_source == 2 && isset($item->db_selection) && StringHelper::check($item->db_selection)) { $getter[] = 'db_selection'; } elseif (isset($item->main_source) && $item->main_source == 3 && isset($item->php_custom_get) && StringHelper::check($item->php_custom_get)) { $getter[] = 'php_custom_get'; } // add some extra if (isset($item->getcustom) && StringHelper::check($item->getcustom)) { $getter[] = 'getcustom'; } } else { // get by id name gettype and main_source $getter = array('id', 'name', 'gettype', 'main_source'); // risky will look at this again $retryAgain = 2; } break; case 'admin_view': if ($retry == 2) { // get by name ... $getter = array('name_list', 'name_single', 'short_description', 'system_name'); // risky will look at this again } else { // get by id name ... $getter = array('id', 'name_list', 'name_single', 'short_description', 'system_name'); // risky will look at this again $retryAgain = 2; } break; case 'joomla_component': if ($retry == 3) { // get by names only $getter = array('name', 'name_code', 'system_name'); } elseif ($retry == 2) { // get by name ... $getter = array('name', 'name_code', 'short_description', 'author', 'email', 'component_version', 'companyname', 'system_name', 'website', 'bom', 'copyright', 'license'); $retryAgain = 3; } else { // get by id name ... $getter = array('id', 'name', 'name_code', 'short_description', 'author', 'component_version', 'companyname', 'system_name'); $retryAgain = 2; } break; case 'component_admin_views': case 'component_site_views': case 'component_custom_admin_views': case 'component_updates': case 'component_mysql_tweaks': case 'component_custom_admin_menus': case 'component_config': case 'component_dashboard': case 'component_placeholders': case 'component_files_folders': case 'component_modules': case 'component_plugins': // get by joomla_component (since there should only be one of each component) $getter = array('joomla_component'); $this->specialValue = array(); // Yet if diverged it makes sense that the ID is updated. if ($diverged) { $this->specialValue['joomla_component'] = (int) $item->joomla_component; } elseif (isset($this->newID['joomla_component'][(int) $item->joomla_component])) { $this->specialValue['joomla_component'] = $this->newID['joomla_component'][(int) $item->joomla_component]; } // (TODO) I have seen this happen, seems dangerous! else { return false; } break; case 'language_translation': // get by source translation since there should just be one $getter = 'source'; if (isset($item->entranslation)) { $item->source = $item->entranslation; } break; case 'language': // get by language tag since there should just be one $getter = 'langtag'; break; case 'joomla_module': // get if ($retry == 3) { // get by names, exteneded and group only $getter = array('name', 'system_name'); } elseif ($retry == 2) { // get by description $getter = array('name', 'system_name', 'description'); $retryAgain = 3; } else { // get by id $getter = array('id', 'name', 'system_name'); $retryAgain = 2; } break; case 'joomla_module_files_folders_urls': case 'joomla_module_updates': // get by admin_view (since there should only be one of each name) $getter = array('joomla_module'); $this->specialValue = array(); // Yet if diverged it makes sense that the ID is updated. if ($diverged) { $this->specialValue['joomla_module'] = (int) $item->joomla_module; } elseif (isset($this->newID['joomla_module'][(int) $item->joomla_module])) { $this->specialValue['joomla_module'] = $this->newID['joomla_module'][(int) $item->joomla_module]; } // (TODO) I have seen this happen, seems dangerous! else { return false; } break; case 'joomla_plugin': // get if ($retry == 3) { // get by names, exteneded and group only $getter = array('name', 'system_name', 'class_extends', 'joomla_plugin_group'); } elseif ($retry == 2) { // get by description $getter = array('name', 'system_name', 'class_extends', 'joomla_plugin_group', 'description'); $retryAgain = 3; } else { // get by id $getter = array('id', 'name', 'system_name', 'class_extends', 'joomla_plugin_group'); $retryAgain = 2; } $this->specialValue = array(); // Yet if diverged it makes sense that the ID is updated. if ($diverged) { $this->specialValue['class_extends'] = (int) $item->class_extends; $this->specialValue['joomla_plugin_group'] = (int) $item->joomla_plugin_group; } elseif (isset($this->newID['class_extends'][(int) $item->class_extends]) && isset($this->newID['joomla_plugin_group'][(int) $item->joomla_plugin_group])) { $this->specialValue['class_extends'] = $this->newID['class_extends'][(int) $item->class_extends]; $this->specialValue['joomla_plugin_group'] = $this->newID['joomla_plugin_group'][(int) $item->joomla_plugin_group]; } // (TODO) I have seen this happen, seems dangerous! else { return false; } break; case 'joomla_plugin_files_folders_urls': case 'joomla_plugin_updates': // get by admin_view (since there should only be one of each name) $getter = array('joomla_plugin'); $this->specialValue = array(); // Yet if diverged it makes sense that the ID is updated. if ($diverged) { $this->specialValue['joomla_plugin'] = (int) $item->joomla_plugin; } elseif (isset($this->newID['joomla_plugin'][(int) $item->joomla_plugin])) { $this->specialValue['joomla_plugin'] = $this->newID['joomla_plugin'][(int) $item->joomla_plugin]; } // (TODO) I have seen this happen, seems dangerous! else { return false; } break; case 'joomla_plugin_group': // get by name since there should just be one $getter = array('name', 'class_extends'); $this->specialValue = array(); // Yet if diverged it makes sense that the ID is updated. if ($diverged) { $this->specialValue['class_extends'] = (int) $item->class_extends; } elseif (isset($this->newID['class_extends'][(int) $item->class_extends])) { $this->specialValue['class_extends'] = $this->newID['class_extends'][(int) $item->class_extends]; } // (TODO) I have seen this happen, seems dangerous! else { return false; } break; case 'class_extends': case 'class_method': case 'class_property': // get by name since there should just be one $getter = array('name', 'extension_type'); // Yet if diverged it makes sense that the ID is updated. if ('plugins' === $item->extension_type && isset($item->joomla_plugin_group)) { $getter[] = 'joomla_plugin_group'; $this->specialValue = array(); if ($diverged) { $this->specialValue['joomla_plugin_group'] = (int) $item->joomla_plugin_group; } elseif (isset($this->newID['joomla_plugin_group'][(int) $item->joomla_plugin_group])) { $this->specialValue['joomla_plugin_group'] = $this->newID['joomla_plugin_group'][(int) $item->joomla_plugin_group]; } // (TODO) I have seen this happen, seems dangerous! else { return false; } } break; default: // can't be found so return false return false; break; } // we try again return $this->getLocalItem($item, $type, $retryAgain, $getter); } return false; } /** * Update constant path with real full path value * * @param string $path The full path * * @return string The updated path * */ private function setFullPath($path) { return str_replace(array_keys(ComponentbuilderHelper::$constantPaths), array_values(ComponentbuilderHelper::$constantPaths), $path); } /** * Convert the name to a path * * @param string $path The path name * * @return string The full path * */ private function setDynamicPath($path) { // now convert to path $path = str_replace('__v_d_m__', '/', $path); // add the full path if possible return str_replace('//', '/', $this->setFullPath($path)); } protected function getAlias($name,$type = false) { // sanitize the name to an alias if (JFactory::getConfig()->get('unicodeslugs') == 1) { $alias = JFilterOutput::stringURLUnicodeSlug($name); } else { $alias = JFilterOutput::stringURLSafe($name); } // must be a uniqe alias if ($type) { return $this->getUniqe($alias,'alias',$type); } return $alias; } /** * Method to generate a uniqe value. * * @param string $field name. * @param string $value data. * @param string $type table. * * @return string New value. */ protected function getUniqe($value,$field,$type) { // insure the filed is always uniqe while (isset($this->uniqeValueArray[$type][$field][$value])) { $value = JString::increment($value, 'dash'); } $this->uniqeValueArray[$type][$field][$value] = $value; return $value; } protected function getAliasesUsed($table) { // Get a db connection. $db = JFactory::getDbo(); // first we check if there is a alias column $columns = $db->getTableColumns('#__componentbuilder_'.$table); if(isset($columns['alias'])){ // Create a new query object. $query = $db->getQuery(true); $query->select($db->quoteName(array('alias'))); $query->from($db->quoteName('#__componentbuilder_'.$table)); $db->setQuery($query); $db->execute(); if ($db->getNumRows()) { $aliases = $db->loadColumn(); foreach($aliases as $alias) { $this->uniqeValueArray[$table]['alias'][$alias] = $alias; } } return true; } return false; } }