* @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\Joomlamodule; use VDM\Joomla\Componentbuilder\Compiler\Joomlamodule\Data as Module; use VDM\Joomla\Componentbuilder\Compiler\Component; use VDM\Joomla\Componentbuilder\Compiler\Config; use VDM\Joomla\Componentbuilder\Compiler\Registry; use VDM\Joomla\Componentbuilder\Compiler\Customcode\Dispenser; use VDM\Joomla\Componentbuilder\Compiler\Interfaces\EventInterface as Event; use VDM\Joomla\Componentbuilder\Compiler\Utilities\Counter; use VDM\Joomla\Componentbuilder\Compiler\Utilities\Folder; use VDM\Joomla\Componentbuilder\Compiler\Utilities\File; use VDM\Joomla\Componentbuilder\Compiler\Utilities\Files; use VDM\Joomla\Componentbuilder\Compiler\Utilities\Indent; use VDM\Joomla\Componentbuilder\Compiler\Utilities\Placefix; use VDM\Joomla\Componentbuilder\Compiler\Utilities\Line; use VDM\Joomla\Componentbuilder\Compiler\Builder\TemplateData; use VDM\Joomla\Utilities\ArrayHelper; use VDM\Joomla\Utilities\ObjectHelper; use VDM\Joomla\Utilities\StringHelper; use VDM\Joomla\Utilities\FileHelper; /** * Joomla Module Structure Builder Class * * @since 3.2.0 */ class Structure { /** * The Data Class. * * @var Module * @since 3.2.0 */ protected Module $module; /** * The Component Class. * * @var Component * @since 3.2.0 */ protected Component $component; /** * The Config Class. * * @var Config * @since 3.2.0 */ protected Config $config; /** * The Registry Class. * * @var Registry * @since 3.2.0 */ protected Registry $registry; /** * The Dispenser Class. * * @var Dispenser * @since 3.2.0 */ protected Dispenser $dispenser; /** * The EventInterface Class. * * @var Event * @since 3.2.0 */ protected Event $event; /** * The Counter Class. * * @var Counter * @since 3.2.0 */ protected Counter $counter; /** * The Folder Class. * * @var Folder * @since 3.2.0 */ protected Folder $folder; /** * The File Class. * * @var File * @since 3.2.0 */ protected File $file; /** * The Files Class. * * @var Files * @since 3.2.0 */ protected Files $files; /** * The TemplateData Class. * * @var TemplateData * @since 3.2.0 */ protected TemplateData $templatedata; /** * Constructor. * * @param Module $module The Data Class. * @param Component $component The Component Class. * @param Config $config The Config Class. * @param Registry $registry The Registry Class. * @param Dispenser $dispenser The Dispenser Class. * @param Event $event The EventInterface Class. * @param Counter $counter The Counter Class. * @param Folder $folder The Folder Class. * @param File $file The File Class. * @param Files $files The Files Class. * @param TemplateData $templatedata The TemplateData Class. * * @since 3.2.0 */ public function __construct(Module $module, Component $component, Config $config, Registry $registry, Dispenser $dispenser, Event $event, Counter $counter, Folder $folder, File $file, Files $files, TemplateData $templatedata) { $this->module = $module; $this->component = $component; $this->config = $config; $this->registry = $registry; $this->dispenser = $dispenser; $this->event = $event; $this->counter = $counter; $this->folder = $folder; $this->file = $file; $this->files = $files; $this->templatedata = $templatedata; } /** * Build the Modules files, folders, url's and config * * @return void * @since 3.2.0 */ public function build() { if ($this->module->exists()) { // for plugin event TODO change event api signatures $component_context = $this->config->component_context; $modules = $this->module->get(); // Trigger Event: jcb_ce_onBeforeSetModules $this->event->trigger( 'jcb_ce_onBeforeBuildModules', array(&$component_context, &$modules) ); foreach ($modules as $module) { if (ObjectHelper::check($module) && isset($module->folder_name) && StringHelper::check($module->folder_name)) { // module path $this->modulePath($module); // set the module paths $this->registry->set('dynamic_paths.' . $module->key, $module->folder_path); // make sure there is no old build $this->folder->remove($module->folder_path); // create the main module folder $this->folder->create($module->folder_path); // create the main module file $this->setMainModFile($module); // creat the custom get file $this->setCustomGet($module); // set helper file $this->setHelperFile($module); // set main xml file $this->setMainXmlFile($module); // set tmpl folder $this->folder->create($module->folder_path . '/tmpl'); // set default file $this->setDefaultFile($module); // set custom default files $this->setTemplateFiles($module); // set install script if needed $this->setInstallScript($module); // set readme if found $this->setReadme($module); // set the CSS and JavaScript in form $this->setCssJsForm($module); // set rules folders if needed if (isset($module->fields_rules_paths) && $module->fields_rules_paths == 2) { // create rules folder $this->folder->create($module->folder_path . '/rules'); } // set forms folder/files if needed $this->setForms($module); // set SQL stuff if needed $this->setSQL($module); // create the language folder $this->folder->create($module->folder_path . '/language'); // also create the lang tag folder $this->folder->create( $module->folder_path . '/language/' . $this->config->get('lang_tag', 'en-GB') ); // check if this module has files $this->setFiles($module); // check if this module has folders $this->setFolders($module); // check if this module has urls $this->setUrls($module); } } } } /** * get the module xml template * * @param object $module The module object * * @return string * @since 3.2.0 */ protected function getXML(object &$module): string { $xml = ''; $xml .= PHP_EOL . ''; $xml .= PHP_EOL . Indent::_(1) . '' . $module->lang_prefix . ''; $xml .= PHP_EOL . Indent::_(1) . '' . Placefix::_h('BUILDDATE') . ''; $xml .= PHP_EOL . Indent::_(1) . '' . Placefix::_h('AUTHOR') . ''; $xml .= PHP_EOL . Indent::_(1) . '' . Placefix::_h('AUTHOREMAIL') . ''; $xml .= PHP_EOL . Indent::_(1) . '' . Placefix::_h('AUTHORWEBSITE') . ''; $xml .= PHP_EOL . Indent::_(1) . '' . Placefix::_h('COPYRIGHT') . ''; $xml .= PHP_EOL . Indent::_(1) . '' . Placefix::_h('LICENSE') . ''; $xml .= PHP_EOL . Indent::_(1) . '' . $module->module_version . ''; $xml .= PHP_EOL . Indent::_(1) . '' . $module->lang_prefix . '_XML_DESCRIPTION'; $xml .= Placefix::_h('MAINXML'); $xml .= PHP_EOL . ''; return $xml; } /** * get the module admin custom script field * * @param array $fieldScriptBucket The field * * @return string * @since 3.2.0 * */ protected function getCustomScriptField(array $fieldScriptBucket): string { $form_field_class = []; $form_field_class[] = Placefix::_h('BOM') . PHP_EOL; $form_field_class[] = "//" . Line::_(__Line__, __Class__) . " No direct access to this file"; $form_field_class[] = "defined('_JEXEC') or die('Restricted access');"; $form_field_class[] = PHP_EOL . "use Joomla\CMS\Form\FormField;"; $form_field_class[] = "use Joomla\CMS\Factory;"; $form_field_class[] = PHP_EOL . "class JFormFieldModadminvvvvvvvdm extends FormField"; $form_field_class[] = "{"; $form_field_class[] = Indent::_(1) . "protected \$type = 'modadminvvvvvvvdm';"; $form_field_class[] = PHP_EOL . Indent::_(1) . "protected function getLabel()"; $form_field_class[] = Indent::_(1) . "{"; $form_field_class[] = Indent::_(2) . "return;"; $form_field_class[] = Indent::_(1) . "}"; $form_field_class[] = PHP_EOL . Indent::_(1) . "protected function getInput()"; $form_field_class[] = Indent::_(1) . "{"; $form_field_class[] = Indent::_(2) . "//" . Line::_(__Line__, __Class__) . " Get the document"; $form_field_class[] = Indent::_(2) . "\$document = Factory::getDocument();"; $form_field_class[] = implode(PHP_EOL, $fieldScriptBucket); $form_field_class[] = Indent::_(2) . "return; // noting for now :)"; $form_field_class[] = Indent::_(1) . "}"; $form_field_class[] = "}"; return implode(PHP_EOL, $form_field_class); } /** * Set the module path * * @param object $module * * @return void * @since 3.2.0 */ protected function modulePath(object &$module): void { $module->folder_path = $this->config->get('compiler_path', JPATH_COMPONENT_ADMINISTRATOR . '/compiler') . '/' . $module->folder_name; } /** * Set the main module file * * @param object $module * * @return void * @since 3.2.0 */ protected function setMainModFile(object $module): void { // set main mod file $file_details = [ 'path' => $module->folder_path . '/' . $module->file_name . '.php', 'name' => $module->file_name . '.php', 'zip' => $module->file_name . '.php' ]; $this->file->write( $file_details['path'], 'files->appendArray($module->key, $file_details); // count the file created $this->counter->file++; } /** * Set the custom get file * * @param object $module * * @return void * @since 3.2.0 */ protected function setCustomGet(object $module): void { if ($module->custom_get) { $file_details = [ 'path' => $module->folder_path . '/data.php', 'name' => 'data.php', 'zip' => 'data.php' ]; $this->file->write( $file_details['path'], 'official_name . ' Data' . PHP_EOL . ' */' . PHP_EOL . "class " . $module->class_data_name . ' extends \JObject' . PHP_EOL . "{" . Placefix::_h('DYNAMICGETS') . "}" . PHP_EOL ); $this->files->appendArray($module->key, $file_details); // count the file created $this->counter->file++; } } /** * Set the helper file * * @param object $module * * @return void * @since 3.2.0 */ protected function setHelperFile(object $module): void { if ($module->add_class_helper >= 1) { $file_details = [ 'path' => $module->folder_path . '/helper.php', 'name' => 'helper.php', 'zip' => 'helper.php' ]; $this->file->write( $file_details['path'], 'files->appendArray($module->key, $file_details); // count the file created $this->counter->file++; } } /** * Set the main XML file * * @param object $module * * @return void * @since 3.2.0 */ protected function setMainXmlFile(object $module): void { $file_details = [ 'path' => $module->folder_path . '/' . $module->file_name . '.xml', 'name' => $module->file_name . '.xml', 'zip' => $module->file_name . '.xml' ]; $this->file->write( $file_details['path'], $this->getXML($module) ); $this->files->appendArray($module->key, $file_details); // count the file created $this->counter->file++; } /** * Set the main default template file * * @param object $module * * @return void * @since 3.2.0 */ protected function setDefaultFile(object $module): void { $file_details = [ 'path' => $module->folder_path . '/tmpl/default.php', 'name' => 'default.php', 'zip' => 'tmpl/default.php' ]; $this->file->write( $file_details['path'], 'files->appendArray($module->key, $file_details); // count the file created $this->counter->file++; } /** * Set the additional template files * * @param object $module * * @return void * @since 3.2.0 */ protected function setTemplateFiles(object $module): void { if (($data_ = $this->templatedata-> get($module->key . '.' . $module->code_name)) !== null) { foreach ($data_ as $template => $data) { $file_details = [ 'path' => $module->folder_path . "/tmpl/default_{$template}.php", 'name' => "default_{$template}.php", 'zip' => "tmpl/default_{$template}.php" ]; $this->file->write( $file_details['path'], 'files->appendArray($module->key, $file_details); // count the file created $this->counter->file++; } } } /** * Set the install script file * * @param object $module * * @return void * @since 3.2.0 */ protected function setInstallScript(object $module): void { if ($module->add_install_script) { $file_details = [ 'path' => $module->folder_path . '/script.php', 'name' => 'script.php', 'zip' => 'script.php' ]; $this->file->write( $file_details['path'], 'files->appendArray($module->key, $file_details); // count the file created $this->counter->file++; } } /** * Set the readme file * * @param object $module * * @return void * @since 3.2.0 */ protected function setReadme(object $module): void { if ($module->addreadme) { $file_details = [ 'path' => $module->folder_path . '/README.md', 'name' => 'README.md', 'zip' => 'README.md' ]; $this->file->write($file_details['path'], $module->readme); $this->files->appendArray($module->key, $file_details); // count the file created $this->counter->file++; } } /** * Set the css and javascript in form * * @param object $module * * @return void * @since 3.2.0 */ protected function setCssJsForm(object $module): void { // set the folders target path $target_path = ''; if ($module->target_client === 'administrator') { $target_path = '/administrator'; } // check if we have custom fields needed for scripts $module->add_scripts_field = false; $field_script_bucket = []; // add any css from the fields $this->setCssForm($module, $target_path, $field_script_bucket); // add any JavaScript from the fields $this->setJsForm($module, $target_path, $field_script_bucket); // set fields folders if needed if ($module->add_scripts_field || (isset($module->fields_rules_paths) && $module->fields_rules_paths == 2)) { // create fields folder $this->folder->create($module->folder_path . '/fields'); // add the custom script field if ($module->add_scripts_field) { $file_details = [ 'path' => $module->folder_path . '/fields/modadminvvvvvvvdm.php', 'name' => 'modadminvvvvvvvdm.php', 'zip' => 'modadminvvvvvvvdm.php' ]; $this->file->write( $file_details['path'], $this->getCustomScriptField( $field_script_bucket ) ); $this->files->appendArray($module->key, $file_details); // count the file created $this->counter->file++; } } } /** * Set the css in form * * @param object $module * @param string $targetPath * @param array $bucket * * @return void * @since 3.2.0 */ protected function setCssForm(object &$module, string $targetPath, array &$bucket): void { if (($css = $this->dispenser->get( 'css_view', $module->key )) !== null && StringHelper::check($css)) { // make sure this script does not have PHP if (strpos((string) $css, 'add_scripts_field = true; // create the css folder $this->folder->create($module->folder_path . '/css'); // add the CSS file $file_details = [ 'path' => $module->folder_path . '/css/mod_admin.css', 'name' => 'mod_admin.css', 'zip' => 'mod_admin.css' ]; $this->file->write( $file_details['path'], Placefix::_h('BOM') . PHP_EOL . PHP_EOL . $css ); $this->files->appendArray($module->key, $file_details); // count the file created $this->counter->file++; // add the field script $bucket[] = Indent::_(2) . "//" . Line::_(__Line__, __Class__) . " Custom CSS"; $bucket[] = Indent::_(2) . "Html::_('stylesheet', " . "modules/" . $module->folder_name . "/css/mod_admin.css', ['version' => 'auto', 'relative' => true]);"; } } } /** * Set the javascript in form * * @param object $module * @param string $targetPath * @param array $bucket * * @return void * @since 3.2.0 */ protected function setJsForm(object &$module, string $targetPath, array &$bucket): void { if (($javascript = $this->dispenser->get( 'view_footer', $module->key )) !== null && StringHelper::check($javascript)) { // make sure this script does not have PHP if (strpos((string) $javascript, 'add_scripts_field = true; // add the JavaScript file $this->folder->create($module->folder_path . '/js'); // add the CSS file $file_details = [ 'path' => $module->folder_path . '/js/mod_admin.js', 'name' => 'mod_admin.js', 'zip' => 'mod_admin.js' ]; $this->file->write( $file_details['path'], Placefix::_h('BOM') . PHP_EOL . PHP_EOL . $javascript ); $this->files->appendArray($module->key, $file_details); // count the file created $this->counter->file++; // add the field script $bucket[] = Indent::_(2) . "//" . Line::_(__Line__, __Class__) . " Custom JS"; $bucket[] = Indent::_(2) . "Html::_('script', " . "/modules/" . $module->folder_name . "/js/mod_admin.js', ['version' => 'auto', 'relative' => true]);"; } } } /** * Set the form folders and files as needed * * @param object $module * * @return void * @since 3.2.0 */ protected function setForms(object &$module): void { if (isset($module->form_files) && ArrayHelper::check( $module->form_files )) { // create forms folder $this->folder->create($module->folder_path . '/forms'); // set the template files foreach ($module->form_files as $file => $fields) { // set file details $file_details = [ 'path' => $module->folder_path . '/forms/' . $file . '.xml', 'name' => $file . '.xml', 'zip' => 'forms/' . $file . '.xml' ]; // build basic XML $xml = ''; $xml .= PHP_EOL . ''; // search if we must add the component path $add_component_path = false; foreach ($fields as $field_name => $fieldsets) { if (!$add_component_path) { foreach ($fieldsets as $fieldset => $field) { if (!$add_component_path && isset( $module->fieldsets_paths[$file . $field_name . $fieldset] ) && $module->fieldsets_paths[$file . $field_name . $fieldset] == 1) { $add_component_path = true; } } } } // only add if part of the component field types path is required if ($add_component_path) { $xml .= PHP_EOL . 'config->get('joomla_version', 3) == 3) { $xml .= PHP_EOL . Indent::_(1) . 'addrulepath="/administrator/components/com_' . $this->config->component_code_name . '/models/rules"'; $xml .= PHP_EOL . Indent::_(1) . 'addfieldpath="/administrator/components/com_' . $this->config->component_code_name . '/models/fields"'; } else { $xml .= PHP_EOL . Indent::_(1) . 'addruleprefix="' . $this->config->namespace_prefix . '\Component\\' . StringHelper::safe($this->config->component_code_name, 'F') . '\Administrator\Rule"'; $xml .= PHP_EOL . Indent::_(1) .'addfieldprefix="' . $this->config->namespace_prefix . '\Component\\' . StringHelper::safe($this->config->component_code_name, 'F') . '\Administrator\Field"'; } $xml .= PHP_EOL . '>'; } else { $xml .= PHP_EOL . '
'; } // add the fields foreach ($fields as $field_name => $fieldsets) { // check if we have an double fields naming set $field_name_inner = ''; $field_name_outer = $field_name; if (strpos((string)$field_name, '.') !== false) { $field_names = explode('.', (string)$field_name); if (count((array)$field_names) == 2) { $field_name_outer = $field_names[0]; $field_name_inner = $field_names[1]; } } $xml .= PHP_EOL . Indent::_(1) . ''; foreach ($fieldsets as $fieldset => $field) { // default to the field set name $label = $fieldset; if (isset($module->fieldsets_label[$file . $field_name . $fieldset])) { $label = $module->fieldsets_label[$file . $field_name . $fieldset]; } // add path to module rules and custom fields if (isset($module->fieldsets_paths[$file . $field_name . $fieldset]) && ($module->fieldsets_paths[$file . $field_name . $fieldset] == 2 || $module->fieldsets_paths[$file . $field_name . $fieldset] == 3)) { if ($module->target == 2) { if (!isset($module->add_rule_path[$file . $field_name . $fieldset])) { $module->add_rule_path[$file . $field_name . $fieldset] = '/administrator/modules/' . $module->file_name . '/rules'; } if (!isset($module->add_field_path[$file . $field_name . $fieldset])) { $module->add_field_path[$file . $field_name . $fieldset] = '/administrator/modules/' . $module->file_name . '/fields'; } } else { if (!isset($module->add_rule_path[$file . $field_name . $fieldset])) { $module->add_rule_path[$file . $field_name . $fieldset] = '/modules/' . $module->file_name . '/rules'; } if (!isset($module->add_field_path[$file . $field_name . $fieldset])) { $module->add_field_path[$file . $field_name . $fieldset] = '/modules/' . $module->file_name . '/fields'; } } } // add path to module rules and custom fields if (isset($module->add_rule_path[$file . $field_name . $fieldset]) || isset($module->add_field_path[$file . $field_name . $fieldset])) { $xml .= PHP_EOL . Indent::_(1) . ''; $xml .= PHP_EOL . Indent::_(1) . '
add_rule_path[$file . $field_name . $fieldset])) { $xml .= PHP_EOL . Indent::_(2) . 'addrulepath="' . $module->add_rule_path[$file . $field_name . $fieldset] . '"'; } if (isset($module->add_field_path[$file . $field_name . $fieldset])) { $xml .= PHP_EOL . Indent::_(2) . 'addfieldpath="' . $module->add_field_path[$file . $field_name . $fieldset] . '"'; } $xml .= PHP_EOL . Indent::_(1) . '>'; } else { $xml .= PHP_EOL . Indent::_(1) . '
'; } // check if we have an inner field set if (StringHelper::check( $field_name_inner )) { $xml .= PHP_EOL . Indent::_(1) . ''; } // add the placeholder of the fields $xml .= Placefix::_h('FIELDSET_' . $file . $field_name . $fieldset); // check if we have an inner field set if (StringHelper::check( $field_name_inner )) { $xml .= PHP_EOL . Indent::_(1) . ''; } $xml .= PHP_EOL . Indent::_(1) . '
'; } $xml .= PHP_EOL . Indent::_(1) . ''; } $xml .= PHP_EOL . ''; // add xml to file $this->file->write($file_details['path'], $xml); $this->files->appendArray($module->key, $file_details); // count the file created $this->counter->file++; } } } /** * Set the sql stuff * * @param object $module * * @return void * @since 3.2.0 */ protected function setSQL(object $module): void { if ($module->add_sql || $module->add_sql_uninstall) { // create SQL folder $this->folder->create($module->folder_path . '/sql'); // create mysql folder $this->folder->create( $module->folder_path . '/sql/mysql' ); // now set the install file if ($module->add_sql) { $this->file->write( $module->folder_path . '/sql/mysql/install.sql', $module->sql ); // count the file created $this->counter->file++; } // now set the uninstall file if ($module->add_sql_uninstall) { $this->file->write( $module->folder_path . '/sql/mysql/uninstall.sql', $module->sql_uninstall ); // count the file created $this->counter->file++; } } } /** * Set the files * * @param object $module * * @return void * @since 3.2.0 */ protected function setFiles(object $module): void { if (isset($module->files) && ArrayHelper::check($module->files)) { // add to component files foreach ($module->files as $file) { // set the pathfinder $file['target_type'] = $module->target_type; $file['target_id'] = $module->id; $this->component->appendArray('files', $file); } } } /** * Set the folders * * @param object $module * * @return void * @since 3.2.0 */ protected function setFolders(object $module): void { if (isset($module->folders) && ArrayHelper::check($module->folders)) { // add to component folders foreach ($module->folders as $folder) { // set the pathfinder $folder['target_type'] = $module->target_type; $folder['target_id'] = $module->id; $this->component->appendArray('folders', $folder); } } } /** * Set the urls * * @param object $module * * @return void * @since 3.2.0 */ protected function setUrls(object &$module): void { if (isset($module->urls) && ArrayHelper::check($module->urls)) { // add to component urls foreach ($module->urls as &$url) { // should we add the local folder if (isset($url['type']) && $url['type'] > 1 && isset($url['url']) && StringHelper::check( $url['url'] )) { // set file name $fileName = basename((string)$url['url']); // get the file contents $data = FileHelper::getContent( $url['url'] ); // build sub path if (strpos($fileName, '.js') !== false) { $path = '/js'; } elseif (strpos($fileName, '.css') !== false) { $path = '/css'; } else { $path = ''; } // create sub media path if not set $this->folder->create( $module->folder_path . $path ); // set the path to module file $url['path'] = $module->folder_path . $path . '/' . $fileName; // we need this for later // write data to path $this->file->write($url['path'], $data); // count the file created $this->counter->file++; } } } } }