* @git Joomla Component Builder * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace VDM\Joomla\Componentbuilder\Compiler\Joomlaplugin; use Joomla\CMS\Factory; use Joomla\CMS\Filter\OutputFilter; use VDM\Joomla\Componentbuilder\Compiler\Factory as Compiler; use VDM\Joomla\Componentbuilder\Compiler\Config; use VDM\Joomla\Componentbuilder\Compiler\Customcode; use VDM\Joomla\Componentbuilder\Compiler\Customcode\Gui; use VDM\Joomla\Componentbuilder\Compiler\Placeholder; use VDM\Joomla\Componentbuilder\Compiler\Language; use VDM\Joomla\Componentbuilder\Compiler\Field; use VDM\Joomla\Componentbuilder\Compiler\Field\Name as FieldName; use VDM\Joomla\Componentbuilder\Compiler\Model\Filesfolders; use VDM\Joomla\Utilities\ArrayHelper; use VDM\Joomla\Utilities\String\ClassfunctionHelper; use VDM\Joomla\Utilities\String\PluginHelper; use VDM\Joomla\Utilities\JsonHelper; use VDM\Joomla\Utilities\StringHelper; use VDM\Joomla\Utilities\GetHelper; /** * Joomla Plugin Data Class * * @since 3.2.0 */ class Data { /** * Compiler Joomla Plugins Data * * @var array * @since 3.2.0 */ protected array $data = []; /** * Compiler Config * * @var Config * @since 3.2.0 */ protected Config $config; /** * Compiler Customcode * * @var Customcode * @since 3.2.0 */ protected Customcode $customcode; /** * Compiler Customcode in Gui * * @var Gui * @since 3.2.0 **/ protected Gui $gui; /** * Compiler Placeholder * * @var Placeholder * @since 3.2.0 **/ protected Placeholder $placeholder; /** * Compiler Language * * @var Language * @since 3.2.0 **/ protected Language $language; /** * Compiler Field * * @var Field * @since 3.2.0 */ protected Field $field; /** * Compiler field name * * @var FieldName * @since 3.2.0 */ protected FieldName $fieldName; /** * Compiler Files Folders * * @var Filesfolders * @since 3.2.0 */ protected Filesfolders $filesFolders; /** * Database object to query local DB * * @since 3.2.0 **/ protected $db; /** * Constructor * * @param Config|null $config The compiler config object. * @param Customcode|null $customcode The compiler customcode object. * @param Gui|null $gui The compiler customcode gui. * @param Placeholder|null $placeholder The compiler placeholder object. * @param Language|null $language The compiler Language object. * @param Field|null $field The compiler field data object. * @param FieldName|null $fieldName The compiler field name object. * @param Filesfolders|null $filesFolders The compiler files folders object. * * @since 3.2.0 */ public function __construct(?Config $config = null, ?Customcode $customcode = null, ?Gui $gui = null, ?Placeholder $placeholder = null, ?Language $language = null, ?Field $field = null, ?FieldName $fieldName = null, ?Filesfolders $filesFolders = null) { $this->config = $config ?: Compiler::_('Config'); $this->customcode = $customcode ?: Compiler::_('Customcode'); $this->gui = $gui ?: Compiler::_('Customcode.Gui'); $this->placeholder = $placeholder ?: Compiler::_('Placeholder'); $this->language = $language ?: Compiler::_('Language'); $this->field = $field ?: Compiler::_('Field'); $this->fieldName = $fieldName ?: Compiler::_('Field.Name'); $this->filesFolders = $filesFolders ?: Compiler::_('Model.Filesfolders'); $this->db = Factory::getDbo(); } /** * Get the Joomla Plugin/s * * @param int|null $id the plugin id * * @return object|array|null if ID found it returns object, if no ID given it returns all set * @since 3.2.0 */ public function get(int $id = null) { if (is_null($id) && $this->exists()) { return $this->data; } elseif ($this->exists($id)) { return $this->data[$id]; } return null; } /** * Check if the Joomla Plugin/s exists * * @param int|null $id the plugin id * * @return bool if ID found it returns true, if no ID given it returns true if any are set * @since 3.2.0 */ public function exists(int $id = null): bool { if (is_null($id)) { return ArrayHelper::check($this->data); } elseif (isset($this->data[$id])) { return true; } return $this->set($id); } /** * Set the Joomla Plugin * * @param int $id the plugin id * * @return bool true on success * @since 3.2.0 */ public function set(int $id): bool { if (isset($this->data[$id])) { return true; } else { // Create a new query object. $query = $this->db->getQuery(true); $query->select('a.*'); $query->select( $this->db->quoteName( array( 'g.name', 'e.name', 'e.head', 'e.comment', 'e.id', 'f.addfiles', 'f.addfolders', 'f.addfilesfullpath', 'f.addfoldersfullpath', 'f.addurls', 'u.version_update', 'u.id' ), array( 'group', 'extends', 'class_head', 'comment', 'class_id', 'addfiles', 'addfolders', 'addfilesfullpath', 'addfoldersfullpath', 'addurls', 'version_update', 'version_update_id' ) ) ); // 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->join( 'LEFT', $this->db->quoteName('#__componentbuilder_class_extends', 'e') . ' ON (' . $this->db->quoteName('a.class_extends') . ' = ' . $this->db->quoteName('e.id') . ')' ); $query->join( 'LEFT', $this->db->quoteName( '#__componentbuilder_joomla_plugin_updates', 'u' ) . ' ON (' . $this->db->quoteName('a.id') . ' = ' . $this->db->quoteName('u.joomla_plugin') . ')' ); $query->join( 'LEFT', $this->db->quoteName( '#__componentbuilder_joomla_plugin_files_folders_urls', 'f' ) . ' ON (' . $this->db->quoteName('a.id') . ' = ' . $this->db->quoteName('f.joomla_plugin') . ')' ); $query->where($this->db->quoteName('a.id') . ' = ' . (int) $id); $query->where($this->db->quoteName('a.published') . ' >= 1'); $this->db->setQuery($query); $this->db->execute(); if ($this->db->getNumRows()) { // get the plugin data $plugin = $this->db->loadObject(); // tweak system to set stuff to the plugin domain $_backup_target = $this->config->build_target; $_backup_lang = $this->config->lang_target; $_backup_langPrefix = $this->config->lang_prefix; // set some keys $plugin->target_type = 'pLuG!n'; $plugin->key = $plugin->id . '_' . $plugin->target_type; // update to point to plugin $this->config->build_target = $plugin->key; $this->config->lang_target = $plugin->key; // set version if not set if (empty($plugin->plugin_version)) { $plugin->plugin_version = '1.0.0'; } // set GUI mapper $guiMapper = array('table' => 'joomla_plugin', 'id' => (int) $id, 'type' => 'php'); // update the name if it has dynamic values $plugin->name = $this->placeholder->update_( $this->customcode->update($plugin->name) ); // update the name if it has dynamic values $plugin->code_name = ClassfunctionHelper::safe( $plugin->name ); // set official name $plugin->official_name = ucwords( $plugin->group . ' - ' . $plugin->name ); // set lang prefix $plugin->lang_prefix = PluginHelper::safeLangPrefix( $plugin->code_name, $plugin->group ); // set langPrefix $this->config->lang_prefix = $plugin->lang_prefix; // set plugin class name $plugin->class_name = PluginHelper::safeClassName( $plugin->code_name, $plugin->group ); // set plugin install class name $plugin->installer_class_name = PluginHelper::safeInstallClassName( $plugin->code_name, $plugin->group ); // set plugin folder name $plugin->folder_name = PluginHelper::safeFolderName( $plugin->code_name, $plugin->group ); // set the zip name $plugin->zip_name = $plugin->folder_name . '_v' . str_replace( '.', '_', (string) $plugin->plugin_version ) . '__J' . $this->config->joomla_version; // set plugin file name $plugin->file_name = strtolower((string) $plugin->code_name); // set plugin context $plugin->context = $plugin->folder_name . '.' . $plugin->id; // set official_name lang strings $this->language->set( $plugin->key, $this->config->lang_prefix, $plugin->official_name ); // set some placeholder for this plugin $this->placeholder->set('Plugin_name', $plugin->official_name); $this->placeholder->set('PLUGIN_NAME', $plugin->official_name); $this->placeholder->set('Plugin', ucfirst((string) $plugin->code_name)); $this->placeholder->set('plugin', strtolower((string) $plugin->code_name)); $this->placeholder->set('Plugin_group', ucfirst((string) $plugin->group)); $this->placeholder->set('plugin_group', strtolower((string) $plugin->group)); $this->placeholder->set('plugin.version', $plugin->plugin_version); $this->placeholder->set('VERSION', $plugin->plugin_version); $this->placeholder->set('plugin_version', str_replace( '.', '_', (string) $plugin->plugin_version )); // set description $this->placeholder->set('DESCRIPTION', ''); if (!isset($plugin->description) || !StringHelper::check( $plugin->description )) { $plugin->description = ''; } else { $plugin->description = $this->placeholder->update_( $this->customcode->update($plugin->description) ); $this->language->set( $plugin->key, $plugin->lang_prefix . '_DESCRIPTION', $plugin->description ); // set description $this->placeholder->set('DESCRIPTION', $plugin->description); $plugin->description = '

' . $plugin->description . '

'; } // get author name $project_author = $this->config->project_author; // we can only set these if the component was passed $plugin->xml_description = "

" . $plugin->official_name . " (v." . $plugin->plugin_version . ")

" . $plugin->description . "

Created by " . trim( (string) OutputFilter::cleanText($project_author) ) . "
Development started " . Factory::getDate($plugin->created)->format("jS F, Y") . "

"; // set xml discription $this->language->set( $plugin->key, $plugin->lang_prefix . '_XML_DESCRIPTION', $plugin->xml_description ); // update the readme if set if ($plugin->addreadme == 1 && !empty($plugin->readme)) { $plugin->readme = $this->placeholder->update_( $this->customcode->update(base64_decode((string) $plugin->readme)) ); } else { $plugin->addreadme = 0; unset($plugin->readme); } // open some base64 strings if (!empty($plugin->main_class_code)) { // set GUI mapper field $guiMapper['field'] = 'main_class_code'; // base64 Decode main_class_code. $plugin->main_class_code = $this->gui->set( $this->placeholder->update_( $this->customcode->update( base64_decode((string) $plugin->main_class_code) ) ), $guiMapper ); } // set the head :) if ($plugin->add_head == 1 && !empty($plugin->head)) { // set GUI mapper field $guiMapper['field'] = 'head'; // base64 Decode head. $plugin->head = $this->gui->set( $this->placeholder->update_( $this->customcode->update( base64_decode((string) $plugin->head) ) ), $guiMapper ); } elseif (!empty($plugin->class_head)) { // base64 Decode head. $plugin->head = $this->gui->set( $this->placeholder->update_( $this->customcode->update( base64_decode((string) $plugin->class_head) ) ), array( 'table' => 'class_extends', 'field' => 'head', 'id' => (int) $plugin->class_id, 'type' => 'php') ); } unset($plugin->class_head); // set the comment if (!empty($plugin->comment)) { // base64 Decode comment. $plugin->comment = $this->gui->set( $this->placeholder->update_( $this->customcode->update( base64_decode((string) $plugin->comment) ) ), array( 'table' => 'class_extends', 'field' => 'comment', 'id' => (int) $plugin->class_id, 'type' => 'php') ); } // start the config array $plugin->config_fields = []; // create the form arrays $plugin->form_files = []; $plugin->fieldsets_label = []; $plugin->fieldsets_paths = []; $plugin->add_rule_path = []; $plugin->add_field_path = []; // set global fields rule to default component path $plugin->fields_rules_paths = 1; // set the fields data $plugin->fields = (isset($plugin->fields) && JsonHelper::check($plugin->fields)) ? json_decode((string) $plugin->fields, true) : null; if (ArrayHelper::check($plugin->fields)) { // ket global key $key = $plugin->key; $dynamic_fields = array('fieldset' => 'basic', 'fields_name' => 'params', 'file' => 'config'); foreach ($plugin->fields as $n => &$form) { if (isset($form['fields']) && ArrayHelper::check( $form['fields'] )) { // make sure the dynamic_field is set to dynamic_value by default foreach ( $dynamic_fields as $dynamic_field => $dynamic_value ) { if (!isset($form[$dynamic_field]) || !StringHelper::check( $form[$dynamic_field] )) { $form[$dynamic_field] = $dynamic_value; } else { if ('fields_name' === $dynamic_field && strpos((string) $form[$dynamic_field], '.') !== false) { $form[$dynamic_field] = $form[$dynamic_field]; } else { $form[$dynamic_field] = StringHelper::safe( $form[$dynamic_field] ); } } } // check if field is external form file if (!isset($form['plugin']) || $form['plugin'] != 1) { // now build the form key $unique = $form['file'] . $form['fields_name'] . $form['fieldset']; } else { // now build the form key $unique = $form['fields_name'] . $form['fieldset']; } // set global fields rule path switchs if ($plugin->fields_rules_paths == 1 && isset($form['fields_rules_paths']) && $form['fields_rules_paths'] == 2) { $plugin->fields_rules_paths = 2; } // set where to path is pointing $plugin->fieldsets_paths[$unique] = $form['fields_rules_paths']; // add the label if set to lang if (isset($form['label']) && StringHelper::check( $form['label'] )) { $plugin->fieldsets_label[$unique] = $this->language->key($form['label']); } // check for extra rule paths if (isset($form['addrulepath']) && ArrayHelper::check($form['addrulepath'])) { foreach ($form['addrulepath'] as $add_rule_path) { if (StringHelper::check($add_rule_path['path'])) { $plugin->add_rule_path[$unique] = $add_rule_path['path']; } } } // check for extra field paths if (isset($form['addfieldpath']) && ArrayHelper::check($form['addfieldpath'])) { foreach ($form['addfieldpath'] as $add_field_path) { if (StringHelper::check($add_field_path['path'])) { $plugin->add_field_path[$unique] = $add_field_path['path']; } } } // build the fields $form['fields'] = array_map( function ($field) use ($key, $unique) { // make sure the alias and title is 0 $field['alias'] = 0; $field['title'] = 0; // set the field details $this->field->set( $field, $key, $key, $unique ); // update the default if set if (StringHelper::check( $field['custom_value'] ) && isset($field['settings'])) { if (($old_default = GetHelper::between( $field['settings']->xml, 'default="', '"', false )) !== false) { // replace old default $field['settings']->xml = str_replace( 'default="' . $old_default . '"', 'default="' . $field['custom_value'] . '"', (string) $field['settings']->xml ); } else { // add the default (hmmm not ideal but okay it should work) $field['settings']->xml = 'default="' . $field['custom_value'] . '" ' . $field['settings']->xml; } } unset($field['custom_value']); // return field return $field; }, array_values($form['fields']) ); // check if field is external form file if (!isset($form['plugin']) || $form['plugin'] != 1) { // load the form file if (!isset($plugin->form_files[$form['file']])) { $plugin->form_files[$form['file']] = []; } if (!isset($plugin->form_files[$form['file']][$form['fields_name']])) { $plugin->form_files[$form['file']][$form['fields_name']] = []; } if (!isset($plugin->form_files[$form['file']][$form['fields_name']][$form['fieldset']])) { $plugin->form_files[$form['file']][$form['fields_name']][$form['fieldset']] = []; } // do some house cleaning (for fields) foreach ($form['fields'] as $field) { // so first we lock the field name in $this->fieldName->get( $field, $plugin->key, $unique ); // add the fields to the global form file builder $plugin->form_files[$form['file']][$form['fields_name']][$form['fieldset']][] = $field; } // remove form unset($plugin->fields[$n]); } else { // load the config form if (!isset($plugin->config_fields[$form['fields_name']])) { $plugin->config_fields[$form['fields_name']] = []; } if (!isset($plugin->config_fields[$form['fields_name']][$form['fieldset']])) { $plugin->config_fields[$form['fields_name']][$form['fieldset']] = []; } // do some house cleaning (for fields) foreach ($form['fields'] as $field) { // so first we lock the field name in $this->fieldName->get( $field, $plugin->key, $unique ); // add the fields to the config builder $plugin->config_fields[$form['fields_name']][$form['fieldset']][] = $field; } // remove form unset($plugin->fields[$n]); } } else { unset($plugin->fields[$n]); } } } unset($plugin->fields); // set files and folders $this->filesFolders->set($plugin); // add PHP in plugin install $plugin->add_install_script = true; $addScriptMethods = [ 'php_preflight', 'php_postflight', 'php_method' ]; $addScriptTypes = [ 'install', 'update', 'uninstall' ]; foreach ($addScriptMethods as $scriptMethod) { foreach ($addScriptTypes as $scriptType) { if (isset( $plugin->{'add_' . $scriptMethod . '_' . $scriptType}) && $plugin->{'add_' . $scriptMethod . '_' . $scriptType} == 1 && StringHelper::check( $plugin->{$scriptMethod . '_' . $scriptType} )) { // set GUI mapper field $guiMapper['field'] = $scriptMethod . '_' . $scriptType; $plugin->{$scriptMethod . '_' . $scriptType} = $this->gui->set( $this->placeholder->update_( $this->customcode->update( base64_decode( (string) $plugin->{$scriptMethod . '_' . $scriptType} ) ) ), $guiMapper ); } else { unset($plugin->{$scriptMethod . '_' . $scriptType}); $plugin->{'add_' . $scriptMethod . '_' . $scriptType} = 0; } } } // add_sql if ($plugin->add_sql == 1 && StringHelper::check($plugin->sql)) { $plugin->sql = $this->placeholder->update_( $this->customcode->update(base64_decode((string) $plugin->sql)) ); } else { unset($plugin->sql); $plugin->add_sql = 0; } // add_sql_uninstall if ($plugin->add_sql_uninstall == 1 && StringHelper::check( $plugin->sql_uninstall )) { $plugin->sql_uninstall = $this->placeholder->update_( $this->customcode->update( base64_decode((string) $plugin->sql_uninstall) ) ); } else { unset($plugin->sql_uninstall); $plugin->add_sql_uninstall = 0; } // update the URL of the update_server if set if ($plugin->add_update_server == 1 && StringHelper::check( $plugin->update_server_url )) { $plugin->update_server_url = $this->placeholder->update_( $this->customcode->update($plugin->update_server_url) ); } // add the update/sales server FTP details if that is the expected protocol $serverArray = array('update_server', 'sales_server'); foreach ($serverArray as $server) { if ($plugin->{'add_' . $server} == 1 && is_numeric( $plugin->{$server} ) && $plugin->{$server} > 0) { // get the server protocol $plugin->{$server . '_protocol'} = GetHelper::var( 'server', (int) $plugin->{$server}, 'id', 'protocol' ); } else { $plugin->{$server} = 0; // only change this for sales server (update server can be added locally to the zip file) if ('sales_server' === $server) { $plugin->{'add_' . $server} = 0; } $plugin->{$server . '_protocol'} = 0; } } // set the update server stuff (TODO) // update_server_xml_path // update_server_xml_file_name // rest globals $this->config->build_target = $_backup_target; $this->config->lang_target = $_backup_lang; $this->config->set('lang_prefix', $_backup_langPrefix); $this->placeholder->remove('Plugin_name'); $this->placeholder->remove('Plugin'); $this->placeholder->remove('plugin'); $this->placeholder->remove('Plugin_group'); $this->placeholder->remove('plugin_group'); $this->placeholder->remove('plugin.version'); $this->placeholder->remove('plugin_version'); $this->placeholder->remove('VERSION'); $this->placeholder->remove('DESCRIPTION'); $this->placeholder->remove('PLUGIN_NAME'); $this->data[$id] = $plugin; return true; } } return false; } }