/** * Generate component * * @param component_id integer Id of the component to be generated * @param code_template_id integer Id of the template to use as source * @param output_path string Path to where the generated component will be stored * @param zip_format string format of the zip file, currently only 'zip' allowed. No zip if blank * @param logging integer 0 or 1 specfying whether a log file will be created * * @return true or false */ public function generateComponent($component_id, $code_template_id, $token, $output_path = 'tmp', $zip_format = '', $logging = 0) { $this->_token = $token; $this->_logging = $logging; // Generate can be a very long running process so if possible set the php max execution time so it does not expire if (function_exists('ini_set')) { ini_set('max_execution_time', '0'); // 0 = no limit. } // If logging requested then set up the log file. if ($this->_logging) { if (!$this->_progress->openLog()) { $error = array('message' => JText::_('COM_COMPONENTARCHITECT_GENERATE_ERROR_GEN0000_LOG_FILE_OPEN_FAILED'), 'errorcode' => 'gen0000'); $this->_progress->outputError($this->_token, $error); $this->_progress->completeProgress($this->_token); return false; } } $this->_search_replace_helper = new ComponentArchitectSearchReplaceHelper($this->_progress); $this->_search_replace_helper->setToken($this->_token); $this->_search_replace_helper->setLogging($this->_logging); if ($code_template_id > 0) { $code_template_model = JModelLegacy::getInstance('codetemplate', 'ComponentArchitectModel', array('ignore_request' => true)); $this->_code_template = $code_template_model->getItem($code_template_id); if ($this->_code_template === false) { $error = array('message' => JText::sprintf('COM_COMPONENTARCHITECT_GENERATE_ERROR_GEN0001_CANNOT_LOAD_COMPONENT', $code_template_id, $code_template_model->getError()), 'errorcode' => 'gen0001'); $this->_progress->outputError($this->_token, $error); $this->_progress->completeProgress($this->_token); return false; } // Double check that only limited punctuation is present in name as this may cause php code or sql problems $this->_code_template->template_component_name = preg_replace($this->_name_regex, '', $this->_code_template->template_component_name); $this->_code_template->template_object_name = preg_replace($this->_name_regex, '', $this->_code_template->template_object_name); } else { /* No code template id provided */ $error = array('message' => JText::_('COM_COMPONENTARCHITECT_GENERATE_ERROR_GEN0002_NO_CODE_TEMPLATE_SELECTED'), 'errorcode' => 'gen0002'); $this->_progress->outputError($this->_token, $error); $this->_progress->completeProgress($this->_token); return false; } if ($component_id > 0) { $component_model = JModelLegacy::getInstance('component', 'ComponentArchitectModel', array('ignore_request' => true)); $this->_component = $component_model->getItem($component_id); if ($this->_component === false) { $error = array('message' => JText::sprintf('COM_COMPONENTARCHITECT_GENERATE_ERROR_GEN0003_CANNOT_LOAD_COMPONENT', $component_id, $component_model->getError()), 'errorcode' => 'gen0003'); $this->_progress->outputError($this->_token, $error); $this->_progress->completeProgress($this->_token); return false; } // Double check that only limited punctuation is present in name as this may cause php code or sql problems $this->_component->name = preg_replace($this->_name_regex, '', $this->_component->name); $this->_component->set('search_replace_pairs', $this->_getComponentSearchPairs($this->_code_template->template_component_name, $this->_component)); } else { /* No component id provided */ $error = array('message' => JText::_('COM_COMPONENTARCHITECT_GENERATE_ERROR_GEN0004_NO_COMPONENT_SELECTED'), 'errorcode' => 'gen0004'); $this->_progress->outputError($this->_token, $error); $this->_progress->completeProgress($this->_token); return false; } // Initialise the Progress session data with the generate data $this->_progress->setInitialiseStage($this->_token, $logging, $this->_code_template->name, $this->_component->name); /* * Stage 1 - Analyse component */ $count_fields = (int) $this->_countFields($component_id); $this->_progress->setProgressStage($this->_token, 'stage_1', $count_fields); $this->_search_replace_helper->setTemplateComponentName(str_replace(" ", "", JString::strtolower($this->_code_template->template_component_name))); $this->_search_replace_helper->setTemplateObjectName(str_replace(" ", "", JString::strtolower($this->_code_template->template_object_name))); $this->_search_replace_helper->setMarkupPrefix($this->_code_template->template_markup_prefix); $this->_search_replace_helper->setMarkupSuffix($this->_code_template->template_markup_suffix); $this->_search_replace_helper->setComponentName(str_replace("_", "", str_replace(" ", "", JString::strtolower($this->_component->code_name)))); //Conditions are a generic set for the generate which may include other conditions besides Joomla! Features //Convert Joomla! Parts to conditions. $no_generate_array = array(); // 1st pass to find all high level conditions that are set to not generate foreach ($this->_component->get('joomla_parts') as $name => $value) { // Higher levels must be set to generate otherwise all levels below will need to be set to not generate if (($name == 'generate_admin' or $name == 'generate_site' or $name == 'generate_site_views' or $name == 'generate_categories' or $name == 'generate_plugins' or $name == 'generate_modules') and $value == '0') { $no_generate_array[] = $name; } } // 2nd pass to check each of the conditions against the no generate ones foreach ($this->_component->get('joomla_parts') as $name => $value) { if ($value == '1') { for ($i = 0; $i < count($no_generate_array); $i++) { if (JString::strpos($name, $no_generate_array[$i]) !== false) { $value = '0'; break; } } if ($value == '1' and (JString::strpos($name, 'generate_categories_site') !== false and in_array('generate_site', $no_generate_array) or JString::strpos($name, 'generate_site_layout') !== false and in_array('generate_site_views', $no_generate_array))) { $value = '0'; } } $this->_search_replace_helper->setComponentConditions($name, (int) $value); } //Convert Joomla! Features to conditions. foreach ($this->_component->get('joomla_features') as $name => $value) { $this->_search_replace_helper->setComponentConditions($name, (int) $value); } // Populate the Component Objects if (!$this->_getFieldTypes()) { $this->_progress->completeProgress($this->_token); return false; } $this->_component_objects = $this->_getComponentObjects($component_id, $this->_component->get('default_object_id'), $this->_code_template->template_component_name, $this->_code_template->template_object_name); if ($this->_component_objects === false) { /* Problem loading component objects */ $error = array('message' => JText::sprintf('COM_COMPONENTARCHITECT_GENERATE_ERROR_GEN0012_CANNOT_POPULATE_COMPONENT_OBJECTS', $this->_component->name), 'errorcode' => 'gen0012'); $this->_progress->outputError($this->_token, $error); $this->_progress->completeProgress($this->_token); return false; } $this->_search_replace_helper->setComponentObjects($this->_component_objects); $generic_language_vars = implode('', $this->_generic_values); // Save search replace pair for the all generic field values array_push($this->_component->search_replace_pairs, array('search' => $this->_markupText('COM_' . JString::strtoupper(str_replace(' ', '', $this->_code_template->template_component_name)) . '_GENERIC_FIELD_VALUES'), 'replace' => $generic_language_vars)); // Update Stage 1 progress as being complete if logging requested this will also create a log record $step = JText::_('COM_COMPONENTARCHITECT_GENERATE_END_STAGE_1'); $this->_progress->setProgress($this->_token, 'stage_1', $step, true); /* * Stage 2 - Create Files */ $this->_code_templates_root = JPATH_COMPONENT_ADMINISTRATOR . '/' . 'codetemplates'; $files_count = $this->_countFiles(JPath::clean($this->_code_templates_root . '/' . $this->_code_template->source_path)); $excluded_files_count = $this->_countExcludedFiles(JPath::clean($this->_code_templates_root . '/' . $this->_code_template->source_path)); $files_count = $files_count - $excluded_files_count; $this->_progress->setProgressStage($this->_token, 'stage_2', $files_count); $template_source_path = JPath::clean($this->_code_templates_root . '/' . $this->_code_template->source_path); $dir = opendir($template_source_path); while (false !== ($file = readdir($dir))) { if ($file != '.' and $file != '..' and !is_file($template_source_path . '/' . $file)) { $src_file = $file; $src_path = $template_source_path . '/' . $file; break; } } closedir($dir); $dst_file = str_replace(str_replace(" ", "", JString::strtolower($this->_code_template->template_component_name)), str_replace("_", "", str_replace(" ", "", JString::strtolower($this->_component->code_name))), $src_file); $dst_path = JPath::clean(JPATH_SITE . '/' . $output_path . '/' . $dst_file); // Call the main function to recursively make copies of the folders and files in the Code Template $this->_search_replace_helper->recursiveCopy($src_path, $dst_path); // Update Stage 2 progress as being complete if logging requested this will also create a log record $step = JText::_('COM_COMPONENTARCHITECT_GENERATE_END_STAGE_2'); $this->_progress->setProgress($this->_token, 'stage_2', $step, true); /* * Stage 3 - Search/Replace Markup */ $files_count = $this->_countFiles($dst_path); $this->_progress->setProgressStage($this->_token, 'stage_3', $files_count); // Initialise the search - replace parameters $this->_search_replace_helper->initialiseSearchReplace($this->_component->get('search_replace_pairs'), '', $dst_path); // Call the main search and replace function $this->_search_replace_helper->doSearchReplace(); // Finally need to copy icons for components and component object if ($this->_component->icon_16px != '') { copy(JPATH_SITE . '/' . $this->_component->icon_16px, $dst_path . '/media/images/' . str_replace("_", "", str_replace(" ", "", JString::strtolower($this->_component->code_name))) . '.png'); copy(JPATH_SITE . '/' . $this->_component->icon_16px, $dst_path . '/media/images/icon-16-' . str_replace("_", "", str_replace(" ", "", JString::strtolower($this->_component->code_name))) . '.png'); } if ($this->_component->icon_48px != '') { $image_name = str_replace("_", "", str_replace(" ", "", JString::strtolower($this->_component->code_name))) . '.png'; copy(JPATH_SITE . '/' . $this->_component->icon_48px, $dst_path . '/media/images/icon-48-' . $image_name); ComponentArchitectHelper::createThumb(JPATH_SITE . '/' . $this->_component->icon_48px, $dst_path . '/media/images/icon-32-' . $image_name, 32, 32); ComponentArchitectHelper::createThumb(JPATH_SITE . '/' . $this->_component->icon_48px, $dst_path . '/media/images/icon-24-' . $image_name, 24, 24); } // If component has to generate categories then include images for those in both admin assets and media (for auto menu items and Header images) if ($this->_search_replace_helper->getComponentConditions('generate_categories')) { if ($this->_component->categories_icon_16px != '') { copy(JPATH_SITE . '/' . $this->_component->categories_icon_16px, $dst_path . '/media/images/' . str_replace("_", "", JString::strtolower($this->_component->code_name)) . '-categories.png'); copy(JPATH_SITE . '/' . $this->_component->categories_icon_16px, $dst_path . '/media/images/icon-16-categories.png'); } if ($this->_component->categories_icon_48px != '') { copy(JPATH_SITE . '/' . $this->_component->categories_icon_48px, $dst_path . '/media/images/icon-48-categories.png'); ComponentArchitectHelper::createThumb(JPATH_SITE . '/' . $this->_component->categories_icon_48px, $dst_path . '/media/images/icon-32-categories.png', 32, 32); ComponentArchitectHelper::createThumb(JPATH_SITE . '/' . $this->_component->categories_icon_48px, $dst_path . '/media/images/icon-24-categories.png', 24, 24); } } foreach ($this->_component_objects as $component_object) { if ($component_object->icon_16px != '') { copy(JPATH_SITE . '/' . $component_object->icon_16px, $dst_path . '/media/images/' . str_replace("_", "", JString::strtolower($this->_component->code_name)) . '-' . str_replace("_", "", str_replace(" ", "", JString::strtolower($component_object->plural_code_name))) . '.png'); copy(JPATH_SITE . '/' . $component_object->icon_16px, $dst_path . '/media/images/icon-16-' . str_replace("_", "", str_replace(" ", "", JString::strtolower($component_object->plural_code_name))) . '.png'); } if ($component_object->icon_48px != '') { $image_name = str_replace("_", "", str_replace(" ", "", JString::strtolower($component_object->plural_code_name))) . '.png'; copy(JPATH_SITE . '/' . $component_object->icon_48px, $dst_path . '/media/images/icon-48-' . $image_name); ComponentArchitectHelper::createThumb(JPATH_SITE . '/' . $component_object->icon_48px, $dst_path . '/media/images/icon-32-' . $image_name, 32, 32); ComponentArchitectHelper::createThumb(JPATH_SITE . '/' . $component_object->icon_48px, $dst_path . '/media/images/icon-24-' . $image_name, 24, 24); } } // Update Stage 3 progress as being complete if logging requested this will also create a log record $step = JText::_('COM_COMPONENTARCHITECT_GENERATE_END_STAGE_3'); $this->_progress->setProgress($this->_token, 'stage_3', $step, true); $component_path = $output_path . '/' . $dst_file; $zip_file = $this->_zip_file; if ($zip_format != '') { jimport('joomla.filesystem.archive'); $zipFilesArray = array(); $dirs = JFolder::folders($dst_path, '.', true, true); array_push($dirs, $dst_path); foreach ($dirs as $dir) { $files = JFolder::files($dir, '.', false, true); foreach ($files as $file) { $data = JFile::read($file); $zipFilesArray[] = array('name' => str_replace($dst_path . '\\', '', str_replace($dst_path . '/', '', $file)), 'data' => $data); } } $zip = JArchive::getAdapter($zip_format); $zip->create(JPATH_SITE . '/' . $output_path . '/' . $dst_file . '.' . $zip_format, $zipFilesArray); $zip_file = $dst_file . '.' . $zip_format; $zip_path = JUri::root() . $output_path; } else { $zip_file = ''; $zip_path = ''; } $this->_progress->completeProgress($this->_token, $component_path, $zip_path, $zip_file); return true; }