imagecms/ImageCMS

View on GitHub
application/modules/import_export/import.php

Summary

Maintainability
B
6 hrs
Test Coverage
<?php

/* * *********************
 * Дать права 0777 на папки:
 * /uploads
 * /uploads/origin
 * /uploads/origin/additional
 * /uploads/shop/products/origin
 * /uploads/shop/products/origin/additional
 *
 * /application/backups
 * /application/backups
 * **********************
 *
 * Дополнительные фото и фото вариантов.
 *
 * Фото вариантов должны содержаться в папку /uploads/origin/
 * Если их нет в этой папке, то производится проверка на наличие их в
 * папке /uploads/shop/products/origin/. Если там они присутствуют, то вносятся
 * в базу.
 *
 * Дополнительные фото продукта должны содержаться в /uploads/origin/additional
 * Если их нет в этой папке, то производится проверка на наличие их в
 * папке /uploads/shop/products/origin/additional. Если там они присутствуют,
 * то вносятся в базу.
 *
 * Доступен импорт картинок по ссылкам из интернета.
 * В поле vimg должно быть http://сайт/picture.png (jpg|gif|jpeg)
 * Имя картинки формируется из имени домена и названия картинки (сайт_picture.png)
 * Картинка сохраняется в папку /uploads/origin, копируются в /uploads/shop/products/origin
 * и заносятся в базу.
 * Если картинка с таким названием уже существует, шаг пропускается.
 *
 * Доступен импорт дополнительных картинок по ссылкам из интернета.
 * В поле imgs должны быть ссылки http://сайт/picture2.png|http://сайт/picture1.png
 * Сохраняются в папку uploads/origin/additional, копируется в /uploads/shop/products/origin/additional
 * и заносятся в базу.
 * Если картинка с таким названием уже существует, шаг пропускается.
 * В строке могут быть как ссылки так и названия картинок, которые содержатся в папке.
 * http://сайт/picture2.png|picture1.png|picture3.png
 *
 * Добавлены новые ошибки Factor.php:
  const ErrorUrlAttribute = "Атрибут 'URL' не указан. Error: EIx011";
  const ErrorPriceAttribute = "Атрибут 'Цена' не указан. Error: EIx012";
  const ErrorNameVariantAttribute = "Атрибут 'Имя варианта' не указан. Error: EIx013";
  const ErrorNameAttribute = "Атрибут 'Имя товара' не указан. Error: EIx010";
 *
 * Файлы хранятся в /application/backups
 * Backup базы остался неизменным в /application/backups
 *
 * Сегментная выгрузка при первом запуске использует imports(). Так как
 * $_POST['offers'], $_POST['limit'], $_POST['countProd'] пусты она просто
 * пересчитывает количество позиций в файле и возвращает об этом информацию.
 * Все последующие этапы используют segmentImport();
 * Количество позиций в сегменте задается в файле importAdmin.js > importSegment() > limit
 */

use CMSFactory\assetManager;
use Currency\Currency;
use import_export\classes\BaseImport;
use import_export\classes\Factor;
use import_export\classes\ImportBootstrap as Imp;
use import_export\classes\Logger as LOG;
use MediaManager\Image;

/**
 * @property Lib_admin lib_admin
 */
class Import extends ShopAdminController
{

    /**
     * Folder backup
     * @var string
     * @access private
     */
    private $uploadDir = './application/backups/';

    /**
     * Default csv file name
     * @var string
     * @access private
     */
    private $csvFileName = 'product_csv_1.csv';

    /**
     * Information about the files
     * @var array
     * @access private
     */
    private $uplaodedFileInfo = [];

    private $fullPath = '/var/www/saas_data/mainsaas/';

    public function __construct() {
        parent::__construct();
        ShopController::checkVar();
        ShopAdminController::checkVarAdmin();
    }

    /**
     * Helper function for unloading segment from ajax
     * @param bool $bool If the function is used by the Imports(), is TRUE
     * @return array Status unloading
     * @access public
     */
    public function segmentImport($bool = false) {
        $result = Imp::create()->startProcess($this->input->post('offers'), $this->input->post('limit'), $this->input->post('countProd'), $this->input->post('EmptyFields'))->resultAsString();

        if (($this->input->post('offers') >= $this->input->post('countProd')) && $this->input->post('offers')) {
            $this->resizeAndUpdatePrice($this->input->post('withResize'), $this->input->post('withCurUpdate'), $result);
            $this->lib_admin->log(lang('Products was imported', 'import_export'));
            echo (json_encode($result));
        } elseif (!$bool) {

            $this->resizeAndUpdatePrice($this->input->post('withResize'), false, $result);
            return $result;
        } else {
            return $result;
        }
    }

    /**
     * Import products from CSV file, make backup db
     * @access public
     */
    public function imports() {
        chmod($this->uploadDir, 0777);
        if (count($_FILES)) {
            $this->saveCSVFile();
            chmod($this->uploadDir . $this->csvFileName, 0777);
            $path = $this->uploadDir . strtr($_FILES['userfile']['name'], [' ' => '_']);
            if (isset($path)) {
                $this->lib_admin->log(lang('Loaded import file', 'import_export') . '. File: ' . $_FILES['userfile']['name']);
                unlink($path);
            }
        }

        if ($this->input->post('attributes') && $this->input->post('csvfile')) {
            $importSettings = $this->cache->fetch('ImportExportCache');
            if (empty($importSettings) || $importSettings['withBackup'] != $this->input->post('withBackup')) {
                $this->cache->store('ImportExportCache', ['withBackup' => $this->input->post('withBackup')], '25920000');
            }
            Imp::create()->withBackup();
            $result = $this->segmentImport(TRUE);
            /* for ajax */
            if (!$this->input->post('offers') && $result['success']) {
                $result['propertiesSegmentImport']['countProductsInFile'] = $_SESSION['countProductsInFile'];
                $result['propertiesSegmentImport']['csvfile'] = trim($this->input->post('csvfile'));
                $result['propertiesSegmentImport']['delimiter'] = trim($this->input->post('delimiter'));
                $result['propertiesSegmentImport']['enclosure'] = trim($this->input->post('enclosure'));
                $result['propertiesSegmentImport']['encoding'] = trim($this->input->post('encoding'));
                $result['propertiesSegmentImport']['import_type'] = trim($this->input->post('import_type'));
                $result['propertiesSegmentImport']['language'] = trim($this->input->post('language'));
                $result['propertiesSegmentImport']['currency'] = trim($this->input->post('currency'));
                $result['propertiesSegmentImport']['withResize'] = trim($this->input->post('withResize'));
                $result['propertiesSegmentImport']['withCurUpdate'] = trim($this->input->post('withCurUpdate'));
                $result['propertiesSegmentImport']['EmptyFields'] = trim($this->input->post('EmptyFields'));
                unset($_SESSION['countProductsInFile']);
            }
            echo (json_encode($result));
        }
        $this->cache->delete_all();
    }

    /**
     * Make resize photo and update price
     * @param bool $resize
     * @param bool $curUpdate
     * @param array $result
     * @access private
     */
    private function resizeAndUpdatePrice($resize = false, $curUpdate = false, $result = null, $updateField = false) {
        if ($result) {
            if ($resize) {
                $result['content'] = explode('/', trim($result['content'][0]));
                Image::create()
                        ->resizeById($result['content'])
                        ->resizeByIdAdditional($result['content'], TRUE);
            }
        } else {
            LOG::create()->set(' resizeAndUpdatePrice $result is empty. import.php - IMPORT');
        }

        if ($curUpdate) {
            Currency::create()->checkPrices();
        }
    }

    /**
     * Downloads the file to the backup and starts conversion function convertXLStoCSV()
     * and configureImportProcess()
     * @access public
     */
    private function saveCSVFile() {
        $this->takeFileName();

        $this->load->library(
            'upload',
            [
             'overwrite'     => true,
             'upload_path'   => $this->uploadDir,
             'allowed_types' => '*',
            ]
        );

        $fileExt = pathinfo($_FILES['userfile']['name'], PATHINFO_EXTENSION);
        if (!in_array($fileExt, ['csv', 'xls', 'xlsx'])) {
            echo json_encode(['error' => lang('Wrong file type. Only csv|xls|xlsx')]);
            return;
        }

        if ($this->upload->do_upload('userfile')) {
            $data = $this->upload->data();

            if (($data['file_ext'] === '.xls') || ($data['file_ext'] === '.xlsx')) {
                $this->convertXLStoCSV($data['full_path']);
                unlink($this->uploadDir . $data['client_name']);
            } else {
                rename($this->uploadDir . str_replace(' ', '_', $data['client_name']), $this->uploadDir . $this->csvFileName);
            }

            $this->configureImportProcess();
        } else {
            echo json_encode(['error' => $this->upload->display_errors()]);
        }
    }

    /**
     * Generate file name
     * @access private
     */
    private function takeFileName() {
        $fileNumber = (in_array($this->input->post('csvfile'), [1, 2, 3])) ? (int) $this->input->post('csvfile') : 1;
        $this->csvFileName = "product_csv_$fileNumber.csv";
    }

    /**
     * Xls and xlsx convert to csv
     * @param string $excel_file Path to the file
     * @access private
     */
    private function convertXLStoCSV($excel_file = '') {
        $objReader = PHPExcel_IOFactory::createReaderForFile($excel_file);
        $objReader->setReadDataOnly(true);
        $objPHPExcel = $objReader->load($excel_file);
        $sheetData = $objPHPExcel->getActiveSheet()->toArray(null, true, true, true);

        foreach ($sheetData as $i) {
            foreach ($i as $j) {
                $toPrint .= '"' . str_replace('"', '""', $j) . '";';
            }
            $toPrint = rtrim($toPrint, ';') . PHP_EOL;
        }
        $filename = $this->csvFileName;
        fopen($this->uploadDir . $filename, 'w+');
        if (is_writable($this->uploadDir . $filename)) {
            if (!$handle = fopen($this->uploadDir . $filename, 'w+')) {
                echo json_encode(['error' => Factor::ErrorFolderPermission]);
                exit;
            }

            write_file($this->uploadDir . $filename, $toPrint);

            fclose($handle);
        } else {
            showMessage(lang("The file {$filename} is not writable", 'admin'));
        }
    }

    /**
     * Forms the attributes of the downloaded file
     * @param bool $vector Generates attributes for ajax
     * @access private
     */
    private function configureImportProcess($vector = true) {
        if (file_exists($this->uploadDir . $this->csvFileName)) {
            $file = fopen($this->uploadDir . $this->csvFileName, 'r');
            $row = array_diff(fgetcsv($file, 1000000, ';', '"'), [null]);
            fclose($file);
            $this->getFilesInfo();
            foreach ($this->uplaodedFileInfo as $file) {
                $uploadedFiles[str_replace('.', '', $file['name'])] = date('d.m.y H:i', $file['date']);
            }
            if ($vector && $this->input->is_ajax_request() && $_FILES) {
                echo json_encode(
                    [
                     'success'    => true,
                     'row'        => $row,
                     'attributes' => BaseImport::create()->attributes,
                     'filesInfo'  => $uploadedFiles,
                    ]
                );
            } else {
                $this->template->add_array(
                    [
                     'rows'       => $row,
                     'attributes' => BaseImport::create()->makeAttributesList()->possibleAttributes,
                     'filesInfo'  => $uploadedFiles,
                    ]
                );
            }
        }
    }

    /**
     * Information about the files
     * @param string $dir path to files
     * @access private
     */
    private function getFilesInfo($dir = null) {
        $dir = ($dir == null) ? $this->uploadDir : $dir;
        foreach (get_filenames($dir) as $file) {
            if (strpos($file, 'roduct_csv_')) {
                $this->uplaodedFileInfo[] = get_file_info($this->uploadDir . $file);
            }
        }
    }

    /**
     * Displays the attributes of a file when you select a cell
     * @access public
     */
    public function getAttributes() {
        $this->takeFileName();
        $this->configureImportProcess(false);
        assetManager::create()
                ->renderAdmin('import_attributes');
    }

    /**
     * Generates status bar imports from ajax to file
     * @access public
     */
    public function errorLog() {
        LOG::create()->set($this->input->post('error') . ' - IMPORT');
    }

}