application/modules/import_export/import.php
<?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');
}
}