* @github Joomla Component Builder * @copyright Copyright (C) 2015 - 2020 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 the component builder autoloader ComponentbuilderHelper::autoLoader(); /** * Compiler class */ class Compiler extends Infusion { /** * The Temp path * * @var string */ public $tempPath; /** * The timer * * @var string */ private $time_start; private $time_end; public $secondsCompiled; /** * The file path array * * @var string */ public $filepath = array( 'component' => '', 'component-folder' => '', 'package' => '', 'plugins' => array(), 'plugins-folders' => array(), 'modules' => array() ); // fixed pathes protected $dynamicIntegration = false; protected $backupPath = false; protected $repoPath = false; protected $addCustomCodeAt = array(); /** * Constructor */ public function __construct($config = array()) { // to check the compiler speed $this->time_start = microtime(true); // first we run the perent constructors if (parent::__construct($config)) { // set temp directory $comConfig = JFactory::getConfig(); $this->tempPath = $comConfig->get('tmp_path'); // set some folder paths in relation to distribution if ($config['backup']) { $this->backupPath = $this->params->get( 'backup_folder_path', $this->tempPath ); $this->dynamicIntegration = true; } // set local repos switch if ($config['repository']) { $this->repoPath = $this->params->get('git_folder_path', null); } // remove site folder if not needed (TODO add check if custom script was moved to site folder then we must do a more complex cleanup here) if ($this->removeSiteFolder && $this->removeSiteEditFolder) { // first remove the files and folders $this->removeFolder($this->componentPath . '/site'); // clear form component xml $xmlPath = $this->componentPath . '/' . $this->fileContentStatic[$this->hhh . 'component' . $this->hhh] . '.xml'; $componentXML = ComponentbuilderHelper::getFileContents( $xmlPath ); $textToSite = ComponentbuilderHelper::getBetween( $componentXML, '', '' ); $textToSiteLang = ComponentbuilderHelper::getBetween( $componentXML, '', '' ); $componentXML = str_replace( array('' . $textToSite . "", '' . $textToSiteLang . ""), array('', ''), $componentXML ); $this->writeFile($xmlPath, $componentXML); } // Trigger Event: jcb_ce_onBeforeUpdateFiles $this->triggerEvent( 'jcb_ce_onBeforeUpdateFiles', array(&$this->componentContext, &$this) ); // now update the files if (!$this->updateFiles()) { return false; } // Trigger Event: jcb_ce_onBeforeGetCustomCode $this->triggerEvent( 'jcb_ce_onBeforeGetCustomCode', array(&$this->componentContext, &$this) ); // now insert into the new files if ($this->getCustomCode()) { // Trigger Event: jcb_ce_onBeforeAddCustomCode $this->triggerEvent( 'jcb_ce_onBeforeAddCustomCode', array(&$this->componentContext, &$this) ); $this->addCustomCode(); } // Trigger Event: jcb_ce_onBeforeSetLangFileData $this->triggerEvent( 'jcb_ce_onBeforeSetLangFileData', array(&$this->componentContext, &$this) ); // set the lang data now $this->setLangFileData(); // set the language notice if it was set if (ComponentbuilderHelper::checkArray($this->langNot) || ComponentbuilderHelper::checkArray($this->langSet)) { if (ComponentbuilderHelper::checkArray($this->langNot)) { $this->app->enqueueMessage( JText::_('

Language Warning

'), 'Warning' ); foreach ($this->langNot as $tag => $percentage) { $this->app->enqueueMessage( JText::sprintf( 'The %s language has %s% translated, you will need to translate %s% of the language strings before it will be added.', $tag, $percentage, $this->percentageLanguageAdd ), 'Warning' ); } $this->app->enqueueMessage( JText::_('

Language Notice

'), 'Notice' ); $this->app->enqueueMessage( JText::sprintf( 'You can change this percentage of translated strings required in the global options of JCB.
Please watch this tutorial for more help surrounding the JCB translations manager.', '"https://youtu.be/zzAcVkn_cWU?list=PLQRGFI8XZ_wtGvPQZWBfDzzlERLQgpMRE" target="_blank" title="JCB Tutorial surrounding Translation Manager"' ), 'Notice' ); } // set why the strings were added $whyAddedLang = JText::sprintf( 'because more then %s% of the strings have been translated.', $this->percentageLanguageAdd ); if ($this->debugLinenr) { $whyAddedLang = JText::_( 'because the debugging mode is on. (debug line numbers)' ); } // show languages that were added if (ComponentbuilderHelper::checkArray($this->langSet)) { $this->app->enqueueMessage( JText::_('

Language Notice

'), 'Notice' ); foreach ($this->langSet as $tag => $percentage) { $this->app->enqueueMessage( JText::sprintf( 'The %s language has %s% translated. Was addeded %s', $tag, $percentage, $whyAddedLang ), 'Notice' ); } } } // move the update server into place $this->setUpdateServer(); // set the global counters $this->setCountingStuff(); // build read me $this->buildReadMe(); // set local repos $this->setLocalRepos(); // zip the component if (!$this->zipComponent()) { // done with error return false; } // if there are modules zip them $this->zipModules(); // if there are plugins zip them $this->zipPlugins(); // do lang mismatch check if (ComponentbuilderHelper::checkArray($this->langMismatch)) { if (ComponentbuilderHelper::checkArray($this->langMatch)) { $mismatch = array_diff( array_unique($this->langMismatch), array_unique($this->langMatch) ); } else { $mismatch = array_unique($this->langMismatch); } // set a notice if we have a mismatch if (isset($mismatch) && ComponentbuilderHelper::checkArray( $mismatch )) { $this->app->enqueueMessage( JText::_('

Language Warning

'), 'Warning' ); if (count((array) $mismatch) > 1) { $this->app->enqueueMessage( JText::_( '

Please check the following mismatching Joomla.JText language constants.

' ), 'Warning' ); } else { $this->app->enqueueMessage( JText::_( '

Please check the following mismatch Joomla.JText language constant.

' ), 'Warning' ); } // add the mismatching issues foreach ($mismatch as $string) { $constant = $this->langPrefix . '_' . ComponentbuilderHelper::safeString($string, 'U'); $this->app->enqueueMessage( JText::sprintf( 'The Joomla.JText._('%s') language constant for %s does not have a corresponding JText::script('%s') decalaration, please add it.', $constant, $string, $string ), 'Warning' ); } } } // check if we should add a EXTERNALCODE notice if (ComponentbuilderHelper::checkArray($this->externalCodeString)) { // number of external code strings $externalCount = count($this->externalCodeString); // the correct string $externalCodeString = ($externalCount == 1) ? JText::_( 'code/string' ) : JText::_('code/strings'); // the notice $this->app->enqueueMessage( JText::_('

External Code Notice

'), 'Notice' ); $this->app->enqueueMessage( JText::sprintf( 'There has been %s - %s added to this component as EXTERNALCODE. To avoid shipping your component with malicious %s always make sure that the correct code/string values were used.', $externalCount, $externalCodeString, $externalCodeString ), 'Notice' ); } // end the timer here $this->time_end = microtime(true); $this->secondsCompiled = $this->time_end - $this->time_start; // completed the compilation return true; } return false; } /** * Set the line number in comments * * @param int $nr The line number * * @return void * */ private function setLine($nr) { if ($this->debugLinenr) { return ' [Compiler ' . $nr . ']'; } return ''; } /** * Set the dynamic data to the created fils * * @return bool true on success * */ protected function updateFiles() { if (isset($this->newFiles['static']) && ComponentbuilderHelper::checkArray($this->newFiles['static']) && isset($this->newFiles['dynamic']) && ComponentbuilderHelper::checkArray($this->newFiles['dynamic'])) { // get the bom file $bom = ComponentbuilderHelper::getFileContents($this->bomPath); // first we do the static files foreach ($this->newFiles['static'] as $static) { if (JFile::exists($static['path'])) { $this->setFileContent( $static['name'], $static['path'], $bom ); } } // now we do the dynamic files foreach ($this->newFiles['dynamic'] as $view => $files) { if (isset($this->fileContentDynamic[$view]) && ComponentbuilderHelper::checkArray( $this->fileContentDynamic[$view] )) { foreach ($files as $file) { if ($file['view'] == $view) { if (JFile::exists($file['path'])) { $this->setFileContent( $file['name'], $file['path'], $bom, $file['view'] ); } } } } // free up some memory unset($this->fileContentDynamic[$view]); } // free up some memory unset($this->newFiles['dynamic']); // do modules if found if (ComponentbuilderHelper::checkArray($this->joomlaModules)) { foreach ($this->joomlaModules as $module) { if (ComponentbuilderHelper::checkObject($module) && isset($this->newFiles[$module->key]) && ComponentbuilderHelper::checkArray( $this->newFiles[$module->key] )) { // move field or rule if needed if (isset($module->fields_rules_paths) && $module->fields_rules_paths == 2) { // check the config fields if (isset($module->config_fields) && ComponentbuilderHelper::checkArray( $module->config_fields )) { foreach ( $module->config_fields as $field_name => $fieldsets ) { foreach ($fieldsets as $fieldset => $fields) { foreach ($fields as $field) { $this->moveFieldsRules( $field, $module->folder_path ); } } } } // check the fieldsets if (isset($module->form_files) && ComponentbuilderHelper::checkArray( $module->form_files )) { foreach ($module->form_files as $file => $files) { foreach ( $files as $field_name => $fieldsets ) { foreach ( $fieldsets as $fieldset => $fields ) { foreach ($fields as $field) { $this->moveFieldsRules( $field, $module->folder_path ); } } } } } } // update the module files foreach ($this->newFiles[$module->key] as $module_file) { if (JFile::exists($module_file['path'])) { $this->setFileContent( $module_file['name'], $module_file['path'], $bom, $module->key ); } } // free up some memory unset($this->newFiles[$module->key]); unset($this->fileContentDynamic[$module->key]); } } } // do plugins if found if (ComponentbuilderHelper::checkArray($this->joomlaPlugins)) { foreach ($this->joomlaPlugins as $plugin) { if (ComponentbuilderHelper::checkObject($plugin) && isset($this->newFiles[$plugin->key]) && ComponentbuilderHelper::checkArray( $this->newFiles[$plugin->key] )) { // move field or rule if needed if (isset($plugin->fields_rules_paths) && $plugin->fields_rules_paths == 2) { // check the config fields if (isset($plugin->config_fields) && ComponentbuilderHelper::checkArray( $plugin->config_fields )) { foreach ( $plugin->config_fields as $field_name => $fieldsets ) { foreach ($fieldsets as $fieldset => $fields) { foreach ($fields as $field) { $this->moveFieldsRules( $field, $plugin->folder_path ); } } } } // check the fieldsets if (isset($plugin->form_files) && ComponentbuilderHelper::checkArray( $plugin->form_files )) { foreach ($plugin->form_files as $file => $files) { foreach ( $files as $field_name => $fieldsets ) { foreach ( $fieldsets as $fieldset => $fields ) { foreach ($fields as $field) { $this->moveFieldsRules( $field, $plugin->folder_path ); } } } } } } // update the plugin files foreach ($this->newFiles[$plugin->key] as $plugin_file) { if (JFile::exists($plugin_file['path'])) { $this->setFileContent( $plugin_file['name'], $plugin_file['path'], $bom, $plugin->key ); } } // free up some memory unset($this->newFiles[$plugin->key]); unset($this->fileContentDynamic[$plugin->key]); } } } return true; } return false; } /** * set the file content * * @return void * */ protected function setFileContent(&$name, &$path, &$bom, $view = null) { // Trigger Event: jcb_ce_onBeforeSetFileContent $this->triggerEvent( 'jcb_ce_onBeforeSetFileContent', array(&$this->componentContext, &$name, &$path, &$bom, &$view) ); // set the file name $this->fileContentStatic[$this->hhh . 'FILENAME' . $this->hhh] = $name; // check if the file should get PHP opening $php = ''; if (ComponentbuilderHelper::checkFileType($name, 'php')) { $php = "triggerEvent( 'jcb_ce_onGetFileContents', array(&$this->componentContext, &$string, &$name, &$path, &$bom, &$view) ); // see if we should add a BOM if (strpos($string, $this->hhh . 'BOM' . $this->hhh) !== false) { list($wast, $code) = explode( $this->hhh . 'BOM' . $this->hhh, $string ); $string = $php . $bom . $code; } // set the answer $answer = $this->setPlaceholders($string, $this->fileContentStatic, 3); // set the dynamic answer if ($view) { $answer = $this->setPlaceholders( $answer, $this->fileContentDynamic[$view], 3 ); } // check if this file needs extra care :) if (isset($this->updateFileContent[$path])) { $answer = $this->setDynamicValues($answer); } // Trigger Event: jcb_ce_onBeforeSetFileContent $this->triggerEvent( 'jcb_ce_onBeforeWriteFileContent', array(&$this->componentContext, &$answer, &$name, &$path, &$bom, &$view) ); // add answer back to file $this->writeFile($path, $answer); // count the file lines $this->lineCount = $this->lineCount + substr_count($answer, PHP_EOL); } /** * move the local update server xml file to a remote ftp server * * @return void * */ protected function setUpdateServer() { // move the component update server to host if ($this->componentData->add_update_server == 1 && $this->componentData->update_server_target == 1 && isset($this->updateServerFileName) && $this->dynamicIntegration) { $update_server_xml_path = $this->componentPath . '/' . $this->updateServerFileName . '.xml'; // make sure we have the correct file if (JFile::exists($update_server_xml_path) && isset($this->componentData->update_server)) { // move to server ComponentbuilderHelper::moveToServer( $update_server_xml_path, $this->updateServerFileName . '.xml', (int) $this->componentData->update_server, $this->componentData->update_server_protocol ); // remove the local file JFile::delete($update_server_xml_path); } } // move the plugins update server to host if (ComponentbuilderHelper::checkArray($this->joomlaPlugins)) { foreach ($this->joomlaPlugins as $plugin) { if (ComponentbuilderHelper::checkObject($plugin) && isset($plugin->add_update_server) && $plugin->add_update_server == 1 && isset($plugin->update_server_target) && $plugin->update_server_target == 1 && isset($plugin->update_server) && is_numeric($plugin->update_server) && $plugin->update_server > 0 && isset($plugin->update_server_xml_path) && JFile::exists($plugin->update_server_xml_path) && isset($plugin->update_server_xml_file_name) && ComponentbuilderHelper::checkString( $plugin->update_server_xml_file_name )) { // move to server ComponentbuilderHelper::moveToServer( $plugin->update_server_xml_path, $plugin->update_server_xml_file_name, (int) $plugin->update_server, $plugin->update_server_protocol ); // remove the local file JFile::delete($plugin->update_server_xml_path); } } } } // link canges made to views into the file license protected function fixLicenseValues($data) { // check if these files have its own config data) if (isset($data['config']) && ComponentbuilderHelper::checkArray( $data['config'] ) && $this->componentData->mvc_versiondate == 1) { foreach ($data['config'] as $key => $value) { if ($this->hhh . 'VERSION' . $this->hhh === $key) { // hmm we sould in some way make it known that this version number // is not in relation the the project but to the file only... any ideas? // this is the best for now... if (1 == $value) { $value = '@first version of this MVC'; } else { $value = '@update number ' . $value . ' of this MVC'; } } $this->fileContentStatic[$key] = $value; } return true; } // else insure to reset to global $this->fileContentStatic[$this->hhh . 'CREATIONDATE' . $this->hhh] = $this->fileContentStatic[$this->hhh . 'CREATIONDATE' . $this->hhh . 'GLOBAL']; $this->fileContentStatic[$this->hhh . 'BUILDDATE' . $this->hhh] = $this->fileContentStatic[$this->hhh . 'BUILDDATE' . $this->hhh . 'GLOBAL']; $this->fileContentStatic[$this->hhh . 'VERSION' . $this->hhh] = $this->fileContentStatic[$this->hhh . 'VERSION' . $this->hhh . 'GLOBAL']; } // set all global numbers protected function setCountingStuff() { // what is the size in terms of an A4 book $this->pageCount = round($this->lineCount / 56); // setup the unrealistic numbers $this->folderSeconds = $this->folderCount * 5; $this->fileSeconds = $this->fileCount * 5; $this->lineSeconds = $this->lineCount * 10; $this->seconds = $this->folderSeconds + $this->fileSeconds + $this->lineSeconds; $this->totalHours = round($this->seconds / 3600); $this->totalDays = round($this->totalHours / 8); // setup the more realistic numbers $this->secondsDebugging = $this->seconds / 4; $this->secondsPlanning = $this->seconds / 7; $this->secondsMapping = $this->seconds / 10; $this->secondsOffice = $this->seconds / 6; $this->actualSeconds = $this->folderSeconds + $this->fileSeconds + $this->lineSeconds + $this->secondsDebugging + $this->secondsPlanning + $this->secondsMapping + $this->secondsOffice; $this->actualTotalHours = round($this->actualSeconds / 3600); $this->actualTotalDays = round($this->actualTotalHours / 8); $this->debuggingHours = round($this->secondsDebugging / 3600); $this->planningHours = round($this->secondsPlanning / 3600); $this->mappingHours = round($this->secondsMapping / 3600); $this->officeHours = round($this->secondsOffice / 3600); // the actual time spent $this->actualHoursSpent = $this->actualTotalHours - $this->totalHours; $this->actualDaysSpent = $this->actualTotalDays - $this->totalDays; // calculate the projects actual time frame of completion $this->projectWeekTime = round($this->actualTotalDays / 5, 1); $this->projectMonthTime = round($this->actualTotalDays / 24, 1); } private function buildReadMe() { // do a final run to update the readme file $two = 0; foreach ($this->newFiles['static'] as $static) { if (('README.md' === $static['name'] || 'README.txt' === $static['name']) && $this->componentData->addreadme && JFile::exists($static['path'])) { $this->setReadMe($static['path']); $two++; } if ($two == 2) { break; } } unset($this->newFiles['static']); } private function setReadMe($path) { // set readme data if not set already if (!isset( $this->fileContentStatic[$this->hhh . 'LINE_COUNT' . $this->hhh] ) || $this->fileContentStatic[$this->hhh . 'LINE_COUNT' . $this->hhh] != $this->lineCount) { $this->buildReadMeData(); } // get the file $string = ComponentbuilderHelper::getFileContents($path); // update the file $answer = $this->setPlaceholders($string, $this->fileContentStatic); // add to zip array $this->writeFile($path, $answer); } private function buildReadMeData() { // set some defaults $this->fileContentStatic[$this->hhh . 'LINE_COUNT' . $this->hhh] = $this->lineCount; $this->fileContentStatic[$this->hhh . 'FIELD_COUNT' . $this->hhh] = $this->fieldCount; $this->fileContentStatic[$this->hhh . 'FILE_COUNT' . $this->hhh] = $this->fileCount; $this->fileContentStatic[$this->hhh . 'FOLDER_COUNT' . $this->hhh] = $this->folderCount; $this->fileContentStatic[$this->hhh . 'PAGE_COUNT' . $this->hhh] = $this->pageCount; $this->fileContentStatic[$this->hhh . 'folders' . $this->hhh] = $this->folderSeconds; $this->fileContentStatic[$this->hhh . 'foldersSeconds' . $this->hhh] = $this->folderSeconds; $this->fileContentStatic[$this->hhh . 'files' . $this->hhh] = $this->fileSeconds; $this->fileContentStatic[$this->hhh . 'filesSeconds' . $this->hhh] = $this->fileSeconds; $this->fileContentStatic[$this->hhh . 'lines' . $this->hhh] = $this->lineSeconds; $this->fileContentStatic[$this->hhh . 'linesSeconds' . $this->hhh] = $this->lineSeconds; $this->fileContentStatic[$this->hhh . 'seconds' . $this->hhh] = $this->actualSeconds; $this->fileContentStatic[$this->hhh . 'actualSeconds' . $this->hhh] = $this->actualSeconds; $this->fileContentStatic[$this->hhh . 'totalHours' . $this->hhh] = $this->totalHours; $this->fileContentStatic[$this->hhh . 'totalDays' . $this->hhh] = $this->totalDays; $this->fileContentStatic[$this->hhh . 'debugging' . $this->hhh] = $this->secondsDebugging; $this->fileContentStatic[$this->hhh . 'secondsDebugging' . $this->hhh] = $this->secondsDebugging; $this->fileContentStatic[$this->hhh . 'planning' . $this->hhh] = $this->secondsPlanning; $this->fileContentStatic[$this->hhh . 'secondsPlanning' . $this->hhh] = $this->secondsPlanning; $this->fileContentStatic[$this->hhh . 'mapping' . $this->hhh] = $this->secondsMapping; $this->fileContentStatic[$this->hhh . 'secondsMapping' . $this->hhh] = $this->secondsMapping; $this->fileContentStatic[$this->hhh . 'office' . $this->hhh] = $this->secondsOffice; $this->fileContentStatic[$this->hhh . 'secondsOffice' . $this->hhh] = $this->secondsOffice; $this->fileContentStatic[$this->hhh . 'actualTotalHours' . $this->hhh] = $this->actualTotalHours; $this->fileContentStatic[$this->hhh . 'actualTotalDays' . $this->hhh] = $this->actualTotalDays; $this->fileContentStatic[$this->hhh . 'debuggingHours' . $this->hhh] = $this->debuggingHours; $this->fileContentStatic[$this->hhh . 'planningHours' . $this->hhh] = $this->planningHours; $this->fileContentStatic[$this->hhh . 'mappingHours' . $this->hhh] = $this->mappingHours; $this->fileContentStatic[$this->hhh . 'officeHours' . $this->hhh] = $this->officeHours; $this->fileContentStatic[$this->hhh . 'actualHoursSpent' . $this->hhh] = $this->actualHoursSpent; $this->fileContentStatic[$this->hhh . 'actualDaysSpent' . $this->hhh] = $this->actualDaysSpent; $this->fileContentStatic[$this->hhh . 'projectWeekTime' . $this->hhh] = $this->projectWeekTime; $this->fileContentStatic[$this->hhh . 'projectMonthTime' . $this->hhh] = $this->projectMonthTime; } private function setLocalRepos() { // move it to the repo folder if set if (isset($this->repoPath) && ComponentbuilderHelper::checkString( $this->repoPath )) { // set the repo path $repoFullPath = $this->repoPath . '/com_' . $this->componentData->sales_name . '__joomla_' . $this->joomlaVersion; // Trigger Event: jcb_ce_onBeforeUpdateRepo $this->triggerEvent( 'jcb_ce_onBeforeUpdateRepo', array(&$this->componentContext, &$this->componentPath, &$repoFullPath, &$this->componentData) ); // remove old data $this->removeFolder($repoFullPath, $this->componentData->toignore); // set the new data JFolder::copy($this->componentPath, $repoFullPath, '', true); // Trigger Event: jcb_ce_onAfterUpdateRepo $this->triggerEvent( 'jcb_ce_onAfterUpdateRepo', array(&$this->componentContext, &$this->componentPath, &$repoFullPath, &$this->componentData) ); // move the modules to local folder repos if (ComponentbuilderHelper::checkArray($this->joomlaModules)) { foreach ($this->joomlaModules as $module) { if (ComponentbuilderHelper::checkObject($module) && isset($module->file_name)) { $module_context = 'module.' . $module->file_name . '.' . $module->id; // set the repo path $repoFullPath = $this->repoPath . '/' . $module->folder_name . '__joomla_' . $this->joomlaVersion; // Trigger Event: jcb_ce_onBeforeUpdateRepo $this->triggerEvent( 'jcb_ce_onBeforeUpdateRepo', array(&$module_context, &$module->folder_path, &$repoFullPath, &$module) ); // remove old data $this->removeFolder( $repoFullPath, $this->componentData->toignore ); // set the new data JFolder::copy( $module->folder_path, $repoFullPath, '', true ); // Trigger Event: jcb_ce_onAfterUpdateRepo $this->triggerEvent( 'jcb_ce_onAfterUpdateRepo', array(&$module_context, &$module->folder_path, &$repoFullPath, &$module) ); } } } // move the plugins to local folder repos if (ComponentbuilderHelper::checkArray($this->joomlaPlugins)) { foreach ($this->joomlaPlugins as $plugin) { if (ComponentbuilderHelper::checkObject($plugin) && isset($plugin->file_name)) { $plugin_context = 'plugin.' . $plugin->file_name . '.' . $plugin->id; // set the repo path $repoFullPath = $this->repoPath . '/' . $plugin->folder_name . '__joomla_' . $this->joomlaVersion; // Trigger Event: jcb_ce_onBeforeUpdateRepo $this->triggerEvent( 'jcb_ce_onBeforeUpdateRepo', array(&$plugin_context, &$plugin->folder_path, &$repoFullPath, &$plugin) ); // remove old data $this->removeFolder( $repoFullPath, $this->componentData->toignore ); // set the new data JFolder::copy( $plugin->folder_path, $repoFullPath, '', true ); // Trigger Event: jcb_ce_onAfterUpdateRepo $this->triggerEvent( 'jcb_ce_onAfterUpdateRepo', array(&$plugin_context, &$plugin->folder_path, &$repoFullPath, &$plugin) ); } } } } } private function zipComponent() { // Component Folder Name $this->filepath['component-folder'] = $this->componentFolderName; // the name of the zip file to create $this->filepath['component'] = $this->tempPath . '/' . $this->filepath['component-folder'] . '.zip'; // Trigger Event: jcb_ce_onBeforeZipComponent $this->triggerEvent( 'jcb_ce_onBeforeZipComponent', array(&$this->componentContext, &$this->componentPath, &$this->filepath['component'], &$this->tempPath, &$this->componentFolderName, &$this->componentData) ); //create the zip file if (ComponentbuilderHelper::zip( $this->componentPath, $this->filepath['component'] )) { // now move to backup if zip was made and backup is required if ($this->backupPath && $this->dynamicIntegration) { // Trigger Event: jcb_ce_onBeforeBackupZip $this->triggerEvent( 'jcb_ce_onBeforeBackupZip', array(&$this->componentContext, &$this->filepath['component'], &$this->tempPath, &$this->backupPath, &$this->componentData) ); // copy the zip to backup path JFile::copy( $this->filepath['component'], $this->backupPath . '/' . $this->componentBackupName . '.zip' ); } // move to sales server host if ($this->componentData->add_sales_server == 1 && $this->dynamicIntegration) { // make sure we have the correct file if (isset($this->componentData->sales_server)) { // Trigger Event: jcb_ce_onBeforeMoveToServer $this->triggerEvent( 'jcb_ce_onBeforeMoveToServer', array(&$this->componentContext, &$this->filepath['component'], &$this->tempPath, &$this->componentSalesName, &$this->componentData) ); // move to server ComponentbuilderHelper::moveToServer( $this->filepath['component'], $this->componentSalesName . '.zip', (int) $this->componentData->sales_server, $this->componentData->sales_server_protocol ); } } // Trigger Event: jcb_ce_onAfterZipComponent $this->triggerEvent( 'jcb_ce_onAfterZipComponent', array(&$this->componentContext, &$this->filepath['component'], &$this->tempPath, &$this->componentFolderName, &$this->componentData) ); // remove the component folder since we are done if ($this->removeFolder($this->componentPath)) { return true; } } return false; } private function zipModules() { if (ComponentbuilderHelper::checkArray($this->joomlaModules)) { foreach ($this->joomlaModules as $module) { if (ComponentbuilderHelper::checkObject($module) && isset($module->zip_name) && ComponentbuilderHelper::checkString($module->zip_name) && isset($module->folder_path) && ComponentbuilderHelper::checkString( $module->folder_path )) { // set module context $module_context = $module->file_name . '.' . $module->id; // Component Folder Name $this->filepath['modules-folder'][$module->id] = $module->zip_name; // the name of the zip file to create $this->filepath['modules'][$module->id] = $this->tempPath . '/' . $module->zip_name . '.zip'; // Trigger Event: jcb_ce_onBeforeZipModule $this->triggerEvent( 'jcb_ce_onBeforeZipModule', array(&$module_context, &$module->folder_path, &$this->filepath['modules'][$module->id], &$this->tempPath, &$module->zip_name, &$module) ); //create the zip file if (ComponentbuilderHelper::zip( $module->folder_path, $this->filepath['modules'][$module->id] )) { // now move to backup if zip was made and backup is required if ($this->backupPath) { $__module_context = 'module.' . $module_context; // Trigger Event: jcb_ce_onBeforeBackupZip $this->triggerEvent( 'jcb_ce_onBeforeBackupZip', array(&$__module_context, &$this->filepath['modules'][$module->id], &$this->tempPath, &$this->backupPath, &$module) ); // copy the zip to backup path JFile::copy( $this->filepath['modules'][$module->id], $this->backupPath . '/' . $module->zip_name . '.zip' ); } // move to sales server host if ($module->add_sales_server == 1) { // make sure we have the correct file if (isset($module->sales_server)) { // Trigger Event: jcb_ce_onBeforeMoveToServer $this->triggerEvent( 'jcb_ce_onBeforeMoveToServer', array(&$__module_context, &$this->filepath['modules'][$module->id], &$this->tempPath, &$module->zip_name, &$module) ); // move to server ComponentbuilderHelper::moveToServer( $this->filepath['modules'][$module->id], $module->zip_name . '.zip', (int) $module->sales_server, $module->sales_server_protocol ); } } // Trigger Event: jcb_ce_onAfterZipModule $this->triggerEvent( 'jcb_ce_onAfterZipModule', array(&$module_context, &$this->filepath['modules'][$module->id], &$this->tempPath, &$module->zip_name, &$module) ); // remove the module folder since we are done $this->removeFolder($module->folder_path); } } } } } private function zipPlugins() { if (ComponentbuilderHelper::checkArray($this->joomlaPlugins)) { foreach ($this->joomlaPlugins as $plugin) { if (ComponentbuilderHelper::checkObject($plugin) && isset($plugin->zip_name) && ComponentbuilderHelper::checkString($plugin->zip_name) && isset($plugin->folder_path) && ComponentbuilderHelper::checkString( $plugin->folder_path )) { // set plugin context $plugin_context = $plugin->file_name . '.' . $plugin->id; // Component Folder Name $this->filepath['plugins-folder'][$plugin->id] = $plugin->zip_name; // the name of the zip file to create $this->filepath['plugins'][$plugin->id] = $this->tempPath . '/' . $plugin->zip_name . '.zip'; // Trigger Event: jcb_ce_onBeforeZipPlugin $this->triggerEvent( 'jcb_ce_onBeforeZipPlugin', array(&$plugin_context, &$plugin->folder_path, &$this->filepath['plugins'][$plugin->id], &$this->tempPath, &$plugin->zip_name, &$plugin) ); //create the zip file if (ComponentbuilderHelper::zip( $plugin->folder_path, $this->filepath['plugins'][$plugin->id] )) { // now move to backup if zip was made and backup is required if ($this->backupPath) { $__plugin_context = 'plugin.' . $plugin_context; // Trigger Event: jcb_ce_onBeforeBackupZip $this->triggerEvent( 'jcb_ce_onBeforeBackupZip', array(&$__plugin_context, &$this->filepath['plugins'][$plugin->id], &$this->tempPath, &$this->backupPath, &$plugin) ); // copy the zip to backup path JFile::copy( $this->filepath['plugins'][$plugin->id], $this->backupPath . '/' . $plugin->zip_name . '.zip' ); } // move to sales server host if ($plugin->add_sales_server == 1) { // make sure we have the correct file if (isset($plugin->sales_server)) { // Trigger Event: jcb_ce_onBeforeMoveToServer $this->triggerEvent( 'jcb_ce_onBeforeMoveToServer', array(&$__plugin_context, &$this->filepath['plugins'][$plugin->id], &$this->tempPath, &$plugin->zip_name, &$plugin) ); // move to server ComponentbuilderHelper::moveToServer( $this->filepath['plugins'][$plugin->id], $plugin->zip_name . '.zip', (int) $plugin->sales_server, $plugin->sales_server_protocol ); } } // Trigger Event: jcb_ce_onAfterZipPlugin $this->triggerEvent( 'jcb_ce_onAfterZipPlugin', array(&$plugin_context, &$this->filepath['plugins'][$plugin->id], &$this->tempPath, &$plugin->zip_name, &$plugin) ); // remove the plugin folder since we are done $this->removeFolder($plugin->folder_path); } } } } } protected function addCustomCode() { // reset all these $this->clearFromPlaceHolders('view'); $this->clearFromPlaceHolders('arg'); foreach ($this->customCode as $nr => $target) { // reset each time per custom code $fingerPrint = array(); if (isset($target['hashtarget'][0]) && $target['hashtarget'][0] > 3 && isset($target['path']) && ComponentbuilderHelper::checkString($target['path']) && isset($target['hashtarget'][1]) && ComponentbuilderHelper::checkString( $target['hashtarget'][1] )) { $file = $this->componentPath . '/' . $target['path']; $size = (int) $target['hashtarget'][0]; $hash = $target['hashtarget'][1]; $cut = $size - 1; $found = false; $bites = 0; $lineBites = array(); $replace = array(); if ($target['type'] == 1 && isset($target['hashendtarget'][0]) && $target['hashendtarget'][0] > 0) { $foundEnd = false; $sizeEnd = (int) $target['hashendtarget'][0]; $hashEnd = $target['hashendtarget'][1]; $cutEnd = $sizeEnd - 1; } else { // replace to the end of the file $foundEnd = true; } $counter = 0; // check if file exist if (JFile::exists($file)) { foreach ( new SplFileObject($file) as $lineNumber => $lineContent ) { // if not found we need to load line bites per line $lineBites[$lineNumber] = (int) mb_strlen( $lineContent, '8bit' ); if (!$found) { $bites = (int) ComponentbuilderHelper::bcmath( 'add', $lineBites[$lineNumber], $bites ); } if ($found && !$foundEnd) { $replace[] = (int) $lineBites[$lineNumber]; // we musk keep last three lines to dynamic find target entry $fingerPrint[$lineNumber] = trim($lineContent); // check lines each time if it fits our target if (count((array) $fingerPrint) === $sizeEnd && !$foundEnd) { $fingerTest = md5(implode('', $fingerPrint)); if ($fingerTest === $hashEnd) { // we are done here $foundEnd = true; $replace = array_slice( $replace, 0, count($replace) - $sizeEnd ); break; } else { $fingerPrint = array_slice( $fingerPrint, -$cutEnd, $cutEnd, true ); } } continue; } if ($found && $foundEnd) { $replace[] = (int) $lineBites[$lineNumber]; } // we musk keep last three lines to dynamic find target entry $fingerPrint[$lineNumber] = trim($lineContent); // check lines each time if it fits our target if (count((array) $fingerPrint) === $size && !$found) { $fingerTest = md5(implode('', $fingerPrint)); if ($fingerTest === $hash) { // we are done here $found = true; // reset in case $fingerPrint = array(); // break if it is insertion if ($target['type'] == 2) { break; } } else { $fingerPrint = array_slice( $fingerPrint, -$cut, $cut, true ); } } } if ($found) { $placeholder = $this->getPlaceHolder( (int) $target['comment_type'] . $target['type'], $target['id'] ); $data = $placeholder['start'] . PHP_EOL . $this->setPlaceholders( $target['code'], $this->placeholders ) . $placeholder['end'] . PHP_EOL; if ($target['type'] == 2) { // found it now add code from the next line $this->addDataToFile($file, $data, $bites); } elseif ($target['type'] == 1 && $foundEnd) { // found it now add code from the next line $this->addDataToFile( $file, $data, $bites, (int) array_sum($replace) ); } else { // Load escaped code since the target endhash has changed $this->loadEscapedCode($file, $target, $lineBites); $this->app->enqueueMessage( JText::_('

Custom Code Warning

'), 'Warning' ); $this->app->enqueueMessage( JText::sprintf( 'Custom code %s could not be added to %s please review the file after install at line %s and reposition the code, remove the comments and recompile to fix the issue. The issue could be due to a change to lines below the custom code.', '#' . $target['id'] . '', $target['path'], $target['from_line'] ), 'Warning' ); } } else { // Load escaped code since the target hash has changed $this->loadEscapedCode($file, $target, $lineBites); $this->app->enqueueMessage( JText::_('

Custom Code Warning

'), 'Warning' ); $this->app->enqueueMessage( JText::sprintf( 'Custom code %s could not be added to %s please review the file after install at line %s and reposition the code, remove the comments and recompile to fix the issue. The issue could be due to a change to lines above the custom code.', '#' . $target['id'] . '', $target['path'], $target['from_line'] ), 'Warning' ); } } else { // Give developer a notice that file is not found. $this->app->enqueueMessage( JText::_('

Custom Code Warning

'), 'Warning' ); $this->app->enqueueMessage( JText::sprintf( 'File %s could not be found, so the custom code for this file could not be addded.', $target['path'] ), 'Warning' ); } } } } protected function loadEscapedCode($file, $target, $lineBites) { // get comment type if ($target['comment_type'] == 1) { $commentType = "// "; $_commentType = ""; } else { $commentType = ""; } // escape the code $code = explode(PHP_EOL, $target['code']); $code = PHP_EOL . $commentType . implode( $_commentType . PHP_EOL . $commentType, $code ) . $_commentType . PHP_EOL; // get place holders $placeholder = $this->getPlaceHolder( (int) $target['comment_type'] . $target['type'], $target['id'] ); // build the data $data = $placeholder['start'] . $code . $placeholder['end'] . PHP_EOL; // get the bites before insertion $bitBucket = array(); foreach ($lineBites as $line => $value) { if ($line < $target['from_line']) { $bitBucket[] = $value; } } // add to the file $this->addDataToFile($file, $data, (int) array_sum($bitBucket)); } // Thanks to http://stackoverflow.com/a/16813550/1429677 protected function addDataToFile($file, $data, $position, $replace = null) { // start the process $fpFile = fopen($file, "rw+"); $fpTemp = fopen('php://temp', "rw+"); // make a copy of the file stream_copy_to_stream($fpFile, $fpTemp); // move to the position where we should add the data fseek($fpFile, $position); // Add the data fwrite($fpFile, $data); // truncate file at the end of the data that was added $remove = ComponentbuilderHelper::bcmath( 'add', $position, mb_strlen($data, '8bit') ); ftruncate($fpFile, $remove); // check if this was a replacement of data if ($replace) { $position = ComponentbuilderHelper::bcmath( 'add', $position, $replace ); } // move to the position of the data that should remain below the new data fseek($fpTemp, $position); // copy that remaining data to the file stream_copy_to_stream($fpTemp, $fpFile); // @Jack // done close both files fclose($fpFile); fclose($fpTemp); // any help to improve this is welcome... } }