<?php
/*************************************
 * SPDX-FileCopyrightText: 2009-2020 Vtenext S.r.l. <info@vtenext.com>
 * SPDX-License-Identifier: AGPL-3.0-only
 ************************************/

/* crmv@64542 crmv@105127 */

/*@ DEL

	This is a template file for the ModuleMaker
	There are some special values in this file:
	
	1. Any comment starting with /*@ is treated as a special comment and the next characters
	on that line specifies a command:
		DEL: remove all the comment until the closing tag (which has the @ sign as well)
		REPLACE: the comment is replaced with a standard comment, with some other text inside
		
	2. All the variables that begin with $TPL_ are replaced with other values/variables
	
@*/

/* Automatic installation script */
/* This file must be placed in the VTEROOT/storage/custom_modules folder */

// security check
if (php_sapi_name() != "cli" && !defined('MODULEMAKERSCRIPT')) {
	throw new InstallException("You are not allowed to invoke this script in this way!");
}

$configInc = '../../config.inc.php';
if (!is_readable($configInc)) throw new InstallException("File $configInc not found. Ensure this file is in the storage/custom_modules folder");

/* Includes */
require($configInc);
chdir($root_directory);
require('vteversion.php'); // crmv@181168

require_once('include/utils/utils.php');
require_once('modules/Update/Update.php');
require_once('vtlib/Vtecrm/Utils.php');
require_once('vtlib/Vtecrm/Field.php');
require_once('vtlib/Vtecrm/Module.php');

/* Globals */
global $adb, $table_prefix, $vtlib_Utils_Log, $current_user;
$vtlib_Utils_Log = true;

// missing current_user, when invoking from CLI
if (!isset($current_user)) {
	$current_user = CRMEntity::getInstance('Users');
	$current_user->id = 1;
}

/* Script class */
$MMS = new ModuleMakerScript();

/* Basic variables */
$target_vte_version = $TPL_VTETARGETVERSION;
$target_vte_revision = $TPL_VTETARGETREVISION;
$module_files = $TPL_MODULEFILESDIR;
$module_name = $TPL_MODULENAME;

$module_mainfield = $TPL_MAINFIELD;
$module_maintable = $TPL_MAINTABLE;
$module_tableid = $TPL_TABLEID;
$module_areaid = $TPL_AREAID;
$module_is_inventory = $TPL_ISINVENTORY;

$module_dest_dir = "modules/$module_name";

$module_all_operations = $TPL_ALLOPERATIONS;

/* Module variables */

// the tables to be created
$module_create_tables = $TPL_CREATETABLES;

// the default sharing for the module
$module_default_sharing = $TPL_DEFAULTSHARING;

// the operations for the module
$module_operations = $TPL_MODOPERATIONS;

// the panels to create
$module_panels = $TPL_PANELS;

// the blocks to create
$module_blocks = $TPL_BLOCKS;

// the fields
$module_fields = $TPL_FIELDS;

// other modules fields
$module_other_fields = $TPL_OTHERFIELDS;

// the filters
$module_filters = $TPL_FILTERS;

// the sequence prefix (or empty if not used)
$module_sequence = $TPL_SEQUENCEPREFIX;

// relations NtoN
$module_relations = $TPL_RELATIONS;

// relations 1toN to existing fields
$module_addrelations = $TPL_ADDRELATIONS;


// labels
$labels = $TPL_LANGUAGES;


// ----------------- SCRIPT START -------------------


// version check
if ($enterprise_current_version != $target_vte_version || $enterprise_current_build != $target_vte_revision) {
	$MMS->warn("This module has been created for a different VTE version (VTE $target_vte_version, revision $target_vte_revision). There is no guarantee that it will work as expected!");
}

// check if module is already installed
if (is_dir($module_dest_dir)) {
	throw new InstallException("There is already a folder called $module_dest_dir. Please, clean up manually the files");
}

if (isModuleInstalled($module_name)) {
	throw new InstallException("The module $module_name seems to be already installed. Please do a full uninstall and try again");
}

// check destination directory
if (!is_writable('modules')) {
	throw new InstallException("The modules folder is not writable, please fix the permissions");
}

// check the source files
if (!is_dir($module_files) || !is_readable($module_files)) {
	throw new InstallException("The folder with the module files is not readable");
}

// copy the files
$r = $MMS->rcopy($module_files, $module_dest_dir);
if (!$r) throw new InstallException("Unable to copy the files in the destination folder $module_dest_dir");


// check the module file
if (!is_readable("modules/$module_name/{$module_name}.php")) {
	throw new InstallException("The module file has not been found");
}

// create the tables
if (is_array($module_create_tables)) {
	foreach ($module_create_tables as $table=>$key) {
		if(!Vtecrm_Utils::CheckTable($table)) {
			Vtecrm_Utils::CreateTable($table,"$key I(19) PRIMARY",true);
		}
	}
}

// create and install module
$newModule = new Vtecrm_Module();
$newModule->name = $module_name;
$newModule->isentitytype = true;
$newModule->isinventory = (bool)$module_is_inventory;
$newModule->basetable = $module_maintable;
$newModule->entityidcolumn = $module_tableid;
$newModule->entityidfield = $module_tableid;
$tabid = $newModule->save();
if (empty($tabid)) throw new InstallException("Unable to install the module properly");

// trigger events
Vtecrm_Module::fireEvent($newModule->name, Vtecrm_Module::EVENT_MODULE_POSTINSTALL);

// initialize webservice
Vtecrm_Webservice::initialize($newModule);

// set Default sharing
$newModule->setDefaultSharing($module_default_sharing ?: 'Private');

// add it to the menu
$menuInstance = Vtecrm_Menu::getInstance('Tools');
$menuInstance->addModule($newModule);

// enable module operations
if (is_array($module_operations) && is_array($module_all_operations)) {
	// crmv@104956
	// remove quickcreate, since it's handled in a different way!
	if (in_array('QuickCreate', $module_operations)) {
		$k = array_search('QuickCreate', $module_operations);
		unset($module_operations[$k]);
		$module_operations = array_values($module_operations);
		// insert into quickcreate table
		if(Vtecrm_Utils::CheckTable($table_prefix.'_quickcreate')) {
			// make a replace!
			$adb->pquery("DELETE FROM {$table_prefix}_quickcreate WHERE tabid = ?", array($tabid));
			$adb->pquery("INSERT INTO {$table_prefix}_quickcreate (tabid) VALUES (?)", array($tabid));
			if (class_exists('Cache')) {
				$cache = Cache::getInstance('getQuickCreateModules');
				if ($cache) $cache->clear();
			}
		}
	}
	// crmv@104956e
	$toDisable = array_diff($module_all_operations, $module_operations);
	if (count($toDisable) > 0) $newModule->disableTools($toDisable);
	if (count($module_operations) > 0) $newModule->enableTools($module_operations);
}

// module sequence numbering
if (!empty($module_sequence) && is_array($module_fields)) {
	foreach ($module_fields as $f) {
		if ($f['uitype'] == 4) {
			$entityModule = CRMEntity::getInstance($module_name);
			$entityModule->setModuleSeqNumber('configure', $module_name, $module_sequence, 1);
			break;
		}
	}
}

// insert it in the area
if ($module_areaid > 0) {
	require_once('modules/Area/Area.php');
	$AManager = AreaManager::getInstance();
	if (method_exists($AManager, 'insertModulesInArea')) {
		$AManager->insertModulesInArea($module_areaid, -1, array($tabid));
	} else {
		$MMS->warn('The module was not inserted in the Area, since the method insertModulesInArea was not found');
	}
}

// create the panels
$blockRet = $MMS->createPanels($module_panels);

// create the blocks
$blockRet = $MMS->createBlocks($module_blocks);

// create the fields
$fieldRet = $MMS->createFields($module_fields);

// other fields (for relations)
if (is_array($module_other_fields)) {
	$fieldRetOther = $MMS->createFields($module_other_fields);
}

// set the module identifier
if (!empty($fieldRet[$module_mainfield])) {
	$newModule->setEntityIdentifier($fieldRet[$module_mainfield]);
} else {
	$MMS->warn("The module identifier field $module_mainfield has not been created");
}

// create the filters
$filterRet = $MMS->createFilters($module_filters);


// relations NtoN (couple of related lists)
if (is_array($module_relations)) {
	foreach ($module_relations as $rel) {
		$relinst = Vtecrm_Module::getInstance($rel['module']);
		if ($relinst) {
			// check the relation function
			if (!empty($rel['function'])) {
				$func = $rel['function'];
			} elseif ($rel['module'] == 'Messages') {
				$func = 'get_messages_list';
			} elseif ($rel['module'] == 'Documents') {
				$func = 'get_attachments';
			} else {
				$func = 'get_related_list';
			}
			// check the inverse relation function
			if (!empty($rel['reverse_function'])) {
				$func2 = $rel['reverse_function'];
			} elseif ($rel['module'] == 'Documents') {
				$func2 = 'get_documents_dependents_list';
			} else {
				$func2 = $func;
			}
			
			// no inverse related for messages
			if ($func == 'get_messages_list') {
				$inverse = false;
			} else {
				$inverse = true;
			}
			
			// create related lists
			if ($inverse) $relinst->setRelatedList($newModule, $module_name, $rel['actions'], $func2);
			$newModule->setRelatedList($relinst, $rel['module'], $rel['actions'], $func);
		}
	}
}

// relations 1toN with existing fields (eg: calendar)
if (is_array($module_addrelations)) {
	foreach ($module_addrelations as $rel) {
		$relinst = Vtecrm_Module::getInstance($rel['module']);
		if ($relinst) {
			$label = $rel['module'];
			if (!empty($rel['function'])) {
				$func = $rel['function'];
			} elseif ($rel['module'] == 'Calendar') {
				$func = 'get_activities';
			} else {
				$func = 'get_dependents_list';
			}
			
			if ($func == 'get_activities') {
				$label = 'Activities';
			}
			
			// set the related for this module
			$newModule->setRelatedList($relinst, $label, $rel['actions'], $func);
			
			// add this module to the relation field
			if ($rel['field']) {
				$field = @Vtecrm_Field::getInstance($rel['field'], $relinst);
				if ($field) {
					$field->setRelatedModules(array($module_name));
				}
				// add it also for the other module
				if ($func == 'get_activities') {
					$relEvents = Vtecrm_Module::getInstance('Events');
					if ($relEvents) {
						$field = @Vtecrm_Field::getInstance($rel['field'], $relEvents);
						if ($field) {
							$field->setRelatedModules(array($module_name));
						}
					}
				}
			}
		}
	}
	
}


// other labels
if (is_array($labels)) {
	foreach ($labels as $module=>$modlang) {
		foreach ($modlang as $lang=>$translist) {
			foreach ($translist as $label=>$translabel) {
				SDK::setLanguageEntry($module, $lang, $label, $translabel);
			}
		}
	}
}


// ----------------------------------- CLASSES ---------------------------------------

class InstallException extends Exception { }

// some useful functions for the install
class ModuleMakerScript {
	
	public $enableLog = true;
	
	public function warn($s) {
		return $this->log('[WARNING] '.$s);
	}
	
	public function log($s) {
		if ($this->enableLog) echo $s."\n";
	}
	
	// copies files and non-empty directories
	public function rcopy($src, $dst) {
		global $new_folder_storage_owner;
		if (is_dir($src)) {
			@mkdir($dst, 0755);
			if (!empty($new_folder_storage_owner)) {
				if ($new_folder_storage_owner['user'] != '') @chown($dst, $new_folder_storage_owner['user']);
				if ($new_folder_storage_owner['group'] != '') @chgrp($dst, $new_folder_storage_owner['group']);
			}
			$files = scandir($src);
			foreach ($files as $file) {
				if ($file != "." && $file != "..") {
					$r = $this->rcopy("$src/$file", "$dst/$file");
					if (!$r) return $r;
				}
			}
		} elseif (file_exists($src)) {
			$r = copy($src, $dst);
			if ($r) {
				if ($new_folder_storage_owner['user'] != '') @chown($dst, $new_folder_storage_owner['user']);
				if ($new_folder_storage_owner['group'] != '') @chgrp($dst, $new_folder_storage_owner['group']);
			}
			return $r;
		}
		return true;
	}
	
	public function createPanels($panels) {
		if (!class_exists('Update') || !method_exists(Update, 'create_panels')) {
			throw new InstallException("The method Update::create_panels has not been found, please update the VTE");
		}
		return Update::create_panels($panels);
	}
	
	public function createBlocks($blocks) {
		if (!class_exists('Update') || !method_exists(Update, 'create_blocks')) {
			throw new InstallException("The method Update::create_blocks has not been found, please update the VTE");
		}
		return Update::create_blocks($blocks);
	}
	
	public function createFields($fields) {
		if (!class_exists('Update') || !method_exists(Update, 'create_fields')) {
			throw new InstallException("The method Update::create_fields has not been found, please update the VTE");
		}
		return Update::create_fields($fields);
	}
	
	public function createFilters($filters) {
		if (!class_exists('Update') || !method_exists(Update, 'create_filters')) {
			throw new InstallException("The method Update::create_filters has not been found, please update the VTE");
		}
		return Update::create_filters($filters);
	}
	
}

