<?php
/**
 * @package    Joomla.Component.Builder
 *
 * @created    4th September, 2022
 * @author     Llewellyn van der Merwe <https://dev.vdm.io>
 * @git        Joomla Component Builder <https://git.vdm.dev/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 Joomla\CMS\Factory;
use Joomla\CMS\Filter\OutputFilter;
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\Componentbuilder\Compiler\Model\Libraries;
use VDM\Joomla\Componentbuilder\Compiler\Dynamicget\Data as Dynamicget;
use VDM\Joomla\Componentbuilder\Compiler\Templatelayout\Data as Templatelayout;
use VDM\Joomla\Utilities\ArrayHelper;
use VDM\Joomla\Utilities\String\ClassfunctionHelper;
use VDM\Joomla\Utilities\JsonHelper;
use VDM\Joomla\Utilities\StringHelper;
use VDM\Joomla\Utilities\GetHelper;


/**
 * Joomla Module Data Class
 * 
 * @since 3.2.0
 */
class Data
{
	/**
	 * Compiler Joomla Plugins Data
	 *
	 * @var    array
	 * @since 3.2.0
	 */
	protected array $data = [];

	/**
	 * The Config Class.
	 *
	 * @var   Config
	 * @since 3.2.0
	 */
	protected Config $config;

	/**
	 * The Customcode Class.
	 *
	 * @var   Customcode
	 * @since 3.2.0
	 */
	protected Customcode $customcode;

	/**
	 * The Gui Class.
	 *
	 * @var   Gui
	 * @since 3.2.0
	 */
	protected Gui $gui;

	/**
	 * The Placeholder Class.
	 *
	 * @var   Placeholder
	 * @since 3.2.0
	 */
	protected Placeholder $placeholder;

	/**
	 * The Language Class.
	 *
	 * @var   Language
	 * @since 3.2.0
	 */
	protected Language $language;

	/**
	 * The Field Class.
	 *
	 * @var   Field
	 * @since 3.2.0
	 */
	protected Field $field;

	/**
	 * The Name Class.
	 *
	 * @var   Fieldname
	 * @since 3.2.0
	 */
	protected Fieldname $fieldname;

	/**
	 * The Filesfolders Class.
	 *
	 * @var   Filesfolders
	 * @since 3.2.0
	 */
	protected Filesfolders $filesfolders;

	/**
	 * The Libraries Class.
	 *
	 * @var   Libraries
	 * @since 3.2.0
	 */
	protected Libraries $libraries;

	/**
	 * The Data Class.
	 *
	 * @var   Dynamicget
	 * @since 3.2.0
	 */
	protected Dynamicget $dynamicget;

	/**
	 * The Data Class.
	 *
	 * @var   Templatelayout
	 * @since 3.2.0
	 */
	protected Templatelayout $templatelayout;

	/**
	 * The Database Class.
	 *
	 * @since 3.2.0
	 */
	protected $db;

	/**
	 * Constructor.
	 *
	 * @param Config           $config           The Config Class.
	 * @param Customcode       $customcode       The Customcode Class.
	 * @param Gui              $gui              The Gui Class.
	 * @param Placeholder      $placeholder      The Placeholder Class.
	 * @param Language         $language         The Language Class.
	 * @param Field            $field            The Field Class.
	 * @param Fieldname        $fieldname        The Name Class.
	 * @param Filesfolders     $filesfolders     The Filesfolders Class.
	 * @param Libraries        $libraries        The Libraries Class.
	 * @param Dynamicget       $dynamicget       The Data Class.
	 * @param Templatelayout   $templatelayout   The Data Class.
	 *
	 * @since 3.2.0
	 */
	public function __construct(Config $config, Customcode $customcode, Gui $gui,
		Placeholder $placeholder, Language $language,
		Field $field, Fieldname $fieldname,
		Filesfolders $filesfolders, Libraries $libraries,
		Dynamicget $dynamicget, Templatelayout $templatelayout)
	{
		$this->config = $config;
		$this->customcode = $customcode;
		$this->gui = $gui;
		$this->placeholder = $placeholder;
		$this->language = $language;
		$this->field = $field;
		$this->fieldname = $fieldname;
		$this->filesfolders = $filesfolders;
		$this->libraries = $libraries;
		$this->dynamicget = $dynamicget;
		$this->templatelayout = $templatelayout;
		$this->db = Factory::getDbo();
	}

	/**
	 * Get the Joomla Module/s
	 *
	 * @param   int|null   $id   the module 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 Module/s exists
	 *
	 * @param   int|null   $id   the module 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 Module
	 *
	 * @param   int     $id     the module 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(
						'f.addfiles',
						'f.addfolders',
						'f.addfilesfullpath',
						'f.addfoldersfullpath',
						'f.addurls',
						'u.version_update',
						'u.id'
					), array(
						'addfiles',
						'addfolders',
						'addfilesfullpath',
						'addfoldersfullpath',
						'addurls',
						'version_update',
						'version_update_id'
					)
				)
			);
			// from these tables
			$query->from('#__componentbuilder_joomla_module AS a');
			$query->join(
				'LEFT', $this->db->quoteName(
					'#__componentbuilder_joomla_module_updates', 'u'
				) . ' ON (' . $this->db->quoteName('a.id') . ' = '
				. $this->db->quoteName('u.joomla_module') . ')'
			);
			$query->join(
				'LEFT', $this->db->quoteName(
					'#__componentbuilder_joomla_module_files_folders_urls', 'f'
				) . ' ON (' . $this->db->quoteName('a.id') . ' = '
				. $this->db->quoteName('f.joomla_module') . ')'
			);
			$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 module data
				$module = $this->db->loadObject();

				// tweak system to set stuff to the module domain
				$_backup_target     = $this->config->build_target;
				$_backup_lang       = $this->config->lang_target;
				$_backup_langPrefix = $this->config->lang_prefix;

				// set some keys
				$module->target_type = 'M0dUl3';
				$module->key         = $module->id . '_' . $module->target_type;

				// update to point to module
				$this->config->build_target = $module->key;
				$this->config->lang_target = $module->key;

				// set version if not set
				if (empty($module->module_version))
				{
					$module->module_version = '1.0.0';
				}

				// set target client
				if ($module->target == 2)
				{
					$module->target_client = 'administrator';
				}
				else
				{
					// default is site area
					$module->target_client = 'site';
				}

				// set GUI mapper
				$guiMapper = array('table' => 'joomla_module',
				                   'id'    => (int) $id, 'type' => 'php');
				// update the name if it has dynamic values
				$module->name = $this->placeholder->update_(
					$this->customcode->update($module->name)
				);

				// set safe class function name
				$module->code_name
					= ClassfunctionHelper::safe(
					$module->name
				);

				// alias of code name
				$module->class_name = $module->code_name;
				// set official name
				$module->official_name = StringHelper::safe(
					$module->name, 'W'
				);
				$this->config->set('lang_prefix', 'MOD_' . strtoupper((string) $module->code_name));

				// set lang prefix
				$module->lang_prefix = $this->config->lang_prefix;

				// set module class name
				$module->class_helper_name = 'Mod' . ucfirst((string) $module->code_name)
					. 'Helper';
				$module->class_data_name   = 'Mod' . ucfirst((string) $module->code_name)
					. 'Data';

				// set module install class name
				$module->installer_class_name = 'mod_' . ucfirst(
						(string) $module->code_name
					) . 'InstallerScript';

				// set module folder name
				$module->folder_name = 'mod_' . strtolower((string) $module->code_name);

				// set the zip name
				$module->zip_name = $module->folder_name . '_v' . str_replace(
						'.', '_', (string) $module->module_version
					) . '__J' . $this->config->joomla_version;

				// set module file name
				$module->file_name = $module->folder_name;

				// set module context
				$module->context = $module->file_name . '.' . $module->id;

				// set official_name lang strings
				$this->language->set(
					$module->key, $this->config->lang_prefix, $module->official_name
				);

				// set some placeholder for this module
				$this->placeholder->set('Module_name', $module->official_name);
				$this->placeholder->set('Module', ucfirst(
					(string) $module->code_name
				));
				$this->placeholder->set('module', strtolower(
					(string) $module->code_name
				));
				$this->placeholder->set('module.version', $module->module_version);
				$this->placeholder->set('module_version', str_replace(
					'.', '_', (string) $module->module_version
				));
				// set description (TODO) add description field to module
				if (!isset($module->description)
					|| !StringHelper::check(
						$module->description
					))
				{
					$module->description = '';
				}
				else
				{
					$module->description = $this->placeholder->update_(
						$this->customcode->update($module->description)
					);
					$this->language->set(
						$module->key, $module->lang_prefix . '_DESCRIPTION',
						$module->description
					);
					$module->description = '<p>' . $module->description
						. '</p>';
				}

				// get author name
				$project_author = $this->config->project_author;

				// set the description
				$module->xml_description = "<h1>" . $module->official_name
					. " (v." . $module->module_version
					. ")</h1> <div style='clear: both;'></div>"
					. $module->description . "<p>Created by <a href='" . trim(
						(string) $this->config->project_website
					) . "' target='_blank'>" . trim(
						(string) OutputFilter::cleanText($project_author)
					) . "</a><br /><small>Development started "
					. Factory::getDate($module->created)->format("jS F, Y")
					. "</small></p>";

				// set xml description
				$this->language->set(
					$module->key, $module->lang_prefix . '_XML_DESCRIPTION',
					$module->xml_description
				);

				// update the readme if set
				if ($module->addreadme == 1 && !empty($module->readme))
				{
					$module->readme = $this->placeholder->update_(
						$this->customcode->update(base64_decode((string) $module->readme))
					);
				}
				else
				{
					$module->addreadme = 0;
					unset($module->readme);
				}

				// get the custom_get
				$module->custom_get = (isset($module->custom_get)
					&& JsonHelper::check($module->custom_get))
					? json_decode((string) $module->custom_get, true) : null;

				if (ArrayHelper::check($module->custom_get))
				{
					$module->custom_get = $this->dynamic->get(
						$module->custom_get, $module->key, $module->key
					);
				}
				else
				{
					$module->custom_get = null;
				}

				// set helper class details
				if ($module->add_class_helper >= 1
					&& StringHelper::check(
						$module->class_helper_code
					))
				{
					if ($module->add_class_helper_header == 1
						&& StringHelper::check(
							$module->class_helper_header
						))
					{
						// set GUI mapper field
						$guiMapper['field'] = 'class_helper_header';
						// base64 Decode code
						$module->class_helper_header = PHP_EOL
							. $this->gui->set(
								$this->placeholder->update_(
									$this->customcode->update(
										base64_decode(
											(string) $module->class_helper_header
										)
									)
								),
								$guiMapper
							) . PHP_EOL;
					}
					else
					{
						$module->add_class_helper_header = 0;
						$module->class_helper_header     = '';
					}
					// set GUI mapper field
					$guiMapper['field'] = 'class_helper_code';
					// base64 Decode code
					$module->class_helper_code = $this->gui->set(
						$this->placeholder->update_(
							$this->customcode->update(
								base64_decode((string) $module->class_helper_code)
							)
						),
						$guiMapper
					);
					// set class type
					if ($module->add_class_helper == 2)
					{
						$module->class_helper_type = 'abstract class ';
					}
					else
					{
						$module->class_helper_type = 'class ';
					}
				}
				else
				{
					$module->add_class_helper    = 0;
					$module->class_helper_code   = '';
					$module->class_helper_header = '';
				}

				// base64 Decode mod_code
				if (isset($module->mod_code)
					&& StringHelper::check($module->mod_code))
				{
					// set GUI mapper field
					$guiMapper['field'] = 'mod_code';
					$module->mod_code   = $this->gui->set(
						$this->placeholder->update_(
							$this->customcode->update(
								base64_decode((string) $module->mod_code)
							)
						),
						$guiMapper
					);

					// check if we have template or layouts to load
					$this->templatelayout->set(
						$module->mod_code , $module->code_name
					);
				}
				else
				{
					$module->mod_code = "// get the module class sfx";
					$module->mod_code .= PHP_EOL
						. "\$moduleclass_sfx = htmlspecialchars(\$params->get('moduleclass_sfx'), ENT_COMPAT, 'UTF-8');";
					$module->mod_code .= PHP_EOL . "// load the default Tmpl";
					$module->mod_code .= PHP_EOL
						. "require JModuleHelper::getLayoutPath('mod_"
						. strtolower((string) $module->code_name)
						. "', \$params->get('layout', 'default'));";
				}

				// base64 Decode default header
				if (isset($module->default_header)
					&& StringHelper::check(
						$module->default_header
					))
				{
					// set GUI mapper field
					$guiMapper['field']     = 'default_header';
					$module->default_header = $this->gui->set(
						$this->placeholder->update_(
							$this->customcode->update(
								base64_decode((string) $module->default_header)
							)
						),
						$guiMapper
					);
				}
				else
				{
					$module->default_header = '';
				}

				// base64 Decode default
				if (isset($module->default)
					&& StringHelper::check($module->default))
				{
					// set GUI mapper field
					$guiMapper['field'] = 'default';
					$guiMapper['type']  = 'html';
					$module->default    = $this->gui->set(
						$this->placeholder->update_(
							$this->customcode->update(
								base64_decode((string) $module->default)
							)
						),
						$guiMapper
					);

					// check if we have template or layouts to load
					$this->templatelayout->set(
						$module->default , $module->code_name
					);
				}
				else
				{
					$module->default = '<h1>No Tmpl set</h1>';
				}

				// start the config array
				$module->config_fields = [];
				// create the form arrays
				$module->form_files      = [];
				$module->fieldsets_label = [];
				$module->fieldsets_paths = [];
				$module->add_rule_path = [];
				$module->add_field_path = [];
				// set global fields rule to default component path
				$module->fields_rules_paths = 1;
				// set the fields data
				$module->fields = (isset($module->fields)
					&& JsonHelper::check($module->fields))
					? json_decode((string) $module->fields, true) : null;
				if (ArrayHelper::check($module->fields))
				{
					// ket global key
					$key            = $module->key;
					$dynamic_fields = array('fieldset'    => 'basic',
					                        'fields_name' => 'params',
					                        'file'        => 'config');
					foreach ($module->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['module']) || $form['module'] != 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 switches
							if ($module->fields_rules_paths == 1
								&& isset($form['fields_rules_paths'])
								&& $form['fields_rules_paths'] == 2)
							{
								$module->fields_rules_paths = 2;
							}
							// set where to path is pointing
							$module->fieldsets_paths[$unique]
								= $form['fields_rules_paths'];
							// 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']))
									{
										$module->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']))
									{
										$module->add_field_path[$unique] = $add_field_path['path'];
									}
								}
							}
							// add the label if set to lang
							if (isset($form['label'])
								&& StringHelper::check(
									$form['label']
								))
							{
								$module->fieldsets_label[$unique]
									= $this->language->key($form['label']);
							}
							// 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['module']) || $form['module'] != 1)
							{
								// load the form file
								if (!isset($module->form_files[$form['file']]))
								{
									$module->form_files[$form['file']]
										= [];
								}
								if (!isset($module->form_files[$form['file']][$form['fields_name']]))
								{
									$module->form_files[$form['file']][$form['fields_name']]
										= [];
								}
								if (!isset($module->form_files[$form['file']][$form['fields_name']][$form['fieldset']]))
								{
									$module->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, $module->key, $unique
									);
									// add the fields to the global form file builder
									$module->form_files[$form['file']][$form['fields_name']][$form['fieldset']][]
										= $field;
								}
								// remove form
								unset($module->fields[$n]);
							}
							else
							{
								// load the config form
								if (!isset($module->config_fields[$form['fields_name']]))
								{
									$module->config_fields[$form['fields_name']]
										= [];
								}
								if (!isset($module->config_fields[$form['fields_name']][$form['fieldset']]))
								{
									$module->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, $module->key, $unique
									);
									// add the fields to the config builder
									$module->config_fields[$form['fields_name']][$form['fieldset']][]
										= $field;
								}
								// remove form
								unset($module->fields[$n]);
							}
						}
						else
						{
							unset($module->fields[$n]);
						}
					}
				}
				unset($module->fields);

				// set files and folders
				$this->filesfolders->set($module);

				// set libraries
				$this->libraries->set($module->code_name, $module);

				// add PHP in module install
				$module->add_install_script = true;
				$addScriptMethods = [
					'php_script',
					'php_preflight',
					'php_postflight',
					'php_method'
				];
				$addScriptTypes = [
					'install',
					'update',
					'uninstall'
				];
				// the next are php placeholders
				$guiMapper['type'] = 'php';
				foreach ($addScriptMethods as $scriptMethod)
				{
					foreach ($addScriptTypes as $scriptType)
					{
						if (isset($module->{'add_' . $scriptMethod . '_' . $scriptType})
							&& $module->{'add_' . $scriptMethod . '_' . $scriptType} == 1
							&& StringHelper::check(
								$module->{$scriptMethod . '_' . $scriptType}
							))
						{
							// set GUI mapper field
							$guiMapper['field'] = $scriptMethod . '_' . $scriptType;
							$module->{$scriptMethod . '_' . $scriptType} = $this->gui->set(
								$this->placeholder->update_(
									$this->customcode->update(
										base64_decode(
											(string) $module->{$scriptMethod . '_' . $scriptType}
										)
									)
								),
								$guiMapper
							);
						}
						else
						{
							unset($module->{$scriptMethod . '_' . $scriptType});
							$module->{'add_' . $scriptMethod . '_' . $scriptType} = 0;
						}
					}
				}

				// add_sql
				if ($module->add_sql == 1
					&& StringHelper::check($module->sql))
				{
					$module->sql = $this->placeholder->update_(
						$this->customcode->update(base64_decode((string) $module->sql))
					);
				}
				else
				{
					unset($module->sql);
					$module->add_sql = 0;
				}

				// add_sql_uninstall
				if ($module->add_sql_uninstall == 1
					&& StringHelper::check(
						$module->sql_uninstall
					))
				{
					$module->sql_uninstall = $this->placeholder->update_(
						$this->customcode->update(
							base64_decode((string) $module->sql_uninstall)
						)
					);
				}
				else
				{
					unset($module->sql_uninstall);
					$module->add_sql_uninstall = 0;
				}

				// update the URL of the update_server if set
				if ($module->add_update_server == 1
					&& StringHelper::check(
						$module->update_server_url
					))
				{
					$module->update_server_url = $this->placeholder->update_(
						$this->customcode->update($module->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 ($module->{'add_' . $server} == 1
						&& is_numeric(
							$module->{$server}
						)
						&& $module->{$server} > 0)
					{
						// get the server protocol
						$module->{$server . '_protocol'}
							= GetHelper::var(
							'server', (int) $module->{$server}, 'id', 'protocol'
						);
					}
					else
					{
						$module->{$server} = 0;
						// only change this for sales server (update server can be added locally to the zip file)
						if ('sales_server' === $server)
						{
							$module->{'add_' . $server} = 0;
						}
						$module->{$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->lang_prefix = $_backup_langPrefix;

				$this->placeholder->remove('Module_name');
				$this->placeholder->remove('Module');
				$this->placeholder->remove('module');
				$this->placeholder->remove('module.version');
				$this->placeholder->remove('module_version');

				$this->data[$id] = $module;

				return true;
			}
		}

		return false;
	}
}

