diff --git a/README.md b/README.md index 6f5a0b002..c881984d4 100644 --- a/README.md +++ b/README.md @@ -140,14 +140,14 @@ TODO + *Author*: [Llewellyn van der Merwe](mailto:joomla@vdm.io) + *Name*: [Component Builder](https://git.vdm.dev/joomla/Component-Builder) + *First Build*: 30th April, 2015 -+ *Last Build*: 31st August, 2022 ++ *Last Build*: 2nd September, 2022 + *Version*: 3.1.5 + *Copyright*: Copyright (C) 2015 Vast Development Method. All rights reserved. + *License*: GNU General Public License version 2 or later; see LICENSE.txt -+ *Line count*: **319873** ++ *Line count*: **321348** + *Field count*: **2002** -+ *File count*: **2090** -+ *Folder count*: **359** ++ *File count*: **2094** ++ *Folder count*: **360** > This **component** was build with a [Joomla](https://extensions.joomla.org/extension/component-builder/) [Automated Component Builder](http://joomlacomponentbuilder.com). > Developed by [Llewellyn van der Merwe](mailto:llewellyn@joomlacomponentbuilder.com) diff --git a/admin/README.txt b/admin/README.txt index 6f5a0b002..c881984d4 100644 --- a/admin/README.txt +++ b/admin/README.txt @@ -140,14 +140,14 @@ TODO + *Author*: [Llewellyn van der Merwe](mailto:joomla@vdm.io) + *Name*: [Component Builder](https://git.vdm.dev/joomla/Component-Builder) + *First Build*: 30th April, 2015 -+ *Last Build*: 31st August, 2022 ++ *Last Build*: 2nd September, 2022 + *Version*: 3.1.5 + *Copyright*: Copyright (C) 2015 Vast Development Method. All rights reserved. + *License*: GNU General Public License version 2 or later; see LICENSE.txt -+ *Line count*: **319873** ++ *Line count*: **321348** + *Field count*: **2002** -+ *File count*: **2090** -+ *Folder count*: **359** ++ *File count*: **2094** ++ *Folder count*: **360** > This **component** was build with a [Joomla](https://extensions.joomla.org/extension/component-builder/) [Automated Component Builder](http://joomlacomponentbuilder.com). > Developed by [Llewellyn van der Merwe](mailto:llewellyn@joomlacomponentbuilder.com) diff --git a/admin/helpers/compiler/a_Get.php b/admin/helpers/compiler/a_Get.php index bafeb57cf..037d28d42 100644 --- a/admin/helpers/compiler/a_Get.php +++ b/admin/helpers/compiler/a_Get.php @@ -277,6 +277,8 @@ class Get * ////////23 is the ID of the code in the system don't change it!!!!!!!!!!!!!!!!!!!!!!!!!! * * @var array + * + * @deprecated 3.3 */ protected $customCodePlaceholders = array( @@ -322,6 +324,7 @@ class Get * The custom code in local files that already exist in system * * @var array + * @deprecated 3.3 */ protected $existingCustomCode = array(); @@ -329,6 +332,7 @@ class Get * The custom code in local files this are new * * @var array + * @deprecated 3.3 */ protected $newCustomCode = array(); @@ -336,6 +340,7 @@ class Get * The index of code already loaded * * @var array + * @deprecated 3.3 */ protected $codeAreadyDone = array(); @@ -972,7 +977,6 @@ class Get */ public function __construct() { - echo '
'; // we do not yet have this set as an option $config['remove_line_breaks'] = 2; // 2 is global (use the components value) @@ -1042,14 +1046,10 @@ class Get $this->db = JFactory::getDbo(); // get global placeholders @deprecated $this->globalPlaceholders = CFactory::_('Component.Placeholder')->get(); - // check if this component is installed on the current website - if ($paths = $this->getLocalInstallPaths()) - { - // start Automatic import of custom code - $today = JFactory::getDate()->toSql(); - // get the custom code from installed files - $this->customCodeFactory($paths, $today); - } + + // get the custom code from installed files + CFactory::_('Customcode.Extractor')->run(); + // Trigger Event: jcb_ce_onBeforeGetComponentData CFactory::_J('Event')->trigger( 'jcb_ce_onBeforeGetComponentData', @@ -6907,11 +6907,11 @@ class Get { // set notice that we could not get a valid string from the target $this->app->enqueueMessage( - JText::_('External Code Warning
'), 'Error' + JText::sprintf('%s Warning
', __CLASS__), 'Error' ); $this->app->enqueueMessage( - JText::_( - 'Use of a deprecated method (getExternalCodeString)!' + JText::sprintf( + 'Use of a deprecated method (%s)!', __METHOD__ ), 'Error' ); } @@ -6930,11 +6930,11 @@ class Get { // set notice that we could not get a valid string from the target $this->app->enqueueMessage( - JText::_('External Code Warning
'), 'Error' + JText::sprintf('%s Warning
', __CLASS__), 'Error' ); $this->app->enqueueMessage( - JText::_( - 'Use of a deprecated method (cutExternalCodeString)!' + JText::sprintf( + 'Use of a deprecated method (%s)!', __METHOD__ ), 'Error' ); @@ -6968,11 +6968,11 @@ class Get { // set notice that we could not get a valid string from the target $this->app->enqueueMessage( - JText::_('External Code Warning
'), 'Error' + JText::sprintf('%s Warning
', __CLASS__), 'Error' ); $this->app->enqueueMessage( - JText::_( - 'Use of a deprecated method (insertCustomCode)!' + JText::sprintf( + 'Use of a deprecated method (%s)!', __METHOD__ ), 'Error' ); @@ -6992,11 +6992,11 @@ class Get { // set notice that we could not get a valid string from the target $this->app->enqueueMessage( - JText::_('External Code Warning
'), 'Error' + JText::sprintf('%s Warning
', __CLASS__), 'Error' ); $this->app->enqueueMessage( - JText::_( - 'Use of a deprecated method (buildCustomCodePlaceholders)!' + JText::sprintf( + 'Use of a deprecated method (%s)!', __METHOD__ ), 'Error' ); @@ -7579,23 +7579,15 @@ class Get */ protected function checkCustomCodeMemory($ids) { - // reset custom code - CFactory::_('Customcode')->active = array(); - foreach ($ids as $pointer => $id) - { - if (isset(CFactory::_('Customcode')->memory[$id])) - { - CFactory::_('Customcode')->active[] = CFactory::_('Customcode')->memory[$id]; - unset($ids[$pointer]); - } - } - // check if any ids left to fetch - if (ArrayHelper::check($ids)) - { - return $ids; - } - - return false; + // set notice that we could not get a valid string from the target + $this->app->enqueueMessage( + JText::sprintf('%s Warning
', __CLASS__), 'Error' + ); + $this->app->enqueueMessage( + JText::sprintf( + 'Use of a deprecated method (%s)!', __METHOD__ + ), 'Error' + ); } /** @@ -7630,127 +7622,57 @@ class Get { // set notice that we could not get a valid string from the target $this->app->enqueueMessage( - JText::_('Power building error
'), 'Error' + JText::sprintf('%s Warning
', __CLASS__), 'Error' ); $this->app->enqueueMessage( - JText::_( - 'Use of a deprecated method (setPower)!' + JText::sprintf( + 'Use of a deprecated method (%s)!', __METHOD__ + ), 'Error' + ); + + return false; + } + + /** + * get the Joomla module path + * + * @return string of module path and target site area on success + * @deprecated 3.3 + */ + protected function getModulePath($id) + { + // set notice that we could not get a valid string from the target + $this->app->enqueueMessage( + JText::sprintf('%s Warning
', __CLASS__), 'Error' + ); + $this->app->enqueueMessage( + JText::sprintf( + 'Use of a deprecated method (%s)!', __METHOD__ ), 'Error' ); return ''; } - /** - * get the Joomla module path - * - * @return array of module path and target site area on success - * - */ - protected function getModulePath($id) - { - if (is_numeric($id) && $id > 0) - { - // Create a new query object. - $query = $this->db->getQuery(true); - - $query->select('a.*'); - $query->select( - $this->db->quoteName( - array( - 'a.name', - 'a.target' - ), array( - 'name', - 'target' - ) - ) - ); - // from these tables - $query->from('#__componentbuilder_joomla_module AS a'); - $query->where($this->db->quoteName('a.id') . ' = ' . (int) $id); - $this->db->setQuery($query); - $this->db->execute(); - if ($this->db->getNumRows()) - { - // get the module data - $module = $this->db->loadObject(); - // update the name if it has dynamic values - $module->name = CFactory::_('Placeholder')->update( - CFactory::_('Customcode')->add($module->name), - $this->globalPlaceholders - ); - // set safe class function name - $module->code_name - = ClassfunctionHelper::safe( - $module->name - ); - // set module folder name - $module->folder_name = 'mod_' . strtolower($module->code_name); - // set the lang key - CFactory::_('Language.Extractor')->langKeys[strtoupper($module->folder_name)] = $module->id - . '_M0dU|3'; - // return the path - if ($module->target == 2) - { - // administrator client area - return JPATH_ADMINISTRATOR . '/modules/' - . $module->folder_name; - } - else - { - // default is the site client area - return JPATH_ROOT . '/modules/' . $module->folder_name; - } - } - } - - return false; - } - /** * get the Joomla Modules IDs * * @return array of IDs on success - * + * @deprecated 3.3 */ protected function getModuleIDs() { - if (($addjoomla_modules = GetHelper::var( - 'component_modules', CFactory::_('Config')->component_id, 'joomla_component', - 'addjoomla_modules' - )) !== false) - { - $addjoomla_modules = (JsonHelper::check( - $addjoomla_modules - )) ? json_decode($addjoomla_modules, true) : null; - if (ArrayHelper::check($addjoomla_modules)) - { - $joomla_modules = array_filter( - array_values($addjoomla_modules), - function ($array) { - // only load the modules whose target association call for it - if (!isset($array['target']) || $array['target'] != 2) - { - return true; - } + // set notice that we could not get a valid string from the target + $this->app->enqueueMessage( + JText::sprintf('%s Warning
', __CLASS__), 'Error' + ); + $this->app->enqueueMessage( + JText::sprintf( + 'Use of a deprecated method (%s)!', __METHOD__ + ), 'Error' + ); - return false; - } - ); - // if we have values we return IDs - if (ArrayHelper::check($joomla_modules)) - { - return array_map( - function ($array) { - return (int) $array['module']; - }, $joomla_modules - ); - } - } - } - - return false; + return []; } /** @@ -8620,113 +8542,42 @@ class Get * get the Joomla plugins IDs * * @return array of IDs on success - * + * @deprecated 3.3 */ protected function getPluginIDs() { - if (($addjoomla_plugins = GetHelper::var( - 'component_plugins', CFactory::_('Config')->component_id, 'joomla_component', - 'addjoomla_plugins' - )) !== false) - { - $addjoomla_plugins = (JsonHelper::check( - $addjoomla_plugins - )) ? json_decode($addjoomla_plugins, true) : null; - if (ArrayHelper::check($addjoomla_plugins)) - { - $joomla_plugins = array_filter( - array_values($addjoomla_plugins), - function ($array) { - // only load the plugins whose target association call for it - if (!isset($array['target']) || $array['target'] != 2) - { - return true; - } + // set notice that we could not get a valid string from the target + $this->app->enqueueMessage( + JText::sprintf('%s Warning
', __CLASS__), 'Error' + ); + $this->app->enqueueMessage( + JText::sprintf( + 'Use of a deprecated method (%s)!', __METHOD__ + ), 'Error' + ); - return false; - } - ); - // if we have values we return IDs - if (ArrayHelper::check($joomla_plugins)) - { - return array_map( - function ($array) { - return (int) $array['plugin']; - }, $joomla_plugins - ); - } - } - } - - return false; + return []; } /** * get the Joomla plugin path * * @return string of plugin path on success - * + * @deprecated 3.3 */ protected function getPluginPath($id) { - if (is_numeric($id) && $id > 0) - { - // Create a new query object. - $query = $this->db->getQuery(true); + // set notice that we could not get a valid string from the target + $this->app->enqueueMessage( + JText::sprintf('%s Warning
', __CLASS__), 'Error' + ); + $this->app->enqueueMessage( + JText::sprintf( + 'Use of a deprecated method (%s)!', __METHOD__ + ), 'Error' + ); - $query->select('a.*'); - $query->select( - $this->db->quoteName( - array( - 'a.name', - 'g.name' - ), array( - 'name', - 'group' - ) - ) - ); - // from these tables - $query->from('#__componentbuilder_joomla_plugin AS a'); - $query->join( - 'LEFT', $this->db->quoteName( - '#__componentbuilder_joomla_plugin_group', 'g' - ) . ' ON (' . $this->db->quoteName('a.joomla_plugin_group') - . ' = ' . $this->db->quoteName('g.id') . ')' - ); - $query->where($this->db->quoteName('a.id') . ' = ' . (int) $id); - $this->db->setQuery($query); - $this->db->execute(); - if ($this->db->getNumRows()) - { - // get the plugin data - $plugin = $this->db->loadObject(); - // update the name if it has dynamic values - $plugin->name = CFactory::_('Placeholder')->update( - CFactory::_('Customcode')->add($plugin->name), - $this->globalPlaceholders - ); - // update the name if it has dynamic values - $plugin->code_name - = ClassfunctionHelper::safe( - $plugin->name - ); - // set plugin folder name - $plugin->group = strtolower($plugin->group); - // set plugin file name - $plugin->file_name = strtolower($plugin->code_name); - // set the lang key - CFactory::_('Language.Extractor')->langKeys['PLG_' . strtoupper( - $plugin->group . '_' . $plugin->file_name - )] - = $plugin->id . '_P|uG!n'; - - // return the path - return $plugin->group . '/' . $plugin->file_name; - } - } - - return false; + return ''; } /** @@ -9496,46 +9347,19 @@ class Get * * @return void * + * @deprecated 3.3 */ protected function setNewCustomCode($when = 1) { - if (count((array) $this->newCustomCode) >= $when) - { - // Create a new query object. - $query = $this->db->getQuery(true); - $continue = false; - // Insert columns. - $columns = array('path', 'type', 'target', 'comment_type', - 'component', 'published', 'created', 'created_by', - 'version', 'access', 'hashtarget', 'from_line', - 'to_line', 'code', 'hashendtarget'); - // Prepare the insert query. - $query->insert( - $this->db->quoteName('#__componentbuilder_custom_code') - ); - $query->columns($this->db->quoteName($columns)); - foreach ($this->newCustomCode as $values) - { - if (count((array) $values) == 15) - { - $query->values(implode(',', $values)); - $continue = true; - } - else - { - // TODO line mismatch... should not happen - } - } - // clear the values array - $this->newCustomCode = array(); - if (!$continue) - { - return false; // insure we dont continue if no values were loaded - } - // Set the query using our newly populated query object and execute it. - $this->db->setQuery($query); - $this->db->execute(); - } + // set notice that we could not get a valid string from the target + $this->app->enqueueMessage( + JText::sprintf('%s Warning
', __CLASS__), 'Error' + ); + $this->app->enqueueMessage( + JText::sprintf( + 'Use of a deprecated method (%s)!', __METHOD__ + ), 'Error' + ); } /** @@ -9545,26 +9369,19 @@ class Get * * @return void * + * @deprecated 3.3 */ protected function setExistingCustomCode($when = 1) { - if (count((array) $this->existingCustomCode) >= $when) - { - foreach ($this->existingCustomCode as $code) - { - // Create a new query object. - $query = $this->db->getQuery(true); - // Prepare the update query. - $query->update( - $this->db->quoteName('#__componentbuilder_custom_code') - )->set($code['fields'])->where($code['conditions']); - // Set the query using our newly populated query object and execute it. - $this->db->setQuery($query); - $this->db->execute(); - } - // clear the values array - $this->existingCustomCode = array(); - } + // set notice that we could not get a valid string from the target + $this->app->enqueueMessage( + JText::sprintf('%s Warning
', __CLASS__), 'Error' + ); + $this->app->enqueueMessage( + JText::sprintf( + 'Use of a deprecated method (%s)!', __METHOD__ + ), 'Error' + ); } /** @@ -9574,79 +9391,11 @@ class Get * @param string $today The date for today * * @return void - * + * @deprecated 3.3 Use CFactory::_('Customcode.Extractor')->run(); */ protected function customCodeFactory(&$paths, &$today) { - // we must first store the current working directory - $joomla = getcwd(); - $counter = array(1 => 0, 2 => 0); - // file types to get - $fileTypes = array('\.php', '\.js', '\.xml'); - - // set some local placeholders - $placeholders = array_flip( - $this->globalPlaceholders - ); - $placeholders[StringHelper::safe( - CFactory::_('Config')->component_code_name, 'F' - ) . 'Helper::'] - = Placefix::_('Component') . 'Helper::'; - $placeholders['COM_' . StringHelper::safe( - CFactory::_('Config')->component_code_name, 'U' - )] - = 'COM_' . Placefix::_('COMPONENT'); - $placeholders['com_' . CFactory::_('Config')->component_code_name] = 'com_' . Placefix::_('component'); - // putt the last first - $placeholders = array_reverse($placeholders, true); - - foreach ($paths as $target => $path) - { - // we are changing the working directory to the component path - chdir($path); - foreach ($fileTypes as $type) - { - // get a list of files in the current directory tree (only PHP, JS and XML for now) - $files = Folder::files('.', $type, true, true); - // check if files found - if (ArrayHelper::check($files)) - { - foreach ($files as $file) - { - $this->searchFileContent( - $counter, $file, $target, - $this->customCodePlaceholders, $placeholders, $today - ); - // insert new code - if (ArrayHelper::check( - $this->newCustomCode - )) - { - $this->setNewCustomCode(100); - } - // update existing custom code - if (ArrayHelper::check( - $this->existingCustomCode - )) - { - $this->setExistingCustomCode(30); - } - } - } - } - } - // change back to Joomla working directory - chdir($joomla); - // make sure all code is stored - if (ArrayHelper::check($this->newCustomCode)) - { - $this->setNewCustomCode(); - } - // update existing custom code - if (ArrayHelper::check($this->existingCustomCode)) - { - $this->setExistingCustomCode(); - } + CFactory::_('Customcode.Extractor')->run(); } /** @@ -9660,406 +9409,23 @@ class Get * * @return array on success * + * @deprecated 3.3 */ protected function searchFileContent(&$counter, &$file, &$target, &$searchArray, &$placeholders, &$today ) { - // we add a new search for the GUI CODE Blocks - CFactory::_('Customcode.Gui')->search($file, $placeholders, $today, $target); - // reset each time per file - $loadEndFingerPrint = false; - $endFingerPrint = array(); - $fingerPrint = array(); - $codeBucket = array(); - $pointer = array(); - $reading = array(); - $reader = 0; - // reset found Start type - $commentType = 0; - // make sure we have the path correct (the script file is not in admin path for example) - // there may be more... will nead to keep our eye on this... since files could be moved during install - $file = str_replace('./', '', $file); # TODO (windows path issues) - if ($file !== 'script.php') - { - $path = $target . '/' . $file; - } - else - { - $path = $file; - } - // now we go line by line - foreach (new SplFileObject($file) as $lineNumber => $lineContent) - { - // we musk keep last few lines to dynamic find target entry later - $fingerPrint[$lineNumber] = trim($lineContent); - // load the end fingerprint - if ($loadEndFingerPrint) - { - $endFingerPrint[$lineNumber] = trim($lineContent); - } - foreach ($searchArray as $type => $search) - { - $i = (int) ($type == 3 || $type == 4) ? 2 : 1; - $_type = (int) ($type == 1 || $type == 3) ? 1 : 2; - if ($reader === 0 || $reader === $i) - { - $targetKey = $type; - $start = '/***[' . $search . '***/'; - $end = '/***[/' . $search . '***/'; - $startHTML = ''; - $endHTML = ''; - // check if the ending place holder was found - if (isset($reading[$targetKey]) && $reading[$targetKey] - && ((trim($lineContent) === $end - || strpos( - $lineContent, $end - ) !== false) - || (trim($lineContent) === $endHTML - || strpos( - $lineContent, $endHTML - ) !== false))) - { - // trim the placeholder and if there is still data then load it - if (isset($endReplace) - && ($_line - = $this->addLineChecker( - $endReplace, 2, $lineContent - )) !== false) - { - $codeBucket[$pointer[$targetKey]][] = $_line; - } - // deactivate the reader - $reading[$targetKey] = false; - if ($_type == 2) - { - // deactivate search - $reader = 0; - } - else - { - // activate fingerPrint for replacement end target - $loadEndFingerPrint = true; - $backupTargetKey = $targetKey; - $backupI = $i; - } - // all new records we can do a bulk insert - if ($i === 1) - { - // end the bucket info for this code block - $this->newCustomCode[$pointer[$targetKey]][] - = $this->db->quote( - (int) $lineNumber - ); // 'toline' - // first reverse engineer this code block - $c0de = CFactory::_('Placeholder.Reverse')->engine( - implode('', $codeBucket[$pointer[$targetKey]]), - $placeholders, $target - ); - $this->newCustomCode[$pointer[$targetKey]][] - = $this->db->quote( - base64_encode($c0de) - ); // 'code' - if ($_type == 2) - { - // load the last value - $this->newCustomCode[$pointer[$targetKey]][] - = $this->db->quote(0); // 'hashendtarget' - } - } - // the record already exist so we must update instead - elseif ($i === 2) - { - // end the bucket info for this code block - $this->existingCustomCode[$pointer[$targetKey]]['fields'][] - = $this->db->quoteName('to_line') . ' = ' - . $this->db->quote($lineNumber); - // first reverse engineer this code block - $c0de = CFactory::_('Placeholder.Reverse')->engine( - implode('', $codeBucket[$pointer[$targetKey]]), - $placeholders, $target, - $this->existingCustomCode[$pointer[$targetKey]]['id'] - ); - $this->existingCustomCode[$pointer[$targetKey]]['fields'][] - = $this->db->quoteName('code') . ' = ' - . $this->db->quote(base64_encode($c0de)); - if ($_type == 2) - { - // load the last value - $this->existingCustomCode[$pointer[$targetKey]]['fields'][] - = $this->db->quoteName('hashendtarget') - . ' = ' . $this->db->quote(0); - } - } - } - // check if the endfingerprint is ready to save - if (count((array) $endFingerPrint) === 3) - { - $hashendtarget = '3__' . md5( - implode('', $endFingerPrint) - ); - // all new records we can do a bulk insert - if ($i === 1) - { - // load the last value - $this->newCustomCode[$pointer[$targetKey]][] - = $this->db->quote( - $hashendtarget - ); // 'hashendtarget' - } - // the record already exist so we must use module to update - elseif ($i === 2) - { - $this->existingCustomCode[$pointer[$targetKey]]['fields'][] - = $this->db->quoteName('hashendtarget') . ' = ' - . $this->db->quote($hashendtarget); - } - // reset the needed values - $endFingerPrint = array(); - $loadEndFingerPrint = false; - // deactivate reader (to allow other search) - $reader = 0; - } - // then read in the code - if (isset($reading[$targetKey]) && $reading[$targetKey]) - { - $codeBucket[$pointer[$targetKey]][] = $lineContent; - } - // see if the custom code line starts now with PHP/JS comment type - if ((!isset($reading[$targetKey]) || !$reading[$targetKey]) - && (($i === 1 && trim($lineContent) === $start) - || strpos($lineContent, $start) !== false)) - { - $commentType = 1; // PHP/JS type - $startReplace = $start; - $endReplace = $end; - } - // see if the custom code line starts now with HTML comment type - elseif ((!isset($reading[$targetKey]) - || !$reading[$targetKey]) - && (($i === 1 && trim($lineContent) === $startHTML) - || strpos($lineContent, $startHTML) !== false)) - { - $commentType = 2; // HTML type - $startReplace = $startHTML; - $endReplace = $endHTML; - } - // check if the starting place holder was found - if ($commentType > 0) - { - // if we have all on one line we have a problem (don't load it TODO) - if (strpos($lineContent, $endReplace) !== false) - { - // reset found comment type - $commentType = 0; - $this->app->enqueueMessage( - JText::_('Custom Codes Warning
'), - 'Warning' - ); - $this->app->enqueueMessage( - JText::sprintf( - 'We found dynamic code all in one line, and ignored it! Please review (%s) for more details!', - $path - ), 'Warning' - ); - continue; - } - // do a quick check to insure we have an id - $id = false; - if ($i === 2) - { - $id = $this->getSystemID( - $lineContent, - array(1 => $start, 2 => $startHTML), - $commentType - ); - } - if ($i === 2 && $id > 0) - { - // make sure we update it only once even if found again. - if (isset($this->codeAreadyDone[$id])) - { - // reset found comment type - $commentType = 0; - continue; - } - // store the id to avoid duplication - $this->codeAreadyDone[$id] = (int) $id; - } - // start replace - $startReplace = $this->setStartReplace( - $id, $commentType, $startReplace - ); - // set active reader (to lock out other search) - $reader = $i; - // set pointer - $pointer[$targetKey] = $counter[$i]; - // activate the reader - $reading[$targetKey] = true; - // start code bucket - $codeBucket[$pointer[$targetKey]] = array(); - // trim the placeholder and if there is still data then load it - if ($_line = $this->addLineChecker( - $startReplace, 1, $lineContent - )) - { - $codeBucket[$pointer[$targetKey]][] = $_line; - } - // get the finger print around the custom code - $inFinger = count($fingerPrint); - $getFinger = $inFinger - 1; - $hasharray = array_slice( - $fingerPrint, -$inFinger, $getFinger, true - ); - $hasleng = count($hasharray); - $hashtarget = $hasleng . '__' . md5( - implode('', $hasharray) - ); - // for good practice - ComponentbuilderHelper::fixPath($path); - // all new records we can do a bulk insert - if ($i === 1 || !$id) - { - // start the bucket for this code - $this->newCustomCode[$pointer[$targetKey]] - = array(); - $this->newCustomCode[$pointer[$targetKey]][] - = $this->db->quote( - $path - ); // 'path' - $this->newCustomCode[$pointer[$targetKey]][] - = $this->db->quote( - (int) $_type - ); // 'type' - $this->newCustomCode[$pointer[$targetKey]][] - = $this->db->quote( - 1 - ); // 'target' - $this->newCustomCode[$pointer[$targetKey]][] - = $this->db->quote( - $commentType - ); // 'comment_type' - $this->newCustomCode[$pointer[$targetKey]][] - = $this->db->quote( - (int) CFactory::_('Config')->component_id - ); // 'component' - $this->newCustomCode[$pointer[$targetKey]][] - = $this->db->quote( - 1 - ); // 'published' - $this->newCustomCode[$pointer[$targetKey]][] - = $this->db->quote( - $today - ); // 'created' - $this->newCustomCode[$pointer[$targetKey]][] - = $this->db->quote( - (int) $this->user->id - ); // 'created_by' - $this->newCustomCode[$pointer[$targetKey]][] - = $this->db->quote( - 1 - ); // 'version' - $this->newCustomCode[$pointer[$targetKey]][] - = $this->db->quote( - 1 - ); // 'access' - $this->newCustomCode[$pointer[$targetKey]][] - = $this->db->quote( - $hashtarget - ); // 'hashtarget' - $this->newCustomCode[$pointer[$targetKey]][] - = $this->db->quote( - (int) $lineNumber - ); // 'fromline' - } - // the record already exist so we must update instead - elseif ($i === 2 && $id > 0) - { - // start the bucket for this code - $this->existingCustomCode[$pointer[$targetKey]] - = array(); - $this->existingCustomCode[$pointer[$targetKey]]['id'] - = (int) $id; - $this->existingCustomCode[$pointer[$targetKey]]['conditions'] - = array(); - $this->existingCustomCode[$pointer[$targetKey]]['conditions'][] - = $this->db->quoteName('id') . ' = ' - . $this->db->quote($id); - $this->existingCustomCode[$pointer[$targetKey]]['fields'] - = array(); - $this->existingCustomCode[$pointer[$targetKey]]['fields'][] - = $this->db->quoteName('path') . ' = ' - . $this->db->quote($path); - $this->existingCustomCode[$pointer[$targetKey]]['fields'][] - = $this->db->quoteName('type') . ' = ' - . $this->db->quote($_type); - $this->existingCustomCode[$pointer[$targetKey]]['fields'][] - = $this->db->quoteName('comment_type') . ' = ' - . $this->db->quote($commentType); - $this->existingCustomCode[$pointer[$targetKey]]['fields'][] - = $this->db->quoteName('component') . ' = ' - . $this->db->quote(CFactory::_('Config')->component_id); - $this->existingCustomCode[$pointer[$targetKey]]['fields'][] - = $this->db->quoteName('from_line') . ' = ' - . $this->db->quote($lineNumber); - $this->existingCustomCode[$pointer[$targetKey]]['fields'][] - = $this->db->quoteName('modified') . ' = ' - . $this->db->quote($today); - $this->existingCustomCode[$pointer[$targetKey]]['fields'][] - = $this->db->quoteName('modified_by') . ' = ' - . $this->db->quote($this->user->id); - $this->existingCustomCode[$pointer[$targetKey]]['fields'][] - = $this->db->quoteName('hashtarget') . ' = ' - . $this->db->quote($hashtarget); - } - else // this should actualy never happen - { - // de activate the reader - $reading[$targetKey] = false; - $reader = 0; - } - // reset found comment type - $commentType = 0; - // update the counter - $counter[$i]++; - } - } - } - // make sure only a few lines is kept at a time - if (count((array) $fingerPrint) > 10) - { - $fingerPrint = array_slice($fingerPrint, -6, 6, true); - } - } - // if the code is at the end of the page and there were not three more lines - if (count((array) $endFingerPrint) > 0 || $loadEndFingerPrint) - { - if (count((array) $endFingerPrint) > 0) - { - $leng = count($endFingerPrint); - $hashendtarget = $leng . '__' . md5( - implode('', $endFingerPrint) - ); - } - else - { - $hashendtarget = 0; - } - // all new records we can do a buldk insert - if ($backupI === 1) - { - // load the last value - $this->newCustomCode[$pointer[$backupTargetKey]][] - = $this->db->quote($hashendtarget); // 'hashendtarget' - } - // the record already exist so we must use module to update - elseif ($backupI === 2) - { - $this->existingCustomCode[$pointer[$backupTargetKey]]['fields'][] - = $this->db->quoteName('hashendtarget') . ' = ' - . $this->db->quote($hashendtarget); - } - } + // set notice that we could not get a valid string from the target + $this->app->enqueueMessage( + JText::sprintf('%s Warning
', __CLASS__), 'Error' + ); + $this->app->enqueueMessage( + JText::sprintf( + 'Use of a deprecated method (%s)!', __METHOD__ + ), 'Error' + ); + + return []; } /** @@ -10072,55 +9438,7 @@ class Get */ protected function setDynamicHASHING($script) { - // check if we should hash a string - if (strpos($script, 'HASHSTRING((((') !== false) - { - // get the strings - $values = GetHelper::allBetween( - $script, 'HASHSTRING((((', '))))' - ); - $locker = array(); - // convert them - foreach ($values as $value) - { - $locker['HASHSTRING((((' . $value . '))))'] - = md5($value); - } - - // update the script - return CFactory::_('Placeholder')->update($script, $locker); - } - // check if we should hash a file - if (strpos($script, 'HASHFILE((((') !== false) - { - // get the strings - $values = GetHelper::allBetween( - $script, 'HASHFILE((((', '))))' - ); - $locker = array(); - // convert them - foreach ($values as $path) - { - // we first get the file if it exist - if ($value = FileHelper::getContent($path)) - { - // now we hash the file content - $locker['HASHFILE((((' . $path . '))))'] - = md5($value); - } - else - { - // could not retrieve the file so we show error - $locker['HASHFILE((((' . $path . '))))'] - = 'ERROR'; - } - } - - // update the script - return CFactory::_('Placeholder')->update($script, $locker); - } - - return $script; + return CFactory::_('Customcode.Hash')->set($script); } /** @@ -10133,30 +9451,7 @@ class Get */ protected function setBase64LOCK($script) { - if (strpos($script, 'LOCKBASE64((((') !== false) - { - // get the strings - $values = GetHelper::allBetween( - $script, 'LOCKBASE64((((', '))))' - ); - $locker = array(); - // convert them - foreach ($values as $value) - { - $locker['LOCKBASE64((((' . $value . '))))'] - = "base64_decode( preg_replace('/\s+/', ''," . - PHP_EOL . Indent::_(2) . "'" . - wordwrap( - base64_encode($value), 64, PHP_EOL . Indent::_(2), true - ) . - "'))"; - } - - // update the script - return CFactory::_('Placeholder')->update($script, $locker); - } - - return $script; + return CFactory::_('Customcode.LockBase')->set($script); } /** @@ -10184,15 +9479,17 @@ class Get */ protected function canAddGuiCodePlaceholder(&$code) { - // check for customcode placeholders - if (strpos($code, '$$$$') !== false) - { - // we do not add GUI wrapper placeholder to code - // that already has any customcode placeholders - return false; - } + // set notice that we could not get a valid string from the target + $this->app->enqueueMessage( + JText::sprintf('%s Warning
', __CLASS__), 'Error' + ); + $this->app->enqueueMessage( + JText::sprintf( + 'Use of a deprecated method (%s)!', __METHOD__ + ), 'Error' + ); - return true; + return false; } /** @@ -10220,29 +9517,19 @@ class Get * * @return bool true on success * + * @deprecated 3.3 */ protected function addLineChecker($replaceKey, $type, $lineContent) { - $check = explode($replaceKey, $lineContent); - switch ($type) - { - case 1: - // beginning of code - $i = trim($check[1]); - if (StringHelper::check($i)) - { - return $check[1]; - } - break; - case 2: - // end of code - $i = trim($check[0]); - if (StringHelper::check($i)) - { - return $check[0]; - } - break; - } + // set notice that we could not get a valid string from the target + $this->app->enqueueMessage( + JText::sprintf('%s Warning
', __CLASS__), 'Error' + ); + $this->app->enqueueMessage( + JText::sprintf( + 'Use of a deprecated method (%s)!', __METHOD__ + ), 'Error' + ); return false; } @@ -10256,23 +9543,21 @@ class Get * * @return array on success * + * @deprecated 3.3 */ protected function setStartReplace($id, $commentType, $startReplace) { - if ($id > 0) - { - switch ($commentType) - { - case 1: // the PHP & JS type - $startReplace .= '/*' . $id . '*/'; - break; - case 2: // the HTML type - $startReplace .= ''; - break; - } - } + // set notice that we could not get a valid string from the target + $this->app->enqueueMessage( + JText::sprintf('%s Warning
', __CLASS__), 'Error' + ); + $this->app->enqueueMessage( + JText::sprintf( + 'Use of a deprecated method (%s)!', __METHOD__ + ), 'Error' + ); - return $startReplace; + return []; } /** @@ -10282,33 +9567,23 @@ class Get * @param string $placeholders The values to search for * @param int $commentType The comment type * - * @return array on success + * @return int on success * + * @deprecated 3.3 */ protected function getSystemID(&$lineContent, $placeholders, $commentType) { - $trim = '/'; - if ($commentType == 2) - { - $trim = ' PHP/JS ---#################################### + * + * New Insert Code = /xxx[INSERT>$$$$]xxx/ /xxx[/INSERT>$$$$]xxx/ + * New Replace Code = /xxx[REPLACE>$$$$]xxx/ /xxx[/REPLACE>$$$$]xxx/ + * + * //////////////////////////////// when JCB adds it back ////////////////////////////////// + * JCB Add Inserted Code = /xxx[INSERTED$$$$]xxx//xx23xx/ /xxx[/INSERTED$$$$]xxx/ + * JCB Add Replaced Code = /xxx[REPLACED$$$$]xxx//xx25xx/ /xxx[/REPLACED$$$$]xxx/ + * + * /////////////////////////////// changeing existing custom code ///////////////////////// + * Update Inserted Code = /xxx[INSERTED>$$$$]xxx//xx23xx/ /xxx[/INSERTED>$$$$]xxx/ + * Update Replaced Code = /xxx[REPLACED>$$$$]xxx//xx25xx/ /xxx[/REPLACED>$$$$]xxx/ + * + * The custom script placeholders - we use the (==) to avoid detection it should be (--) + * ###################################---> HTML ---##################################### + * + * New Insert Code = !==[INSERT>$$$$]==> !==[/INSERT>$$$$]==> + * New Replace Code = !==[REPLACE>$$$$]==> !==[/REPLACE>$$$$]==> + * + * ///////////////////////////////// when JCB adds it back /////////////////////////////// + * JCB Add Inserted Code = + * JCB Add Replaced Code = + * + * //////////////////////////// changeing existing custom code /////////////////////////// + * Update Inserted Code = !==[INSERTED>$$$$]==> !==[/INSERTED>$$$$]==> + * Update Replaced Code = !==[REPLACED>$$$$]==> !==[/REPLACED>$$$$]==> + * + * ////////23 is the ID of the code in the system don't change it!!!!!!!!!!!!!!!!!!!!!!!!!! + * + * More info read: https://git.vdm.dev/joomla/Component-Builder/wiki/TIPS:-Custom-Code + * + * @since 3.2.0 + */ +class Extractor +{ + /** + * The placeholder keys + * + * @var array + * @since 3.2.0 + */ + protected array $PKeys + = [ + 1 => 'REPLACE<>$$$$]', + 2 => 'INSERT<>$$$$]', + 3 => 'REPLACED<>$$$$]', + 4 => 'INSERTED<>$$$$]' + ]; + + /** + * The custom code in local files that already exist in system + * + * @var array + * @since 3.2.0 + */ + protected array $existing = []; + + /** + * The custom code in local files that are new + * + * @var array + * @since 3.2.0 + */ + protected array $new = []; + + /** + * The index of code already loaded + * + * @var array + * @since 3.2.0 + */ + protected array $done = []; + + /** + * The search counter + * + * @var array + * @since 3.2.0 + */ + protected array $counter = [1 => 0, 2 => 0]; + + /** + * The file types to search + * + * @var array + * @since 3.2.0 + */ + protected array $fileTypes = ['\.php', '\.js', '\.xml']; + + /** + * The local placeholders + * + * @var array + * @since 3.2.0 + */ + protected array $placeholders; + + /** + * Today's date in SQL format + * + * @var string + * @since 3.2.0 + */ + protected string $today; + + /** + * Compiler Config + * + * @var Config + * @since 3.2.0 + **/ + protected Config $config; + + /** + * Compiler Customcode Gui + * + * @var Gui + * @since 3.2.0 + **/ + protected Gui $gui; + + /** + * Compiler Customcode Extractor Paths + * + * @var Paths + * @since 3.2.0 + **/ + protected Paths $paths; + + /** + * Compiler Placeholder Reverse + * + * @var Reverse + * @since 3.2.0 + **/ + protected Reverse $reverse; + + /** + * Compiler Component Placeholder + * + * @var Placeholder + * @since 3.2.0 + **/ + protected Placeholder $componentPlaceholder; + + /** + * Current User Object + * + * @var User + * @since 3.2.0 + **/ + protected User $user; + + /** + * Database object to query local DB + * + * @var \JDatabaseDriver + * @since 3.2.0 + **/ + protected \JDatabaseDriver $db; + + /** + * Database object to query local DB + * + * @var CMSApplication + * @since 3.2.0 + **/ + protected CMSApplication $app; + + /** + * Constructor. + * + * @param Config|null $config The compiler config object. + * @param Gui|null $gui The compiler customcode gui object. + * @param Paths|null $paths The compiler customcode extractor paths object. + * @param Reverse|null $reverse The compiler placeholder reverse object. + * @param Placeholder|null $placeholder The compiler component placeholder object. + * @param User|null $user The current User object. + * @param \JDatabaseDriver|null $db The Database Driver object. + * @param CMSApplication|null $app The CMS Application object. + * + * @throws \Exception + * @since 3.2.0 + */ + public function __construct(?Config $config = null, ?Gui $gui = null, ?Paths $paths = null, + ?Reverse $reverse = null, ?Placeholder $placeholder = null, + ?User $user = null, ?\JDatabaseDriver $db = null, ?CMSApplication $app = null) + { + $this->config = $config ?: Compiler::_('Config'); + $this->gui = $gui ?: Compiler::_('Customcode.Gui'); + $this->paths = $paths ?: Compiler::_('Customcode.Extractor.Paths'); + $this->reverse = $reverse ?: Compiler::_('Placeholder.Reverse'); + $this->componentPlaceholder = $placeholder ?: Compiler::_('Component.Placeholder'); + $this->user = $user ?: Factory::getUser(); + $this->db = $db ?: Factory::getDbo(); + $this->app = $app ?: Factory::getApplication(); + + // set today's date + $this->today = Factory::getDate()->toSql(); + + // set some local placeholders + $placeholders = array_flip( + $this->componentPlaceholder->get() + ); + + $placeholders[StringHelper::safe( + $this->config->component_code_name, 'F' + ) . 'Helper::'] = Placefix::_('Component') . 'Helper::'; + + $placeholders['COM_' . StringHelper::safe( + $this->config->component_code_name, 'U' + )] = 'COM_' . Placefix::_('COMPONENT'); + + $placeholders['com_' . $this->config->component_code_name] = 'com_' . Placefix::_('component'); + + // set the local placeholders + $this->placeholders = array_reverse($placeholders, true); + } + + /** + * get the custom code from the local files + * + * @return void + * @since 3.2.0 + */ + public function run() + { + // we must first store the current working directory + $joomla = getcwd(); + + foreach ($this->paths->active as $target => $path) + { + // we are changing the working directory to the component path + chdir($path); + foreach ($this->fileTypes as $type) + { + // get a list of files in the current directory tree (only PHP, JS and XML for now) + $files = Folder::files('.', $type, true, true); + + // check if files found + if (ArrayHelper::check($files)) + { + foreach ($files as $file) + { + // search the file + $this->searchFileContent($file, $target); + + // insert new code + $this->insert(100); + + // update existing custom code + $this->update(30); + } + } + } + } + + // change back to Joomla working directory + chdir($joomla); + + // make sure all code is stored + $this->insert(); + // update existing custom code + $this->update(); + } + + /** + * search a file for placeholders and store result + * + * @param string $file The file path to search + * + * @return array on success + * @since 3.2.0 + */ + protected function searchFileContent(&$file, &$target) + { + // we add a new search for the GUI CODE Blocks + $this->gui->search($file, $this->placeholders, $this->today, $target); + + // reset each time per file + $loadEndFingerPrint = false; + $endFingerPrint = []; + $fingerPrint = []; + $codeBucket = []; + $pointer = []; + $reading = []; + $reader = 0; + + // reset found Start type + $commentType = 0; + + // make sure we have the path correct (the script file is not in admin path for example) + // there may be more... will nead to keep our eye on this... since files could be moved during install + $file = str_replace('./', '', $file); # TODO (windows path issues) + + if ($file !== 'script.php') + { + $path = $target . '/' . $file; + } + else + { + $path = $file; + } + + // now we go line by line + foreach (new \SplFileObject($file) as $lineNumber => $lineContent) + { + // we must keep last few lines to dynamic find target entry later + $fingerPrint[$lineNumber] = trim($lineContent); + + // load the end fingerprint + if ($loadEndFingerPrint) + { + $endFingerPrint[$lineNumber] = trim($lineContent); + } + + foreach ($this->PKeys as $type => $search) + { + $i = (int) ($type == 3 || $type == 4) ? 2 : 1; + $_type = (int) ($type == 1 || $type == 3) ? 1 : 2; + + if ($reader === 0 || $reader === $i) + { + $targetKey = $type; + + $start = '/***[' . $search . '***/'; + $end = '/***[/' . $search . '***/'; + $startHTML = ''; + $endHTML = ''; + + // check if the ending placeholder was found + if (isset($reading[$targetKey]) && $reading[$targetKey] + && ((trim($lineContent) === $end + || strpos($lineContent, $end) !== false) + || (trim($lineContent) === $endHTML + || strpos($lineContent, $endHTML) !== false))) + { + // trim the placeholder and if there is still data then load it + if (isset($endReplace) + && ($_line = $this->addLineChecker($endReplace, 2, $lineContent)) !== false) + { + $codeBucket[$pointer[$targetKey]][] = $_line; + } + + // deactivate the reader + $reading[$targetKey] = false; + + if ($_type == 2) + { + // deactivate search + $reader = 0; + } + else + { + // activate fingerPrint for replacement end target + $loadEndFingerPrint = true; + $backupTargetKey = $targetKey; + $backupI = $i; + } + + // all new records we can do a bulk insert + if ($i === 1) + { + // end the bucket info for this code block + $this->new[$pointer[$targetKey]][] + = $this->db->quote( + (int) $lineNumber + ); // 'toline' + + // first reverse engineer this code block + $c0de = $this->reverse->engine( + implode('', $codeBucket[$pointer[$targetKey]]), + $this->placeholders, $target + ); + + $this->new[$pointer[$targetKey]][] + = $this->db->quote( + base64_encode($c0de) + ); // 'code' + + if ($_type == 2) + { + // load the last value + $this->new[$pointer[$targetKey]][] + = $this->db->quote(0); // 'hashendtarget' + } + } + // the record already exist so we must update instead + elseif ($i === 2) + { + // end the bucket info for this code block + $this->existing[$pointer[$targetKey]]['fields'][] + = $this->db->quoteName('to_line') . ' = ' + . $this->db->quote($lineNumber); + + // first reverse engineer this code block + $c0de = $this->reverse->engine( + implode('', $codeBucket[$pointer[$targetKey]]), + $this->placeholders, $target, + $this->existing[$pointer[$targetKey]]['id'] + ); + + $this->existing[$pointer[$targetKey]]['fields'][] + = $this->db->quoteName('code') . ' = ' + . $this->db->quote(base64_encode($c0de)); + + if ($_type == 2) + { + // load the last value + $this->existing[$pointer[$targetKey]]['fields'][] + = $this->db->quoteName('hashendtarget') + . ' = ' . $this->db->quote(0); + } + } + } + + // check if the endfingerprint is ready to save + if (count((array) $endFingerPrint) === 3) + { + $hashendtarget = '3__' . md5( + implode('', $endFingerPrint) + ); + + // all new records we can do a bulk insert + if ($i === 1) + { + // load the last value + $this->new[$pointer[$targetKey]][] + = $this->db->quote( + $hashendtarget + ); // 'hashendtarget' + } + // the record already exist so we must update + elseif ($i === 2) + { + $this->existing[$pointer[$targetKey]]['fields'][] + = $this->db->quoteName('hashendtarget') . ' = ' + . $this->db->quote($hashendtarget); + } + + // reset the needed values + $endFingerPrint = []; + $loadEndFingerPrint = false; + + // deactivate reader (to allow other search) + $reader = 0; + } + + // then read in the code + if (isset($reading[$targetKey]) && $reading[$targetKey]) + { + $codeBucket[$pointer[$targetKey]][] = $lineContent; + } + + // see if the custom code line starts now with PHP/JS comment type + if ((!isset($reading[$targetKey]) || !$reading[$targetKey]) + && (($i === 1 && trim($lineContent) === $start) + || strpos($lineContent, $start) !== false)) + { + $commentType = 1; // PHP/JS type + $startReplace = $start; + $endReplace = $end; + } + // see if the custom code line starts now with HTML comment type + elseif ((!isset($reading[$targetKey]) + || !$reading[$targetKey]) + && (($i === 1 && trim($lineContent) === $startHTML) + || strpos($lineContent, $startHTML) !== false)) + { + $commentType = 2; // HTML type + $startReplace = $startHTML; + $endReplace = $endHTML; + } + + // check if the starting place holder was found + if ($commentType > 0) + { + // if we have all on one line we have a problem (don't load it TODO) + if (strpos($lineContent, $endReplace) !== false) + { + // reset found comment type + $commentType = 0; + $this->app->enqueueMessage( + Text::_('COM_COMPONENTBUILDER_HR_HTHREECUSTOM_CODES_WARNINGHTHREE'), + 'Warning' + ); + $this->app->enqueueMessage( + Text::sprintf('COM_COMPONENTBUILDER_WE_FOUND_DYNAMIC_CODE_BALL_IN_ONE_LINEB_AND_IGNORED_IT_PLEASE_REVIEW_S_FOR_MORE_DETAILS', + $path + ), 'Warning' + ); + continue; + } + + // do a quick check to insure we have an id + $id = false; + if ($i === 2) + { + $id = $this->getSystemID( + $lineContent, + array(1 => $start, 2 => $startHTML), + $commentType + ); + } + + if ($i === 2 && $id > 0) + { + // make sure we update it only once even if found again. + if (isset($this->done[$id])) + { + // reset found comment type + $commentType = 0; + continue; + } + // store the id to avoid duplication + $this->done[$id] = (int) $id; + } + + // start replace + $startReplace = $this->setStartReplace( + $id, $commentType, $startReplace + ); + + // set active reader (to lock out other search) + $reader = $i; + + // set pointer + $pointer[$targetKey] = $this->counter[$i]; + + // activate the reader + $reading[$targetKey] = true; + + // start code bucket + $codeBucket[$pointer[$targetKey]] = []; + + // trim the placeholder and if there is still data then load it + if ($_line = $this->addLineChecker( + $startReplace, 1, $lineContent + )) + { + $codeBucket[$pointer[$targetKey]][] = $_line; + } + + // get the finger print around the custom code + $inFinger = count($fingerPrint); + $getFinger = $inFinger - 1; + $hasharray = array_slice( + $fingerPrint, -$inFinger, $getFinger, true + ); + $hasleng = count($hasharray); + $hashtarget = $hasleng . '__' . md5( + implode('', $hasharray) + ); + + // for good practice + Path::fix($path); + + // all new records we can do a bulk insert + if ($i === 1 || !$id) + { + // start the bucket for this code + $this->new[$pointer[$targetKey]] = []; + $this->new[$pointer[$targetKey]][] + = $this->db->quote( + $path + ); // 'path' + + $this->new[$pointer[$targetKey]][] + = $this->db->quote( + (int) $_type + ); // 'type' + + $this->new[$pointer[$targetKey]][] + = $this->db->quote( + 1 + ); // 'target' + + $this->new[$pointer[$targetKey]][] + = $this->db->quote( + $commentType + ); // 'comment_type' + + $this->new[$pointer[$targetKey]][] + = $this->db->quote( + (int) $this->config->component_id + ); // 'component' + + $this->new[$pointer[$targetKey]][] + = $this->db->quote( + 1 + ); // 'published' + + $this->new[$pointer[$targetKey]][] + = $this->db->quote( + $this->today + ); // 'created' + + $this->new[$pointer[$targetKey]][] + = $this->db->quote( + (int) $this->user->id + ); // 'created_by' + + $this->new[$pointer[$targetKey]][] + = $this->db->quote( + 1 + ); // 'version' + + $this->new[$pointer[$targetKey]][] + = $this->db->quote( + 1 + ); // 'access' + + $this->new[$pointer[$targetKey]][] + = $this->db->quote( + $hashtarget + ); // 'hashtarget' + + $this->new[$pointer[$targetKey]][] + = $this->db->quote( + (int) $lineNumber + ); // 'fromline' + + } + // the record already exist so we must update instead + elseif ($i === 2 && $id > 0) + { + // start the bucket for this code + $this->existing[$pointer[$targetKey]] = []; + $this->existing[$pointer[$targetKey]]['id'] + = (int) $id; + $this->existing[$pointer[$targetKey]]['conditions'] = []; + $this->existing[$pointer[$targetKey]]['conditions'][] + = $this->db->quoteName('id') . ' = ' + . $this->db->quote($id); + $this->existing[$pointer[$targetKey]]['fields'] = []; + $this->existing[$pointer[$targetKey]]['fields'][] + = $this->db->quoteName('path') . ' = ' + . $this->db->quote($path); + $this->existing[$pointer[$targetKey]]['fields'][] + = $this->db->quoteName('type') . ' = ' + . $this->db->quote($_type); + $this->existing[$pointer[$targetKey]]['fields'][] + = $this->db->quoteName('comment_type') . ' = ' + . $this->db->quote($commentType); + $this->existing[$pointer[$targetKey]]['fields'][] + = $this->db->quoteName('component') . ' = ' + . $this->db->quote($this->config->component_id); + $this->existing[$pointer[$targetKey]]['fields'][] + = $this->db->quoteName('from_line') . ' = ' + . $this->db->quote($lineNumber); + $this->existing[$pointer[$targetKey]]['fields'][] + = $this->db->quoteName('modified') . ' = ' + . $this->db->quote($this->today); + $this->existing[$pointer[$targetKey]]['fields'][] + = $this->db->quoteName('modified_by') . ' = ' + . $this->db->quote($this->user->id); + $this->existing[$pointer[$targetKey]]['fields'][] + = $this->db->quoteName('hashtarget') . ' = ' + . $this->db->quote($hashtarget); + } + else // this should actualy never happen + { + // de activate the reader + $reading[$targetKey] = false; + $reader = 0; + } + + // reset found comment type + $commentType = 0; + // update the counter + $this->counter[$i]++; + } + } + } + + // make sure only a few lines is kept at a time + if (count((array) $fingerPrint) > 10) + { + $fingerPrint = array_slice($fingerPrint, -6, 6, true); + } + } + + // if the code is at the end of the page and there were not three more lines + if (count((array) $endFingerPrint) > 0 || $loadEndFingerPrint) + { + if (count((array) $endFingerPrint) > 0) + { + $leng = count($endFingerPrint); + $hashendtarget = $leng . '__' . md5( + implode('', $endFingerPrint) + ); + } + else + { + $hashendtarget = 0; + } + + // all new records we can do a buldk insert + if ($backupI === 1) + { + // load the last value + $this->new[$pointer[$backupTargetKey]][] + = $this->db->quote($hashendtarget); // 'hashendtarget' + } + // the record already exist so we must use module to update + elseif ($backupI === 2) + { + $this->existing[$pointer[$backupTargetKey]]['fields'][] + = $this->db->quoteName('hashendtarget') . ' = ' + . $this->db->quote($hashendtarget); + } + } + } + + /** + * Insert the code + * + * @param int $when To set when to update + * + * @return void + * @since 3.2.0 + */ + protected function insert(int $when = 1) + { + if (ArrayHelper::check($this->new) >= $when) + { + // Create a new query object. + $query = $this->db->getQuery(true); + $continue = false; + // Insert columns. + $columns = array('path', 'type', 'target', 'comment_type', + 'component', 'published', 'created', 'created_by', + 'version', 'access', 'hashtarget', 'from_line', + 'to_line', 'code', 'hashendtarget'); + // Prepare the insert query. + $query->insert( + $this->db->quoteName('#__componentbuilder_custom_code') + ); + $query->columns($this->db->quoteName($columns)); + foreach ($this->new as $values) + { + if (count((array) $values) == 15) + { + $query->values(implode(',', $values)); + $continue = true; + } + else + { + // TODO line mismatch... should not happen + } + } + // clear the values array + $this->new = []; + if (!$continue) + { + return; // insure we don't continue if no values were loaded + } + // Set the query using our newly populated query object and execute it. + $this->db->setQuery($query); + $this->db->execute(); + } + } + + /** + * Update the code + * + * @param int $when To set when to update + * + * @return void + * @since 3.2.0 + */ + protected function update(int $when = 1) + { + if (ArrayHelper::check($this->existing) >= $when) + { + foreach ($this->existing as $code) + { + // Create a new query object. + $query = $this->db->getQuery(true); + // Prepare the update query. + $query->update( + $this->db->quoteName('#__componentbuilder_custom_code') + )->set($code['fields'])->where($code['conditions']); + // Set the query using our newly populated query object and execute it. + $this->db->setQuery($query); + $this->db->execute(); + } + // clear the values array + $this->existing = []; + } + } + + /** + * set the start replace placeholder + * + * @param int $id The comment id + * @param int $commentType The comment type + * @param string $startReplace The main replace string + * + * @return string on success + * @since 3.2.0 + */ + protected function setStartReplace(int $id, int $commentType, string $startReplace): string + { + if ($id > 0) + { + switch ($commentType) + { + case 1: // the PHP & JS type + $startReplace .= '/*' . $id . '*/'; + break; + case 2: // the HTML type + $startReplace .= ''; + break; + } + } + + return $startReplace; + } + + /** + * Check if this line should be added + * + * @param string $replaceKey The key to remove from line + * @param int $type The line type + * @param string $lineContent The line to check + * + * @return bool|int true on success + * @since 3.2.0 + */ + protected function addLineChecker(string $replaceKey, int $type, string $lineContent) + { + $check = explode($replaceKey, $lineContent); + switch ($type) + { + case 1: + // beginning of code + if (isset($check[1]) && StringHelper::check($check[1])) + { + return trim($check[1]); + } + break; + case 2: + // end of code + if (isset($check[0]) && StringHelper::check($check[0])) + { + return trim($check[0]); + } + break; + } + + return false; + } + + /** + * search for the system id in the line given + * + * @param string $lineContent The file path to search + * @param array $placeholders The values to search for + * @param int $commentType The comment type + * + * @return mixed on success + * @since 3.2.0 + */ + protected function getSystemID(string &$lineContent, array $placeholders, int $commentType) + { + $trim = '/'; + if ($commentType == 2) + { + $trim = '