YetiForceCompany/YetiForceCRM

View on GitHub
vtlib/Vtiger/Module.php

Summary

Maintainability
C
1 day
Test Coverage
D
68%
<?php
/* +**********************************************************************************
 * The contents of this file are subject to the vtiger CRM Public License Version 1.0
 * ("License"); You may not use this file except in compliance with the License
 * The Original Code is:  vtiger CRM Open Source
 * The Initial Developer of the Original Code is vtiger.
 * Portions created by vtiger are Copyright (C) vtiger.
 * All Rights Reserved.
 * Contributor(s): YetiForce S.A.
 * ********************************************************************************** */

namespace vtlib;

/**
 * Provides API to work with vtiger CRM Modules.
 */
class Module extends ModuleBasic
{
    /**
     * Allow export.
     *
     * @var bool
     */
    public $allowExport = false;

    /**
     * Get related list sequence to use.
     *
     * @return int
     */
    public function __getNextRelatedListSequence()
    {
        return (new \App\Db\Query())->from('vtiger_relatedlists')->where(['tabid' => $this->id])->max('sequence') + 1;
    }

    /**
     * Set related list information between other module.
     *
     * @param Module Instance of target module with which relation should be setup
     * @param string Label to display in related list (default is target module name)
     * @param array List of action button to show ('ADD', 'SELECT')
     * @param string Callback function name of this module to use as handler
     * @param mixed $moduleInstance
     * @param mixed $label
     * @param mixed $actions
     * @param mixed $functionName
     * @param mixed $fieldName
     * @param mixed $fields
     */
    public function setRelatedList($moduleInstance, $label = '', $actions = false, $functionName = 'getRelatedList', $fieldName = null, $fields = [])
    {
        $db = \App\Db::getInstance();
        if (empty($moduleInstance)) {
            return;
        }
        if (empty($label)) {
            $label = $moduleInstance->name;
        }
        // Allow ADD action of other module records (default)
        if (false === $actions) {
            $actions = ['ADD'];
        }
        $useactionsText = $actions;
        if (\is_array($actions)) {
            $useactionsText = implode(',', $actions);
        }
        $useactionsText = strtoupper($useactionsText);
        $isExists = (new \App\Db\Query())
            ->select(['relation_id'])
            ->from('vtiger_relatedlists')
            ->where(['tabid' => $this->id, 'related_tabid' => $moduleInstance->id, 'name' => $functionName, 'label' => $label, 'field_name' => $fieldName])
            ->exists();
        if ($isExists) {
            \App\Log::trace("Setting relation with $moduleInstance->name [$useactionsText] ... Error, the related module already exists", __METHOD__);
            return;
        }
        $sequence = $this->__getNextRelatedListSequence();
        $presence = 0; // 0 - Enabled, 1 - Disabled

        $db->createCommand()->insert('vtiger_relatedlists', [
            'tabid' => $this->id,
            'related_tabid' => $moduleInstance->id,
            'name' => $functionName,
            'sequence' => $sequence,
            'label' => $label,
            'presence' => $presence,
            'actions' => $useactionsText,
            'field_name' => $fieldName,
        ])->execute();
        if ($fields) {
            $id = $db->getLastInsertID('vtiger_relatedlists_relation_id_seq');
            $allFields = (new \App\Db\Query())->select(['fieldid', 'fieldname'])
                ->from('vtiger_field')
                ->where(['tabid' => $moduleInstance->id])
                ->indexBy('fieldname')->all();
            foreach ($fields as $key => $value) {
                $db->createCommand()->insert('vtiger_relatedlists_fields', [
                    'relation_id' => $id,
                    'fieldid' => $allFields[$value]['fieldid'],
                    'sequence' => $key,
                ])->execute();
            }
        }
        if ('getManyToMany' === $functionName) {
            $refTableName = \Vtiger_Relation_Model::getReferenceTableInfo($moduleInstance->name, $this->name);
            $schema = $db->getSchema();
            if (!$schema->getTableSchema($refTableName['table'])) {
                $db->createTable($refTableName['table'], [
                    'crmid' => 'int',
                    'relcrmid' => 'int',
                ]);
                $db->createCommand()->createIndex("{$refTableName['table']}_crmid_idx", $refTableName['table'], 'crmid')->execute();
                $db->createCommand()->createIndex("{$refTableName['table']}_relcrmid_idx", $refTableName['table'], 'relcrmid')->execute();
                $db->createCommand()->addForeignKey(
                    "fk_1_{$refTableName['table']}", $refTableName['table'], 'crmid', 'vtiger_crmentity', 'crmid', 'CASCADE', 'RESTRICT'
                )->execute();
                $db->createCommand()->addForeignKey(
                    "fk_2_{$refTableName['table']}", $refTableName['table'], 'relcrmid', 'vtiger_crmentity', 'crmid', 'CASCADE', 'RESTRICT'
                )->execute();
            }
        }
        \App\Cache::clear();
        \App\Log::trace("Setting relation with $moduleInstance->name  ... DONE", __METHOD__);
    }

    /**
     * Unset related list information that exists with other module.
     *
     * @param \Module Instance of target module with which relation should be setup
     * @param string Label to display in related list (default is target module name)
     * @param string Callback function name of this module to use as handler
     * @param mixed $moduleInstance
     * @param mixed $label
     * @param mixed $function_name
     */
    public function unsetRelatedList($moduleInstance, $label = '', $function_name = 'getRelatedList')
    {
        if (empty($moduleInstance)) {
            return;
        }
        if (empty($label)) {
            $label = $moduleInstance->name;
        }
        $id = (new \App\Db\Query())
            ->select(['relation_id'])
            ->from('vtiger_relatedlists')
            ->where(['tabid' => $this->id, 'related_tabid' => $moduleInstance->id, 'name' => $function_name, 'label' => $label])
            ->scalar();
        $createCommand = \App\Db::getInstance()->createCommand();
        $createCommand->delete('vtiger_relatedlists', ['relation_id' => $id])->execute();
        $createCommand->delete('vtiger_relatedlists_fields', ['relation_id' => $id])->execute();
        \App\Relation::clearCacheById($id);
        \App\Log::trace("Unsetting relation with $moduleInstance->name ... DONE", __METHOD__);
    }

    /**
     * Add custom link for a module page.
     *
     * @param string Type can be like 'DETAIL_VIEW_BASIC', 'LISTVIEW' etc..
     * @param string Label to use for display
     * @param string HREF value to use for generated link
     * @param string Path to the image file (relative or absolute)
     * @param int Sequence of appearance
     *
     * NOTE: $url can have variables like $MODULE (module for which link is associated),
     * $RECORD (record on which link is dispalyed)
     * @param mixed      $type
     * @param mixed      $label
     * @param mixed      $iconpath
     * @param mixed      $sequence
     * @param mixed|null $handlerInfo
     */
    public function addLink($type, $label, $url, $iconpath = '', $sequence = 0, $handlerInfo = null)
    {
        Link::addLink($this->id, $type, $label, $url, $iconpath, $sequence, $handlerInfo);
    }

    /**
     * Delete custom link of a module.
     *
     * @param string Type can be like 'DETAIL_VIEW_BASIC', 'LISTVIEW' etc..
     * @param string Display label to lookup
     * @param string URL value to lookup
     * @param mixed $type
     * @param mixed $label
     * @param mixed $url
     */
    public function deleteLink($type, $label, $url = false)
    {
        Link::deleteLink($this->id, $type, $label, $url);
    }

    /**
     * Get all the custom links related to this module.
     */
    public function getLinks()
    {
        return Link::getAll($this->id);
    }

    /**
     * Initialize webservice setup for this module instance.
     */
    public function initWebservice()
    {
        Webservice::initialize($this);
    }

    public function createFiles(Field $entityField)
    {
        $targetpath = 'modules/' . $this->name;

        if (!is_file($targetpath)) {
            $templatepath = 'vtlib/ModuleDir/BaseModule/';
            $flags = \FilesystemIterator::KEY_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::UNIX_PATHS;
            $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($templatepath, $flags), \RecursiveIteratorIterator::SELF_FIRST);
            foreach ($objects as $name => $object) {
                $targetPath = str_replace($templatepath, '', $name);
                $targetPath = str_replace('_ModuleName_', $this->name, $targetPath);
                if (is_dir($name)) {
                    if (!is_dir($targetPath)) {
                        mkdir($targetPath, 0755);
                    }
                } else {
                    $fileContent = file_get_contents($name);
                    $replacevars = [
                        '<ModuleName>' => $this->name,
                        '<ModuleLabel>' => $this->label,
                        '<modulename>' => strtolower($this->name),
                        '<entityfieldlabel>' => $entityField->label,
                        '<entitycolumn>' => $entityField->column,
                        '<entityfieldname>' => $entityField->name,
                        '_ModuleName_' => $this->name,
                        '<_baseTableName_>' => 'u_' . (\App\Db::getInstance()->getConfig('base')['tablePrefix']) . strtolower($this->name),
                    ];
                    foreach ($replacevars as $key => $value) {
                        $fileContent = str_replace($key, addslashes($value), $fileContent);
                    }
                    file_put_contents($targetPath, $fileContent);
                }
            }
            $languages = \App\Language::getAll(false);
            $langFile = 'languages/' . \App\Language::DEFAULT_LANG . '/' . $this->name . '.json';
            foreach ($languages as $prefix => $language) {
                if (\App\Language::DEFAULT_LANG !== $prefix) {
                    copy($langFile, 'languages/' . $prefix . '/' . $this->name . '.json');
                }
            }
        }
    }

    /**
     * Get instance by id or name.
     *
     * @param mixed id or name of the module
     * @param mixed $value
     *
     * @return self
     */
    public static function getInstance($value)
    {
        $instance = false;
        $data = Functions::getModuleData($value);
        if ($data) {
            $instance = new self();
            $instance->initialize($data);
        }
        return $instance;
    }

    /**
     * Get instance of the module class.
     *
     * @param string Module name
     * @param mixed $modulename
     */
    public static function getClassInstance($modulename)
    {
        $instance = false;
        $filepath = "modules/$modulename/$modulename.php";
        if (Utils::checkFileAccessForInclusion($filepath, false)) {
            Deprecated::checkFileAccessForInclusion($filepath);
            include_once $filepath;
            if (class_exists($modulename)) {
                $instance = new $modulename();
            }
        }
        return $instance;
    }

    /**
     * Fire the event for the module (if moduleHandler is defined).
     *
     * @param mixed $modulename
     * @param mixed $eventType
     */
    public static function fireEvent($modulename, $eventType)
    {
        $return = true;
        $instance = self::getClassInstance((string) $modulename);
        if ($instance && method_exists($instance, 'moduleHandler')) {
            \App\Log::trace("Invoking moduleHandler for $eventType ...START", __METHOD__);
            $fire = $instance->moduleHandler((string) $modulename, (string) $eventType);
            if (null !== $fire && true !== $fire) {
                $return = false;
            }
            \App\Log::trace("Invoking moduleHandler for $eventType ...DONE", __METHOD__);
        }
        return $return;
    }

    /**
     * Toggle the module (enable/disable).
     *
     * @param mixed $moduleName
     * @param mixed $enableDisable
     */
    public static function toggleModuleAccess($moduleName, $enableDisable)
    {
        $eventType = false;
        if (true === $enableDisable) {
            $enableDisable = 0;
            $eventType = self::EVENT_MODULE_ENABLED;
        } elseif (false === $enableDisable) {
            $enableDisable = 1;
            $eventType = self::EVENT_MODULE_DISABLED;
        }
        $fire = self::fireEvent($moduleName, $eventType);
        if ($fire) {
            \App\Db::getInstance()->createCommand()->update('vtiger_tab', ['presence' => $enableDisable], ['name' => $moduleName])->execute();
            $tabId = \App\Module::getModuleId($moduleName);
            \App\Cache::delete('moduleTabByName', $moduleName);
            \App\Cache::delete('moduleTabById', $tabId);
            \App\Cache::delete('moduleTabs', 'all');
            \App\Cache::staticDelete('module', $moduleName);
            \App\Cache::staticDelete('module', $tabId);
            \App\Module::createModuleMetaFile();
            \Settings_GlobalPermission_Record_Model::recalculate();
            $menuRecordModel = new \Settings_Menu_Record_Model();
            $menuRecordModel->refreshMenuFiles();
        }
    }

    /**
     * Check if this module is customized.
     *
     * @return bool
     */
    public function isCustomizable(): bool
    {
        return 1 === $this->customized;
    }

    /**
     * Check if this module is upgradable.
     *
     * @return bool
     */
    public function isModuleUpgradable(): bool
    {
        return $this->isCustomizable() && 0 === $this->premium;
    }

    /**
     * Check if this module is exportable.
     *
     * @return bool
     */
    public function isExportable(): bool
    {
        return $this->allowExport || ($this->isCustomizable() && 0 === $this->premium);
    }
}