YetiForceCompany/YetiForceCRM

View on GitHub
app/CustomView.php

Summary

Maintainability
F
4 days
Test Coverage
D
65%
<?php
/**
 * Custom view file.
 *
 * @package App
 *
 * @copyright YetiForce S.A.
 * @license   YetiForce Public License 6.5 (licenses/LicenseEN.txt or yetiforce.com)
 * @author    Mariusz Krzaczkowski <m.krzaczkowski@yetiforce.com>
 * @author    Radosław Skrzypczak <r.skrzypczak@yetiforce.com>
 */

namespace App;

/**
 * Custom view class.
 */
class CustomView
{
    const CV_STATUS_DEFAULT = 0;
    const CV_STATUS_PRIVATE = 1;
    const CV_STATUS_PENDING = 2;
    const CV_STATUS_PUBLIC = 3;
    const CV_STATUS_SYSTEM = 4;

    /**
     * Do we have multiple ids.
     *
     * @param string $cvId (comma separated id list or one id)
     *
     * @return bool
     */
    public static function isMultiViewId($cvId)
    {
        return false !== strpos($cvId, ',');
    }

    /**
     * Function to get all the date filter type informations.
     *
     * @return array
     */
    public static function getDateFilterTypes()
    {
        $dateFilters = Condition::DATE_OPERATORS;
        foreach (array_keys($dateFilters) as $filterType) {
            $dateValues = \DateTimeRange::getDateRangeByType($filterType);
            $dateFilters[$filterType]['startdate'] = $dateValues[0];
            $dateFilters[$filterType]['enddate'] = $dateValues[1];
        }
        return $dateFilters;
    }

    /**
     * Get current page.
     *
     * @param string     $moduleName
     * @param int|string $viewId
     *
     * @return int
     */
    public static function getCurrentPage($moduleName, $viewId)
    {
        if (!empty($_SESSION['lvs'][$moduleName][$viewId]['start'])) {
            return $_SESSION['lvs'][$moduleName][$viewId]['start'];
        }
        return 1;
    }

    /**
     * Set current page.
     *
     * @param string     $moduleName
     * @param int|string $viewId
     * @param int        $start
     */
    public static function setCurrentPage($moduleName, $viewId, $start)
    {
        if (empty($start)) {
            unset($_SESSION['lvs'][$moduleName][$viewId]['start']);
        } else {
            $_SESSION['lvs'][$moduleName][$viewId]['start'] = $start;
        }
    }

    /**
     * Function that sets the module filter in session.
     *
     * @param string     $moduleName - module name
     * @param int|string $viewId     - filter id
     */
    public static function setCurrentView($moduleName, $viewId)
    {
        $_SESSION['lvs'][$moduleName]['viewname'] = $viewId;
    }

    /**
     * Function that reads current module filter.
     *
     * @param string $moduleName - module name
     *
     * @return int|string
     */
    public static function getCurrentView($moduleName)
    {
        return $_SESSION['lvs'][$moduleName]['viewname'] ?? null;
    }

    /**
     * Get sorted by.
     *
     * @param string $moduleName
     *
     * @return string
     */
    public static function getSortBy($moduleName)
    {
        return empty($_SESSION['lvs'][$moduleName]['sortby']) ? [] : $_SESSION['lvs'][$moduleName]['sortby'];
    }

    /**
     * Set sorted by.
     *
     * @param string $moduleName
     * @param mixed  $sortBy
     */
    public static function setSortBy(string $moduleName, $sortBy)
    {
        if (empty($sortBy)) {
            unset($_SESSION['lvs'][$moduleName]['sortby']);
        } else {
            $_SESSION['lvs'][$moduleName]['sortby'] = $sortBy;
        }
    }

    /**
     * Has view changed.
     *
     * @param string     $moduleName
     * @param int|string $viewId
     *
     * @return bool
     */
    public static function hasViewChanged(string $moduleName, $viewId = false): bool
    {
        return empty($_SESSION['lvs'][$moduleName]['viewname'])
        || ($viewId && ($viewId !== $_SESSION['lvs'][$moduleName]['viewname']))
        || !isset($_SESSION['lvs'][$moduleName]['sortby']);
    }

    /**
     * Static Function to get the Instance of CustomView.
     *
     * @param string $moduleName
     * @param mixed  $userModelOrId
     *
     * @return \self
     */
    public static function getInstance($moduleName, $userModelOrId = false)
    {
        if (!$userModelOrId) {
            $userModelOrId = User::getCurrentUserId();
        }
        if (is_numeric($userModelOrId)) {
            $userModel = User::getUserModel($userModelOrId);
        } else {
            $userModel = $userModelOrId;
        }
        $cacheName = $moduleName . '.' . $userModel->getId();
        if (\App\Cache::staticHas('AppCustomView', $cacheName)) {
            return \App\Cache::staticGet('AppCustomView', $cacheName);
        }
        $instance = new self();
        $instance->moduleName = $moduleName;
        $instance->user = $userModel;
        \App\Cache::staticSave('AppCustomView', $cacheName, $instance);

        return $instance;
    }

    /** @var \Vtiger_Module_Model */
    private $module;
    private $moduleName;
    private $user;
    private $defaultViewId;

    /**
     * Gets module object.
     *
     * @return false|\Vtiger_Module_Model
     */
    public function getModule()
    {
        if (!$this->module) {
            $this->module = \Vtiger_Module_Model::getInstance($this->moduleName);
        }
        return $this->module;
    }

    /**
     * Get custom view from file.
     *
     * @param string $cvId
     *
     * @throws Exceptions\AppException
     */
    private function getCustomViewFromFile($cvId)
    {
        \App\Log::trace(__METHOD__ . ' - ' . $cvId);
        $handlerClass = \Vtiger_Loader::getComponentClassName('Filter', $cvId, $this->moduleName);
        $filter = new $handlerClass();
        Cache::staticSave('getCustomView', $cvId, $filter);
        return $filter;
    }

    /**
     * Get custom view from file.
     *
     * @param string $cvIds (comma separated multi cdIds)
     *
     * @throws Exceptions\AppException
     */
    public function getCustomView($cvIds)
    {
        \App\Log::trace(__METHOD__ . ' - ' . $cvIds);
        if (Cache::staticHas('getCustomView', $cvIds)) {
            return Cache::staticGet('getCustomView', $cvIds);
        }
        if (empty($cvIds) || !static::isMultiViewId($cvIds)) {
            return $this->getCustomViewFromFile($cvIds);
        }
        $filters = [];
        foreach (explode(',', $cvIds) as $cvId) {
            $filters[] = $this->getCustomViewFromFile($cvId);
        }
        Cache::staticSave('getCustomView', $cvIds, $filters);
        return $filters;
    }

    /**
     * Columns list by cvid.
     *
     * @param mixed $cvId
     *
     * @throws Exceptions\AppException
     *
     * @return array
     */
    private function getColumnsByCvidFromDb($cvId)
    {
        \App\Log::trace(__METHOD__ . ' - ' . $cvId);
        $columnList = [];
        if (is_numeric($cvId)) {
            $dataReader = (new Db\Query())->select(['field_name', 'module_name', 'source_field_name', 'label'])
                ->from('vtiger_cvcolumnlist')
                ->innerJoin('vtiger_tab', 'vtiger_tab.name=vtiger_cvcolumnlist.module_name')
                ->innerJoin('vtiger_field', 'vtiger_tab.tabid = vtiger_field.tabid AND vtiger_field.fieldname = vtiger_cvcolumnlist.field_name')
                ->where(['cvid' => $cvId, 'vtiger_field.presence' => [0, 2]])->orderBy('columnindex')->createCommand()->query();
            while ($row = $dataReader->read()) {
                if (!empty($row['source_field_name']) && !$this->getModule()->getFieldByName($row['source_field_name'])->isActiveField()) {
                    continue;
                }
                $columnList[] = $row;
            }
            $dataReader->close();
            if ($columnList) {
                Cache::save('getColumnsListByCvid', $cvId, $columnList);
            }
        } else {
            $view = $this->getCustomViewFromFile($cvId);
            $columnList = $view->getColumnList();
            Cache::save('getColumnsListByCvid', $cvId, $columnList);
        }
        return $columnList;
    }

    /**
     * Columns list by cvid.
     *
     * @param mixed $cvIds (comma separated)
     *
     * @throws Exceptions\AppException
     *
     * @return array
     */
    public function getColumnsListByCvid($cvIds)
    {
        \App\Log::trace(__METHOD__ . ' - ' . $cvIds);
        if (Cache::has('getColumnsListByCvid', $cvIds)) {
            return Cache::get('getColumnsListByCvid', $cvIds);
        }
        if (empty($cvIds) || !static::isMultiViewId($cvIds)) {
            return $this->getColumnsByCvidFromDb($cvIds);
        }
        $columnLists = [];
        foreach (explode(',', $cvIds) as $cvId) {
            $columnLists[] = $this->getColumnsByCvidFromDb($cvId);
        }
        Cache::save('getColumnsListByCvid', $cvIds, $columnLists);
        return $columnLists;
    }

    /**
     * Returns conditions for filter.
     *
     * @param int|string $id
     *
     * @return array
     *               [
     *               'condition' => "AND" or "OR"
     *               'rules' => [[
     *               'fieldname' => name of fields
     *               'operator' => operator, for instance: 'e'
     *               'value' => values
     *               ]]
     *               ]
     */
    public static function getConditions($id): array
    {
        if (Cache::has('CustomView_GetConditions', $id)) {
            return Cache::get('CustomView_GetConditions', $id);
        }
        $dataReader = (new \App\Db\Query())->select([
            'u_#__cv_condition.group_id',
            'u_#__cv_condition.field_name',
            'u_#__cv_condition.module_name',
            'u_#__cv_condition.source_field_name',
            'u_#__cv_condition.operator',
            'u_#__cv_condition.value',
            'condition_index' => 'u_#__cv_condition.index',
            'u_#__cv_condition_group.id',
            'u_#__cv_condition_group.condition',
            'u_#__cv_condition_group.parent_id',
            'group_index' => 'u_#__cv_condition_group.index',
        ])->from('u_#__cv_condition_group')
            ->leftJoin('u_#__cv_condition', 'u_#__cv_condition.group_id = u_#__cv_condition_group.id')
            ->where(['u_#__cv_condition_group.cvid' => $id])
            ->orderBy(['u_#__cv_condition_group.parent_id' => SORT_ASC])
            ->createCommand()->query();
        $referenceGroup = $referenceParent = $conditions = [];
        while ($condition = $dataReader->read()) {
            if ($condition['group_id']) {
                $isEmptyCondition = false;
            } else {
                $condition['group_id'] = $condition['id'];
                $isEmptyCondition = true;
            }
            $value = $condition['value'];
            $fieldName = "{$condition['field_name']}:{$condition['module_name']}" . ($condition['source_field_name'] ? ':' . $condition['source_field_name'] : '');
            if (isset($referenceParent[$condition['parent_id']], $referenceGroup[$condition['group_id']])) {
                $referenceParent[$condition['parent_id']][$condition['condition_index']] = [
                    'fieldname' => $fieldName,
                    'operator' => $condition['operator'],
                    'value' => $value,
                ];
            } elseif (isset($referenceGroup[$condition['parent_id']])) {
                if ($isEmptyCondition) {
                    $referenceGroup[$condition['parent_id']][$condition['group_index']] = [
                        'condition' => $condition['condition'],
                        'rules' => [],
                    ];
                } else {
                    $referenceGroup[$condition['parent_id']][$condition['group_index']] = [
                        'condition' => $condition['condition'],
                        'rules' => [
                            $condition['condition_index'] => [
                                'fieldname' => $fieldName,
                                'operator' => $condition['operator'],
                                'value' => $value,
                            ],
                        ],
                    ];
                }
                $referenceParent[$condition['parent_id']] = &$referenceGroup[$condition['parent_id']][$condition['group_index']]['rules'];
                $referenceGroup[$condition['group_id']] = &$referenceGroup[$condition['parent_id']][$condition['group_index']]['rules'];
            } else {
                if ($isEmptyCondition) {
                    $conditions = [
                        'condition' => $condition['condition'],
                        'rules' => [],
                    ];
                } else {
                    $conditions = [
                        'condition' => $condition['condition'],
                        'rules' => [
                            $condition['condition_index'] => [
                                'fieldname' => $fieldName,
                                'operator' => $condition['operator'],
                                'value' => $value,
                            ],
                        ],
                    ];
                }
                $referenceParent[$condition['parent_id']] = &$conditions['rules'];
                $referenceGroup[$condition['group_id']] = &$conditions['rules'];
            }
        }
        $conditions = static::sortConditions($conditions);
        Cache::save('CustomView_GetConditions', $id, $conditions, Cache::LONG);
        return $conditions;
    }

    /**
     * Sorting conditions.
     *
     * @param array|null $array
     * @param ?array     $arrayToSort
     *
     * @return array|null
     */
    private static function sortConditions(?array $arrayToSort): ?array
    {
        if (isset($arrayToSort['rules'])) {
            ksort($arrayToSort['rules']);
            foreach ($arrayToSort['rules'] as $rule) {
                if (isset($rule['condition'])) {
                    static::sortConditions($rule);
                }
            }
        }
        return $arrayToSort;
    }

    /**
     * Get fields to detect duplicates.
     *
     * @param int|string $viewId
     *
     * @return array
     */
    public static function getDuplicateFields($viewId): array
    {
        if (!is_numeric($viewId)) {
            return [];
        }
        if (Cache::has('CustomView_GetDuplicateFields', $viewId)) {
            return Cache::get('CustomView_GetDuplicateFields', $viewId);
        }
        $data = (new \App\Db\Query())->select(['vtiger_field.fieldname', 'u_#__cv_duplicates.ignore'])
            ->from('u_#__cv_duplicates')
            ->innerJoin('vtiger_field', 'vtiger_field.fieldid = u_#__cv_duplicates.fieldid')
            ->where(['u_#__cv_duplicates.cvid' => $viewId])->all();
        Cache::save('CustomView_GetDuplicateFields', $viewId, $data);
        return $data;
    }

    /**
     * To get the customViewId of the specified module.
     *
     * @param mixed $noCache
     *
     * @return int|string
     */
    public function getViewId($noCache = false)
    {
        \App\Log::trace(__METHOD__);
        if (isset($this->defaultViewId)) {
            return $this->defaultViewId;
        }
        if ($noCache || Request::_isEmpty('viewname')) {
            $viewId = null;
            if (Request::_has('mid')) {
                $viewId = current(self::getModuleFiltersByMenuId(Request::_getInteger('mid'), $this->moduleName));
            }
            if (empty($viewId) && !$noCache && self::getCurrentView($this->moduleName)) {
                $viewId = self::getCurrentView($this->moduleName);
                if (empty($this->getFilterInfo($viewId))) {
                    $viewId = null;
                }
            }
            if (empty($viewId)) {
                $viewId = $this->getDefaultCvId();
            }
            if (empty($viewId) || !$this->isPermittedCustomView($viewId)) {
                $viewId = $this->getMandatoryFilter();
            }
        } else {
            $viewId = Request::_get('viewname');
            if (!is_numeric($viewId)) {
                if ('All' === $viewId) {
                    $viewId = $this->getMandatoryFilter();
                } else {
                    $viewId = $this->getViewIdByName($viewId);
                }
                if (!$viewId) {
                    $viewId = $this->getDefaultCvId();
                }
            } else {
                $viewId = (int) $viewId;
                if (!$this->isPermittedCustomView($viewId)) {
                    throw new Exceptions\NoPermitted('ERR_NO_PERMITTED_TO_VIEW');
                }
            }
        }
        $this->defaultViewId = $viewId;
        return $viewId;
    }

    /**
     * Get default cvId.
     *
     * @return int|string
     */
    public function getDefaultCvId()
    {
        $cacheName = $this->moduleName . $this->user->getId();
        if (Cache::has('GetDefaultCvId', $cacheName)) {
            return Cache::get('GetDefaultCvId', $cacheName);
        }
        $query = (new Db\Query())->select(['userid', 'default_cvid'])->from('vtiger_user_module_preferences')->where(['tabid' => Module::getModuleId($this->moduleName)]);
        $data = $query->createCommand()->queryAllByGroup();
        $defaultCvId = null;
        foreach ($this->user->getMemberStructure() as $member) {
            if (isset($data[$member]) && $this->isPermittedCustomView($data[$member])) {
                $defaultCvId = $data[$member];
                break;
            }
        }
        if (!$defaultCvId) {
            foreach ($this->getFilters() as $cvId => $values) {
                if (1 === $values['setdefault'] && $this->isPermittedCustomView($cvId)) {
                    $defaultCvId = $cvId;
                    break;
                }
            }
            if (!$defaultCvId) {
                $defaultCvId = $this->getMandatoryFilter();
            }
        }
        Cache::save('GetDefaultCvId', $cacheName, $defaultCvId);
        return $defaultCvId;
    }

    /**
     * Function to check if the current user is able to see the customView.
     *
     * @param int|string $viewId
     *
     * @return bool
     */
    public function isPermittedCustomView($viewId)
    {
        return self::isPermitted($viewId, $this->moduleName, $this->user->getId());
    }

    /**
     * Get mandatory filter by module.
     *
     * @param bolean $returnData
     *
     * @return array|int
     */
    public function getMandatoryFilter($returnData = false)
    {
        Log::trace(__METHOD__);
        $info = $this->getFilters();
        $returnValue = '';
        foreach ($info as $index => &$values) {
            if (0 === $values['presence']) {
                $returnValue = $index;
                break;
            }
            if (2 === $values['presence']) {
                $returnValue = $index;
            }
        }
        return $returnData ? $info[$returnValue] : $returnValue;
    }

    /**
     * Get viewId by name.
     *
     * @param string $viewName
     *
     * @return int|null
     */
    public function getViewIdByName(string $viewName): ?int
    {
        $viewId = null;
        foreach ($this->getFilters() as $cvId => &$values) {
            if ($values['viewname'] === $viewName) {
                $viewId = $cvId;
                break;
            }
        }
        return $viewId;
    }

    /**
     * Function to get basic information about filter.
     *
     * @param int $cvId
     *
     * @return array
     */
    public function getFilterInfo(int $cvId): array
    {
        return $this->getFilters()[$cvId] ?? [];
    }

    /**
     * Function to get basic information about all filters.
     *
     * @param string $moduleName
     *
     * @return array
     */
    public function getFilters(): array
    {
        return self::getFiltersByModule($this->moduleName);
    }

    /**
     * Check permissions.
     *
     * @param int    $cvId
     * @param string $moduleName
     * @param int    $userId
     *
     * @return bool
     */
    public static function isPermitted(int $cvId, string $moduleName = null, int $userId = null): bool
    {
        $userModel = $userId ? \App\User::getUserModel($userId) : \App\User::getCurrentUserModel();
        return ($data = self::getCVDetails($cvId, $moduleName))
        && ($userModel->isAdmin()
        || $data['userid'] === $userModel->getId()
        || 0 === $data['presence']
        || \in_array($data['status'], [self::CV_STATUS_DEFAULT, self::CV_STATUS_PUBLIC])
        || (self::CV_STATUS_PRIVATE === $data['status'] && array_intersect($userModel->getMemberStructure(), $data['members'])));
    }

    /**
     * Function to get basic information about all filters for module.
     *
     * @param string $moduleName
     *
     * @return array
     */
    public static function getFiltersByModule(string $moduleName): array
    {
        if (Cache::has('CustomViewInfo', $moduleName)) {
            return Cache::get('CustomViewInfo', $moduleName);
        }
        $members = (new Db\Query())->select(['u_#__cv_privileges.cvid', 'member'])->from('u_#__cv_privileges')
            ->innerJoin('vtiger_customview', 'u_#__cv_privileges.cvid=vtiger_customview.cvid')
            ->where(['entitytype' => $moduleName])->createCommand()->queryAllByGroup(2);
        $info = (new Db\Query())->from('vtiger_customview')->where(['entitytype' => $moduleName])->indexBy('cvid')->orderBy(['sequence' => SORT_ASC])->all();
        foreach ($info as &$item) {
            $item['cvid'] = (int) $item['cvid'];
            $item['setdefault'] = (int) $item['setdefault'];
            $item['setmetrics'] = (int) $item['setmetrics'];
            $item['status'] = (int) $item['status'];
            $item['privileges'] = (int) $item['privileges'];
            $item['featured'] = (int) $item['featured'];
            $item['presence'] = (int) $item['presence'];
            $item['sequence'] = (int) $item['sequence'];
            $item['userid'] = (int) $item['userid'];
            $item['members'] = $members[$item['cvid']] ?? [];
            Cache::save('CustomViewInfo', $item['cvid'], $item);
        }
        Cache::save('CustomViewInfo', $moduleName, $info);
        return $info;
    }

    /**
     * Reset current views configuration in session.
     *
     * @param string|bool $moduleName
     */
    public static function resetCurrentView($moduleName = false)
    {
        if (\App\Session::has('lvs')) {
            if ($moduleName) {
                $lvs = \App\Session::get('lvs');
                if (isset($lvs[$moduleName])) {
                    unset($lvs[$moduleName]);
                    \App\Session::set('lvs', $lvs);
                }
            } else {
                \App\Session::set('lvs', []);
            }
        }
    }

    /**
     * Get module filters by menu id.
     *
     * @param int    $menuId
     * @param string $moduleName
     *
     * @return array
     */
    public static function getModuleFiltersByMenuId(int $menuId, string $moduleName = ''): array
    {
        $cacheKey = 'getModuleFiltersByMenuId' . $moduleName;
        if (\App\Cache::staticHas($cacheKey, $menuId)) {
            return \App\Cache::staticGet($cacheKey, $menuId);
        }
        $filters = [];
        $userModel = User::getCurrentUserModel();
        $roleMenu = 'user_privileges/menu_' . filter_var($userModel->getDetail('roleid'), FILTER_SANITIZE_NUMBER_INT) . '.php';
        file_exists($roleMenu) ? require $roleMenu : require 'user_privileges/menu_0.php';
        if (0 === \count($menus) && file_exists($roleMenu)) {
            require 'user_privileges/menu_0.php';
        }
        if (isset($filterList[$menuId])) {
            $filtersMenu = explode(',', $filterList[$menuId]['filters']);
            $filtersCustomView = array_keys(\CustomView_Record_Model::getAll($moduleName));
            $filters = array_intersect($filtersMenu, $filtersCustomView);
        }
        \App\Cache::staticSave($cacheKey, $menuId, $filters);
        return $filters;
    }

    /**
     * Get custom view by ID.
     *
     * @param int $cvId
     *
     * @return array
     */
    public static function getCustomViewById(int $cvId): array
    {
        if (Cache::has('CustomViewById', $cvId)) {
            return Cache::get('CustomViewById', $cvId);
        }
        $data = (new Db\Query())->from('vtiger_customview')->where(['cvid' => $cvId])->one() ?: [];
        if (!empty($data['advanced_conditions'])) {
            $data['advanced_conditions'] = \App\Json::decode($data['advanced_conditions']);
        }
        Cache::save('CustomViewById', $cvId, $data);
        return $data;
    }

    /**
     * Gets custom view details by ID.
     *
     * @param int         $cvId
     * @param string|null $moduleName
     *
     * @return array
     */
    public static function getCVDetails(int $cvId, string $moduleName = null): array
    {
        if (Cache::has('CustomViewInfo', $cvId)) {
            return Cache::get('CustomViewInfo', $cvId);
        }
        if (!$moduleName) {
            $moduleName = self::getCustomViewById($cvId)['entitytype'] ?? '';
        }
        return $moduleName ? (self::getFiltersByModule($moduleName)[$cvId] ?? []) : [];
    }

    /**
     * Function clear cache by custom view ID.
     *
     * @param int         $cvId
     * @param string|null $moduleName
     *
     * @return void
     */
    public static function clearCacheById(int $cvId, string $moduleName = null): void
    {
        Cache::delete('CustomViewById', $cvId);
        Cache::delete('CustomViewInfo', $cvId);
        Cache::delete('getAllFilterColors', false);
        Cache::delete('getAllFilterColors', true);
        if (null === $moduleName) {
            foreach (\App\Module::getAllModuleNames() as $moduleName) {
                Cache::delete('CustomViewInfo', $moduleName);
            }
        } else {
            Cache::delete('CustomViewInfo', $moduleName);
        }
    }
}