YetiForceCompany/YetiForceCRM

View on GitHub
app/Export/Records.php

Summary

Maintainability
D
2 days
Test Coverage
F
0%
<?php
/**
 * Abstract base view controller file.
 *
 * @package   Export
 *
 * @copyright YetiForce S.A.
 * @license   YetiForce Public License 3.0 (licenses/LicenseEN.txt or yetiforce.com)
 * @author    Adrian Kon <a.kon@yetiforce.com>
 * @author    Radosław Skrzypczak <r.skrzypczak@yetiforce.com>
 */

namespace App\Export;

/**
 * Abstract export records class.
 */
abstract class Records extends \App\Base
{
    /** @var int Data export in a format that can be imported later */
    public const EXPORT_FORMAT = 0;

    /** @var int Data export in user format */
    public const USER_FORMAT = 1;

    /** @var string Module name */
    protected $moduleName;

    /** @var \Vtiger_Module_Model Module model. */
    protected $moduleInstance;

    /** @var \Vtiger_Field_Model[] Field model instance. */
    protected $moduleFieldInstances;

    /** @var string File extension */
    protected $fileExtension = '';

    /** @var array Headers field */
    protected $headers = [];

    /** @var array Fields for export */
    protected $fields = [];

    /** @var int Limit of exported entries */
    protected $limit;

    /** @var bool Export all data */
    protected $format = self::EXPORT_FORMAT;

    /** @var bool Export all data */
    public $fullData = false;

    /**
     * Get instance.
     *
     * @param string $moduleName
     * @param string $exportType
     *
     * @return \self
     */
    public static function getInstance(string $moduleName, string $exportType = 'csv')
    {
        if ('csv' === $exportType || empty($exportType)) {
            $componentName = 'ExportToCsv';
        } elseif ('xls' === $exportType || 'xlsx' === $exportType || 'ods' === $exportType) {
            $componentName = 'ExportToSpreadsheet';
        } else {
            $componentName = 'ExportTo' . ucfirst($exportType);
        }

        $modelClassName = \Vtiger_Loader::getComponentClassName('Model', $componentName, $moduleName);
        $instance = new $modelClassName();
        $instance->fileExtension = $exportType;
        $instance->moduleName = $moduleName;
        $instance->moduleInstance = \Vtiger_Module_Model::getInstance($moduleName);
        $instance->moduleFieldInstances = $instance->moduleInstance->getFields();
        $instance->queryGenerator = new \App\QueryGenerator($moduleName);

        return $instance;
    }

    /**
     * Get supported file formats.
     *
     * @param string $moduleName
     *
     * @return array
     */
    public static function getSupportedFileFormats(string $moduleName): array
    {
        return \App\Config::module($moduleName, 'EXPORT_SUPPORTED_FILE_FORMATS') ?? [
            'LBL_CSV' => 'csv',
            'LBL_XML' => 'xml',
            'LBL_XLS' => 'xls',
        ];
    }

    /**
     * Function exports the data.
     */
    abstract public function exportData();

    /**
     * Set the format of the exported data.
     *
     * @param int $format self::EXPORT_FORMAT or self::USER_FORMAT
     *
     * @return $this
     */
    public function setFormat(int $format)
    {
        $this->format = $format;

        return $this;
    }

    /**
     * Get query generator object.
     *
     * @return \App\QueryGenerator
     */
    public function getQueryGenerator(): \App\QueryGenerator
    {
        return $this->queryGenerator;
    }

    /**
     * Set.
     *
     * @param array $fields
     *
     * @return $this
     */
    public function setFields(array $fields)
    {
        foreach ($fields as $fieldName) {
            $this->setField($fieldName);
        }

        return $this;
    }

    /**
     * Load fields from custom view.
     *
     * @param int $cvId
     *
     * @return void
     */
    public function loadFieldsFromCvId(int $cvId): void
    {
        foreach (\App\CustomView::getInstance($this->moduleName)->getColumnsListByCvid($cvId) as $fieldInfo) {
            ['field_name' => $relatedFieldName, 'module_name' => $relatedModule, 'source_field_name' => $referenceField] = $fieldInfo;
            $cvFieldData = $referenceField ? "{$relatedFieldName}:{$relatedModule}:{$referenceField}" : $relatedFieldName;
            if (($fieldModel = $this->setField($cvFieldData)) && $fieldInfo['label']) {
                $fieldModel->set('isLabelCustomized', true);
                $fieldModel->set('label', $fieldInfo['label']);
            }
        }
    }

    /**
     * Set field model.
     *
     * @param string $fieldName
     *
     * @return Vtiger_Field_Model|false
     */
    public function setField(string $fieldName)
    {
        $fieldModel = null;
        [$relatedFieldName, $relatedModule, $referenceField] = array_pad(explode(':', $fieldName), 3, null);

        if ($referenceField) {
            $relatedFieldModel = \Vtiger_Module_Model::getInstance($relatedModule)->getFieldByName($relatedFieldName);
            if (!isset($this->moduleFieldInstances[$referenceField]) || !$relatedFieldModel) {
                throw new \App\Exceptions\IllegalValue("ERR_FIELD_NOT_FOUND||{$relatedFieldName}");
            }
            $fieldModel = clone $relatedFieldModel;
            $fieldModel->set('source_field_name', $referenceField);
        } elseif (!empty($this->moduleFieldInstances[$relatedFieldName])) {
            $fieldModel = clone $this->moduleFieldInstances[$relatedFieldName];
        }

        if ($fieldModel && $fieldModel->isExportable()) {
            $result = $this->fields[$fieldModel->getFullName()] = $fieldModel;
        } else {
            $result = false;
        }

        return $result;
    }

    /**
     * Set a limit for exported data.
     *
     * @param int $limit
     *
     * @return $this
     */
    public function setLimit(int $limit)
    {
        $this->limit = $limit;

        return $this;
    }

    /**
     * Get file headers.
     *
     * @return array
     */
    public function getHeaders(): array
    {
        if (!$this->headers) {
            if ($this->fullData) {
                $this->headers = $this->getAllModuleFieldsAsHeaders();
            } else {
                foreach ($this->fields as $fieldModel) {
                    $label = $fieldModel->getFullLabelTranslation($this->moduleInstance);
                    $this->headers[] = \App\Purifier::decodeHtml($label);
                }
            }
        }

        return $this->headers;
    }

    /**
     * Get fields for query.
     *
     * @return array
     */
    public function getFieldsForQuery(): array
    {
        if ($this->fullData) {
            $this->setFields(array_keys($this->moduleFieldInstances));
        }

        return $this->fields;
    }

    /**
     * Function returns all module fields as headers.
     *
     * @return array
     */
    public function getAllModuleFieldsAsHeaders(): array
    {
        $headers = [];
        $exportBlockName = \App\Config::component('Export', 'BLOCK_NAME');
        foreach ($this->moduleFieldInstances as $fieldModel) {
            if ($fieldModel->isExportable()) {
                $header = \App\Language::translate(\App\Purifier::decodeHtml($fieldModel->getFieldLabel()), $this->moduleName);
                if ($exportBlockName) {
                    $header = \App\Language::translate(\App\Purifier::decodeHtml($fieldModel->getBlockName()), $this->moduleName) . '::' . $header;
                }
                $headers[] = $header;
            }
        }
        if ($this->moduleInstance->isInventory()) {
            $inventoryModel = \Vtiger_Inventory_Model::getInstance($this->moduleName);
            $inventoryFields = $inventoryModel->getFields();
            $headers[] = 'Inventory::recordIteration';
            foreach ($inventoryFields as $field) {
                $headers[] = 'Inventory::' . \App\Language::translate(\App\Purifier::decodeHtml($field->get('label')), $this->moduleName);
                foreach ($field->getCustomColumn() as $columnName => $dbType) {
                    $headers[] = 'Inventory::' . $columnName;
                }
            }
        }
        return $headers;
    }

    /**
     * Function that generates Export Query object.
     *
     * @return \App\Db\Query
     */
    public function getExportQuery(): \App\Db\Query
    {
        $queryGenerator = $this->getQueryGenerator();
        $queryGenerator->clearFields()->setLimit($this->limit);
        $queryFields = $this->getFieldsForQuery();
        if (!$queryFields) {
            $queryGenerator->setFields(['id'])->addCondition('id', 0, 'e');
        }
        foreach (array_keys($queryFields) as $fieldName) {
            $queryGenerator->setField($fieldName);
        }
        return $queryGenerator->createQuery();
    }

    /**
     * This function takes in an array of values for an user and sanitizes it for export
     * Requires modification after adding a new field type.
     *
     * @param array $recordValues
     */
    public function sanitizeValues(array $recordValues): array
    {
        if (self::USER_FORMAT === $this->format) {
            $valuesToReturn = $this->getDataInUserFormat($recordValues);
        } else {
            $valuesToReturn = $this->getDataInExportFormat($recordValues);
        }

        return $valuesToReturn;
    }

    /**
     * Sanitize inventory values.
     *
     * @param array $inventoryRow
     * @param array $inventoryFields
     *
     * @return array
     */
    public function sanitizeInventoryValues(array $inventoryRow, array $inventoryFields): array
    {
        $inventoryEntries = [];
        foreach ($inventoryFields as $columnName => $field) {
            $value = $inventoryRow[$columnName];
            if (\in_array($field->getType(), ['Name', 'Reference'])) {
                $value = trim($value);
                if (!empty($value)) {
                    $recordModule = \App\Record::getType($value);
                    $displayValue = \App\Record::getLabel($value);
                    if (!empty($recordModule) && !empty($displayValue)) {
                        $value = $recordModule . '::::' . $displayValue;
                    } else {
                        $value = '';
                    }
                } else {
                    $value = '';
                }
            } elseif ('Currency' === $field->getType()) {
                $value = $field->getDisplayValue($value);
            }

            $inventoryEntries['inv_' . $columnName] = $value;
            foreach ($field->getCustomColumn() as $customColumnName => $dbType) {
                $valueParam = $inventoryRow[$customColumnName];
                if ('currencyparam' === $customColumnName) {
                    $field = $inventoryFields['currency'];
                    $valueData = $field->getCurrencyParam([], $valueParam);
                    if (\is_array($valueData)) {
                        $valueNewData = [];
                        foreach ($valueData as $currencyId => $data) {
                            $currencyName = \App\Fields\Currency::getById($currencyId)['currency_name'];
                            $valueNewData[$currencyName] = $data;
                        }
                        $valueParam = \App\Json::encode($valueNewData);
                    }
                }
                $inventoryEntries['inv_' . $customColumnName] = $valueParam;
            }
        }
        return $inventoryEntries;
    }

    /**
     * Get data from record in display format.
     *
     * @param array $recordValues
     *
     * @return array
     */
    public function getDataInUserFormat(array $recordValues): array
    {
        $response = [];
        foreach ($this->fields as $dbKey => $fieldModel) {
            $idKey = 'id';
            if ($fieldModel->get('source_field_name')) {
                $name = $fieldModel->get('source_field_name') . $fieldModel->getModuleName();
                $idKey = $name . $idKey;
                $dbKey = $name . $fieldModel->getName();
            }

            $response[$fieldModel->getFullName()] = $this->getDisplayValue($fieldModel, $recordValues[$dbKey], $recordValues[$idKey] ?? 0, $recordValues);
        }

        return $response;
    }

    /**
     * Function returns record data in export format.
     *
     * @param array $recordValues
     *
     * @return array
     */
    public function getDataInExportFormat(array $recordValues): array
    {
        $response = [];
        foreach ($this->fields as $dbKey => $fieldModel) {
            $idKey = 'id';
            if ($fieldModel->get('source_field_name')) {
                $name = $fieldModel->get('source_field_name') . $fieldModel->getModuleName();
                $idKey = $name . $idKey;
                $dbKey = $name . $fieldModel->getName();
            }

            $response[$fieldModel->getFullName()] = $fieldModel->getUITypeModel()->getValueToExport($recordValues[$dbKey], $recordValues[$idKey] ?? 0);
        }

        return $response;
    }

    /**
     * Get display value.
     *
     * @param \Vtiger_Field_Model $fieldModel
     * @param mixed               $value
     * @param int                 $recordId
     * @param array               $rowData
     * @param int                 $length
     *
     * @return string
     */
    public function getDisplayValue(\Vtiger_Field_Model $fieldModel, $value, int $recordId, array $rowData, $length = 65000)
    {
        if ('sharedOwner' === $fieldModel->getFieldDataType() && $recordId) {
            $value = implode(',', \App\Fields\SharedOwner::getById($recordId));
        }
        $returnValue = $fieldModel->getDisplayValue($value, $recordId, null, true, $length);
        if (\is_string($returnValue)) {
            $returnValue = \App\Purifier::decodeHtml($returnValue);
        }

        return $returnValue && 'text' === $fieldModel->getFieldDataType() ? strip_tags($returnValue) : $returnValue;
    }

    /**
     * Send HTTP Header.
     *
     * @return void
     */
    public function sendHttpHeader(): void
    {
        header("content-disposition: attachment; filename=\"{$this->getFileName()}\"");
        header("content-type: {$this->getExportContentType()}; charset=UTF-8");
        header('expires: Mon, 31 Dec 2000 00:00:00 GMT');
        header('last-modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
        if (isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE')) {
            header('pragma: public');
        }
        header('cache-control: post-check=0, pre-check=0', false);
    }

    /**
     * Function returns the export type - This can be extended to support different file exports.
     *
     * @return string
     */
    public function getExportContentType(): string
    {
        return \App\Fields\File::getMimeContentType($this->getFileName());
    }

    /**
     * @param string $moduleName
     *
     * @return string
     */
    public function getFileName(): string
    {
        return \App\Utils::sanitizeSpecialChars(\App\Purifier::decodeHtml(\App\Language::translate($this->moduleName, $this->moduleName))) . ".{$this->fileExtension}";
    }
}