1 <?php
2 3 4 5 6 7
8 class ProductCatalogImportTask extends ScheduledTask {
9
10 private static $baseFileURL = 'offers/';
11 private static $importFileName = 'catalog.xml';
12
13 protected $importStatus;
14 protected $importLog;
15 private static $max_version_count = 1;
16 private static $max_log_count = 30;
17
18
19 protected static $allow_parameters = true;
20
21
22 protected static $allow_filters = true;
23
24
25 protected $cachedCatalogParams = array();
26
27 protected $categoryCount = 0;
28 protected $productCount = 0;
29
30 protected static $possibleStartPages = array('StartCatalog', 'Catalog', 'DocPage');
31
32
33 protected $importSettings = false;
34
35 protected static $possibleSettings = array('ImportMode' => 'full', 'ClearProducts' => 0, 'ClearCategory'=>0, 'StartPage'=>'/', 'SubSite'=>0, 'OldItemAction'=>'delete');
36
37 protected static $catalog_version = 3;
38
39 static $catalog_saveable_fields = array('URLSegment', 'Description', 'Content', 'MenuTitle', 'MetaTitle', 'MetaKeywords', 'MetaDescription', 'GroupTitle', 'Sort');
40
41 static $product_saveable_fields_v2 = array('URLSegment', 'Description', 'Content', 'Vendor', 'MenuTitle', 'MetaTitle', 'MetaKeywords', 'MetaDescription', 'Available', 'AllowPurchase', 'Sort');
42
43 static $product_saveable_fields_v3 = array('URLSegment', 'Description', 'Content', 'Vendor', 'SKU', 'MenuTitle', 'MetaTitle', 'MetaKeywords', 'MetaDescription', 'Available', 'AllowPurchase','Quantity', 'Weight', 'GroupID', 'GroupTitle', 'Sort');
44
45 46 47 48 49 50
51 static function set_catalog_version($version) {
52 if ($version == 2 || $version == 3) {
53 self::$catalog_version = $version;
54 }
55
56 if ($version == 2) {
57 self::$allow_parameters = false;
58 self::$allow_filters = false;
59 }
60 }
61
62
63 protected static function get_product_saveable_fields() {
64 if (self::$catalog_version == 3) {
65 return self::$product_saveable_fields_v3;
66 }
67 return self::$product_saveable_fields_v2;
68 }
69
70 static function getImportFileName() {
71 return self::$importFileName;
72 }
73
74 static function set_base_url($url) {
75 self::$baseFileURL = $url;
76 }
77
78 static function get_base_url() {
79 return preg_replace('!/+$!','', self::$baseFileURL) . '/';
80 }
81
82 83 84 85 86 87 88
89 static function import_path($filename) {
90 return self::get_base_url() . preg_replace('!^/!', '', $filename);
91 }
92
93 94 95 96 97 98 99
100 static function absolute_import_path($filename) {
101 return BASE_PATH . '/' . self::import_path($filename);
102 }
103
104 105 106 107 108 109 110
111 static function module_path($filename) {
112 return BASE_PATH . '/catalog_import/' . $filename;
113 }
114
115 116 117 118 119
120 static function set_log_count($count) {
121 self::$max_log_count = $count;
122 }
123
124 125 126 127 128
129 static function set_version_count($count) {
130 self::$max_version_count = $count;
131 }
132
133 134 135 136 137
138 static function get_possible_start_pages() {
139 return self::$possibleStartPages;
140 }
141
142 143 144 145 146
147 static function set_possible_start_pages($pages) {
148 self::$possibleStartPages = $pages;
149 }
150
151 152 153 154 155
156 static function set_allow_parameters($val) {
157 self::$allow_parameters = $val;
158 }
159
160 161 162 163 164
165 static function set_allow_filters($val) {
166 self::$allow_filters = $val;
167 }
168
169 static function import_parse_bool_value($value) {
170 if (in_array($value, array('true', '1'))) {
171 return 1;
172 }
173 if (in_array($value, array('false', '0'))) {
174 return 0;
175 }
176 return null;
177 }
178
179 180 181 182 183 184
185 function fillImportSettings($settings, $srcName) {
186 if (!$this->checkImportSettings($settings)) {
187 $this->importLog->addLog(sprintf(_t('ProductCatalogImportTask.ErrorIn', 'Error In %s'), $srcName), 'warning');
188 return false;
189 }
190 if (!$this->importSettings)
191 $this->importSettings = new DataObject();
192 foreach (array_keys(self::$possibleSettings) as $set) {
193 if (!isset($this->importSettings->{$set}) && isset($settings->{$set})) {
194
195 $value = trim((string)$settings->{$set});
196 if ($value === 'true') $value = 1;
197 if ($value === 'false') $value = 0;
198 $this->importSettings->{$set} = $value;
199
200 if ($value && $set == 'StartPage') {
201 if ($value == '/') {
202 $this->importSettings->StartPage = 0;
203 } else {
204 $this->importSettings->StartPage = DataObject::get_one('SiteTree', "URLSegment = '{$value}'")->ID;
205 }
206 }
207
208 if ($value && $set == 'SubSite') {
209 $this->importSettings->SubSite = (int) $value;
210 }
211 }
212 }
213 return true;
214 }
215
216 217 218 219 220
221 function checkImportSettings($settings) {
222 if (!$settings) {
223 $this->importLog->addLog(_t('ProductCatalogImportTask.WrongSettings', 'Wrong Settings'), 'warning');
224 return false;
225 }
226 if ($settings->StartPage) {
227 if ($settings->StartPage != '/') {
228 $startPage = DataObject::get_one('SiteTree', "URLSegment = '{$settings->StartPage}'");
229 if (!$startPage) {
230 $this->importLog->addLog(_t('ProductCatalogImportTask.NoRoot', 'No Root'), 'warning');
231 return false;
232 }
233 }
234 }
235 if (class_exists('Subsite') && $settings->SubSite) {
236 $subsite = DataObject::get_by_id('Subsite', (int) $settings->SubSite);
237 if (!$subsite) {
238 $this->importLog->addLog(_t('ProductCatalogImportTask.NoSubsite', 'No Subsite'), 'warning');
239 return false;
240 }
241 }
242 if ($settings->ImportMode && $settings->ImportMode != 'full' && $settings->ImportMode != 'incremental' ) {
243 $this->importLog->addLog(
244 sprintf(_t('ProductCatalogImportTask.WrongImportParam', 'Wrong Import Param') . ' ImportMode: "%s"!', $settings->ImportMode),
245 'warning'
246 );
247 return false;
248 }
249 if ($settings->ClearCategory && !in_array($settings->ClearCategory, array('true', 'false'))) {
250 $this->importLog->addLog(
251 sprintf(_t('ProductCatalogImportTask.WrongImportParam', 'Wrong Import Param') . ' ClearCategory: "%s"!', $settings->ClearCategory),
252 'warning'
253 );
254 return false;
255 }
256 if ($settings->ClearProducts && !in_array($settings->ClearProducts, array('true', 'false'))) {
257 $this->importLog->addLog(
258 sprintf(_t('ProductCatalogImportTask.WrongImportParam', 'Wrong Import Param') . ' ClearProducts: "%s"!', $settings->ClearProducts),
259 'warning'
260 );
261 return false;
262 }
263 if ($settings->ClearCategory == 'true' && $settings->ClearProducts == 'false' ) {
264 $this->importLog->addLog(
265 _t('ProductCatalogImportTask.WrongImportParamClearProducts', 'Wrong Import Param ClearProducts'),
266 'warning'
267 );
268 return false;
269 }
270 if ($settings->ImportMode == 'incremental' && ($settings->ClearCategory == 'true' || $settings->ClearProducts == 'true')) {
271 $this->importLog->addLog(
272 _t('ProductCatalogImportTask.WrongImportParamImportMode', 'Wrong Import Param ImportMode'),
273 'warning'
274 );
275 return false;
276 }
277 if ($setting->OldItemAction && !in_array($settings->OldItemAction, array('hide', 'unpublish', 'delete', ''))) {
278 $this->importLog->addLog(
279 sprintf(_t('ProductCatalogImportTask.WrongOldItemActionParam', 'Wrong Old Item Action') . ' OldItemAction: "%s"!', $settings->OldItemAction),
280 'warning'
281 );
282 return false;
283 }
284 return true;
285 }
286
287 288 289 290 291 292 293
294 function xml2log($element) {
295 $result = '<' . $element->getName();
296 foreach ($element->attributes() as $name => $val) {
297 $result .= ' ' . $name . '="' . $val . '"';
298 }
299 $result .= '>';
300 return $result;
301 }
302
303 function TotalMessage() {
304 return sprintf('Импортировано категорий: %d, товаров: %d.', $this->categoryCount, $this->productCount);
305 }
306
307 308 309 310
311 function process() {
312 $pidFile = TEMP_FOLDER . '/import.pid';
313 if (is_file($pidFile)) {
314 $pid = (int) file_get_contents($pidFile);
315 if (posix_getsid($pid) !== false) {
316
317 return;
318 }
319 else {
320
321 $log = ProductImportLog::get_last_log();
322 if ($log && $log->Status == 'process') {
323 $log->EndTime = $log->LastEdited;
324 $log->addStatus('error', 'Скрипт импорта неожиданно прекратил работу');
325 }
326 unlink($pidFile);
327 }
328 }
329
330 if (!is_file(self::absolute_import_path('start_ok'))) return;
331
332 file_put_contents($pidFile, getmypid());
333
334
335 unlink(self::absolute_import_path('start_ok'));
336
337
338 $startTime = time();
339
340
341 Versioned::$versions_ttl = self::$max_version_count;
342
343
344 AssociatedFolderDecorator::$createFolders = false;
345
346
347 if (class_exists('LogItem')) {
348 LogItem::enable(false);
349 }
350
351
352 ProductImportLog::optimize_log_tables(self::$max_log_count);
353
354
355 $this->importLog = new ProductImportLog();
356 $this->importLog->write();
357 $this->importLog->addLog( _t('ProductCatalogImportTask.StartAt', 'Start At') . date("Y-m-d H:i:s", $startTime));
358 if (!is_file(self::absolute_import_path(self::$importFileName))) {
359 $this->importLog->addLog("Нет файла импорта товаров " . self::$importFileName, 'warning');
360 return false;
361 }
362
363 $validator = new XMLValidate(self::absolute_import_path(self::$importFileName), self::module_path('catalog3.xsd'));
364 $rs = $validator->validate();
365 if ($rs != 'validated') {
366 foreach ($rs as $error)
367 $this->importLog->addLog( _t('ProductCatalogImportTask.ValidationError', 'Validation Error') . ': '.$error, 'error');
368 $this->importLog->addStatus('error', _t('ProductCatalogImportTask.ValidationError', 'Validation Error'));
369 return false;
370 }
371
372
373 if (is_file(self::absolute_import_path('config.xml'))) {
374 $settings = simplexml_load_file(self::absolute_import_path('config.xml'));
375
376 unlink(self::absolute_import_path('config.xml'));
377 if (!$this->fillImportSettings($settings, 'config.xml'))
378 return false;
379 }
380 if ($this->importProductCatalog($startTime)) {
381 $this->importLog->EndTime = date('Y-m-d H:i:s');
382 $this->importLog->addStatus('ok', $this->TotalMessage());
383 $this->importLog->addLog(sprintf(_t('ProductCatalogImportTask.ImportDoneAt', 'Import done at %s'), date("Y-m-d H:i:s")));
384 } else {
385 $this->importLog->EndTime = date('Y-m-d H:i:s');
386 $this->importLog->addStatus('error', _t('ProductCatalogImportTask.ImportError', 'Import Error'));
387 }
388 unlink($pidFile);
389 }
390
391 function importProductCatalog($startTime) {
392
393 $this->categoryCount = 0;
394 $this->productCount = 0;
395
396 $importData = simplexml_load_file(self::absolute_import_path(self::$importFileName));
397 rename(self::absolute_import_path(self::$importFileName), self::absolute_import_path(self::$importFileName) . '.bak');
398
399
400 if ($importData->Config && !$this->fillImportSettings($importData->Config, self::$importFileName)) {
401 return false;
402 }
403
404
405 $sc = SiteConfig::current_site_config();
406 $this->fillImportSettings($sc, 'SiteConfig');
407 if (!$this->fillImportSettings(self::$possibleSettings, 'Defaults')) {
408 return false;
409 }
410
411 $this->importLog->addLog(sprintf(
412 _t('ProductCatalogImportTask.ImportSettings', 'Import Settings: ImportMode = %s; ClearCategory = %s; ClearProducts = %s; BaseFileURL = %s; StartPage = %s; SubsiteID = %s; OldItemAction = %s'), $this->importSettings->ImportMode, $this->importSettings->ClearCategory, $this->importSettings->ClearProducts, self::$baseFileURL, $this->importSettings->StartPage, (int)$this->importSettings->Subsite, $this->importSettings->OldItemAction
413 ));
414
415 $this->importLog->ProductCatalogDate = (string) $importData->attributes()->date;
416 $this->importLog->addStatus('process', _t('ProductCatalogImportTask.ImportInProgress', 'Import In Progress'));
417 $this->importLog->addLog(_t('ProductCatalogImportTask.CatalogImport', 'CatalogImport'));
418
419
420 if (self::$allow_parameters) {
421 $IDs = array();
422 if ($params = DataObject::get('ProductParam', "ParentCatalogID = 0")) {
423 $IDs = $params->map('ID', 'ID');
424 }
425 $this->importParams($importData, 0, $IDs);
426
427 $possibleFilterFields = Product::$possible_filter_fields;
428 if ($rootParams = DataObject::get('ProductParam', "ParentCatalogID = 0 AND ShowDefault = 1")) {
429 $possibleFilterFields = array_merge($possibleFilterFields, $rootParams->map('TechTitle', 'TechTitle'));
430 }
431 $this->cachedCatalogParams[0] = $possibleFilterFields;
432 }
433
434
435 if (self::$allow_filters) {
436 $IDs = array();
437 if ($filters = DataObject::get('CatalogFilter', "ParentCatalogID = 0")) {
438 $IDs = $filters->map('ID', 'ID');
439 }
440 if ($importData->xpath('CatalogFilters')) {
441 $this->importFilters($importData, 0, $IDs);
442 }
443 }
444
445 $categories = $importData->xpath('Category');
446
447
448 if ($this->importSettings->ClearCategory) {
449 singleton('Catalog')->importClearAll($this->importLog);
450 singleton('Product')->importClearAll($this->importLog);
451 }
452
453
454 if ($this->importSettings->ClearProducts) {
455 singleton('Product')->importClearAll($this->importLog);
456 }
457
458 $startPageID = $this->importSettings->StartPage;
459 $this->importCategories($categories, $startPageID);
460 if ($this->importSettings->ImportMode == 'full') {
461 $this->importLog->addLog(_t('ProductCatalogImportTask.ClearUnupdated', 'Clear Unupdated...'));
462 $this->unpublishNotUpdated($startTime);
463 }
464
465
466 $this->deleteEmptyDBFiles();
467
468 return true;
469 }
470
471 472 473 474 475 476
477 function importCategories($categories, $parentID) {
478 foreach($categories as $category) {
479 $newCategoryID = $this->importCategory($category, $parentID);
480 if (($childs = $category->xpath('Childs/Category')) && $newCategoryID) {
481 $this->importCategories($childs, $newCategoryID);
482 }
483 }
484 }
485
486 487 488 489 490
491 function importCategory($category, $parentID) {
492 if (!$category->attributes()->id)
493 return false;
494 $importID = (string)$category->attributes()->id;
495 $newCategory = DataObject::get_one('Catalog', "ImportID = '{$importID}'");
496 if (!$newCategory) {
497 $newCategory = new Catalog();
498 $newCategory->ImportID = $importID;
499 } else {
500 if (((string)$category->attributes()->unpublish == "true")) {
501 $newCategory->doUnpublish();
502 return false;
503 }
504 if (((string)$category->attributes()->delete == "true")) {
505 $newCategory->doUnpublish();
506 $newCategory->delete();
507 return false;
508 }
509 }
510 $newCategory->ParentID = $parentID;
511 $newCategory->Title = (string)$category->Title;
512
513
514 foreach(self::$catalog_saveable_fields as $field) {
515 if ($category->{$field}) {
516 $newCategory->{$field} = (string)$category->{$field};
517 }
518 }
519
520 if ($this->importSettings->SubSite) {
521 $newCategory->SubsiteID = $this->importSettings->SubSite;
522 }
523 $img = $this->importFile($newCategory->PhotoID, $category->Photo);
524 if ($img) {
525 $newCategory->PhotoID = $img->ID;
526 }
527
528 if (isset($category->ShowInMenus)) {
529 $newCategory->ShowInMenus = ($category->ShowInMenus == 'true') ? 1 : 0;
530 } else {
531 $newCategory->ShowInMenus = 1;
532 }
533 $newCategory->writeToStage('Stage');
534 $newCategory->publish('Stage', 'Live');
535
536
537
538
539 if (self::$allow_parameters) {
540 $IDs = array();
541 if ($newCategory->Params() && $newCategory->Params()->Count()) {
542 $IDs = $newCategory->Params()->getIdList();
543 }
544 $this->importParams($category, $newCategory->ID, $IDs);
545
546
547 $newCategory->OwnParams = 0;
548 if ($category->xpath('ShowParams')) {
549 $params = $category->xpath('ShowParams/ShowParam');
550
551 if ($newCategory->EnabledParams() && $newCategory->EnabledParams()->Count()) {
552 $newCategory->EnabledParams()->removeAll();
553 }
554
555 foreach($params as $paramID) {
556 $param = DataObject::get_one('ProductParam', "TechTitle = '". Convert::raw2sql((string)$paramID) . "'");
557 if ($param) {
558 $newCategory->EnabledParams()->add($param);
559 }
560 }
561 $newCategory->OwnParams = 1;
562 }
563 $newCategory->writeToStage('Stage');
564 $newCategory->publish('Stage', 'Live');
565 }
566
567 if (self::$allow_filters) {
568 $possibleFilterFields = Product::$possible_filter_fields;
569 if ($activeCatalogParams = $newCategory->catalogParams()) {
570 $possibleFilterFields = array_merge($possibleFilterFields, $activeCatalogParams->map('TechTitle', 'TechTitle'));
571 }
572 $this->cachedCatalogParams[$newCategory->ID] = $possibleFilterFields;
573
574
575 $IDs = array();
576 if ($newCategory->CatalogFilters() && $newCategory->CatalogFilters()->Count()) {
577 $IDs = $newCategory->CatalogFilters()->getIdList();
578 }
579
580 if ($category->xpath('CatalogFilters')) {
581 $this->importFilters($category, $newCategory->ID, $IDs);
582 }
583
584
585 $newCategory->OwnFilter = 0;
586 if ($category->xpath('ShowFilters')) {
587 $filters = $category->xpath('ShowFilters/ShowFilter');
588
589 if ($newCategory->EnabledCatalogFilters() && $newCategory->EnabledCatalogFilters()->Count()) {
590 $newCategory->EnabledCatalogFilters()->removeAll();
591 }
592
593 foreach($filters as $filterID) {
594 $filter = DataObject::get_one('CatalogFilter', "TechTitle = '". Convert::raw2sql((string)$filterID) . "'");
595 if ($filter) {
596 $newCategory->EnabledCatalogFilters()->add($filter);
597 }
598 }
599 $newCategory->OwnFilter = 1;
600 }
601 $newCategory->writeToStage('Stage');
602 $newCategory->publish('Stage', 'Live');
603 }
604
605 $this->categoryCount++;
606
607
608 if ($category->xpath('Products')) {
609 $products = $category->xpath('Products/Product');
610 foreach($products as $product) {
611 $this->importProduct($product, $newCategory->ID);
612 }
613 } else {
614
615 }
616 return $newCategory->ID;
617 }
618
619 function importParams($xml, $parentID, $oldIDs) {
620 if ($xml->xpath('ProductParams')) {
621 $params = $xml->xpath('ProductParams/ProductParam');
622 foreach($params as $param) {
623 if (!$param->attributes()->id) {
624 $this->importLog->addLog('У элемента ProductParam отсутвует обязательный атрибут id!', 'warning');
625 continue;
626 }
627 $paramData = DataObject::get_one('ProductParam', "ParentCatalogID = {$parentID} AND TechTitle = '". Convert::raw2sql($param->attributes()->id) . "'");
628 if (!$paramData) {
629 $paramData = new ProductParam();
630 $paramData->TechTitle = Convert::raw2sql($param->attributes()->id);
631 }
632 $paramData->ParentCatalogID = $parentID;
633 $paramData->Title = (string)$param->Title;
634 $paramData->Type = (string)$param->Type;
635 $paramData->ShowInList = ($param->ShowInList == 'true') ? 1 : 0;
636 $paramData->ShowInView = ($param->ShowInView == 'true') ? 1 : 0;
637 $paramData->ShowDefault = ($param->ShowDefault == 'true') ? 1 : 0;
638 $paramData->Sort = (int)$param->Sort;
639 $paramData->write();
640 unset($oldIDs[$paramData->ID]);
641 }
642
643
644 foreach($oldIDs as $ID) {
645 $param = DataObject::get_by_id('ProductParam', $ID);
646 if ($param) {
647 $param->delete();
648 }
649 }
650 }
651 }
652
653 function importFilters($xml, $parentID, $oldIDs) {
654
655 $filters = $xml->xpath('CatalogFilters/CatalogFilter');
656 foreach($filters as $filter) {
657 if (!$filter->attributes()->id) {
658 $this->importLog->addLog('У элемента CatalogFilter отсутвует обязательный атрибут id!', 'warning');
659 continue;
660 }
661 $filterField = trim((string)$filter->Field);
662 if (!isset($this->cachedCatalogParams[$parentID]) || (array_search($filterField, $this->cachedCatalogParams[$parentID]) === false)) {
663 $this->importLog->addLog("У каталога с ID {$parentID} отсутвствует параметр {$filterField} для фильтра!", 'warning');
664 continue;
665 }
666 $filterData = DataObject::get_one('CatalogFilter', "ParentCatalogID = {$parentID} AND TechTitle = '". Convert::raw2sql($filter->attributes()->id) . "'");
667 if (!$filterData) {
668 $filterData = new CatalogFilter();
669 $filterData->TechTitle = Convert::raw2sql($filter->attributes()->id);
670 }
671 $filterData->ParentCatalogID = $parentID;
672 $filterData->Title = (string)$filter->Title;
673 $filterData->Type = (string)$filter->Type;
674 $filterData->FilterField = $filterField;
675 $filterData->Important = ($filter->Important == 'true') ? 1 : 0;
676 $filterData->ShowDefault = ($filter->ShowDefault == 'true') ? 1 : 0;
677 $filterData->Sort = (int)$filter->Sort;
678 if ((string)$filter->GroupTitle && $filterData->Type == 'boolgroup') {
679 $filterData->GroupTitle = (string)$filter->GroupTitle;
680 }
681 if ((string)$filter->DefaultValue && $filterData->Type == 'list') {
682 $filterData->DefaultValue = (string)$filter->DefaultValue;
683 }
684 $filterData->write();
685 unset($oldIDs[$filterData->ID]);
686 }
687
688
689 foreach($oldIDs as $ID) {
690 $filter = DataObject::get_by_id('CatalogFilter', $ID);
691 if ($filter) {
692 $filter->delete();
693 }
694 }
695 }
696
697 function importProduct($product, $categoryID) {
698 if (!$product->attributes()->id)
699 return;
700 $importID = (string)$product->attributes()->id;
701 $productID = DB::query("SELECT ID FROM Product WHERE \"ImportID\" = '{$importID}' ORDER BY ID DESC")->value();
702 if ($productID) {
703
704 $newProduct = Versioned::get_one_by_stage("Product", "Stage", "\"Product\".\"ID\" = {$productID}");
705 if (!$newProduct) {
706 $newProduct = Versioned::get_one_by_stage("Product", "Live", "\"Product\".\"ID\" = {$productID}");
707 }
708 if (!$newProduct) {
709 return;
710 }
711
712 if (((string)$product->attributes()->unpublish == "true")) {
713 $newProduct->doUnpublish();
714 return false;
715 }
716 if (((string)$product->attributes()->delete == "true")) {
717 $newProduct->doUnpublish();
718 $newProduct->delete();
719 return false;
720 }
721 } else {
722 $newProduct = new Product();
723 $newProduct->ImportID = $importID;
724 }
725
726 $newProduct->ParentID = $categoryID;
727 $newProduct->Title = (string)$product->Title;
728
729
730 foreach(self::get_product_saveable_fields() as $field) {
731
732
733 if($field == 'Content' && $product->{$field}){
734 $content = "";
735 $splitted = explode("\n",(string)$product->{$field});
736 foreach ($splitted as $line) {
737 $content .= "<p>{$line}</p>";
738 }
739 $newProduct->Content = $content;
740 continue;
741 }
742 if ($product->{$field}) {
743 $newProduct->{$field} = (string)$product->{$field};
744 }
745 }
746
747 if ($product->Available) {
748 $value = self::import_parse_bool_value($product->Available);
749 if ($value !== null) {
750 $newProduct->Available = $value;
751 }
752 }
753
754 if ($product->AllowPurchase) {
755 $value = self::import_parse_bool_value($product->AllowPurchase);
756 if ($value !== null) {
757 $newProduct->AllowPurchase = $value;
758 }
759 }
760
761
762 if ($this->importSettings->SubSite) {
763 $newProduct->SubsiteID = $this->importSettings->SubSite;
764 }
765
766 767 768 769 770
771 if ($product->BasePrice) {
772 $newProduct->BasePrice = (float)$product->BasePrice;
773 }
774
775 if ($product->CostPrice) {
776 $newProduct->CostPrice = 0;
777 if ((float)$product->CostPrice) {
778 $newProduct->CostPrice = (float)$product->CostPrice;
779 }
780 }
781
782
783 $img = $this->importFile($newProduct->PhotoID, (string)$product->Photo);
784 if ($img) {
785 $newProduct->PhotoID = $img->ID;
786 }
787
788 $newProduct->writeToStage('Stage');
789 $newProduct->publish('Stage', 'Live');
790
791
792 if ($product->xpath('Images')) {
793 $oldPhotos = $newProduct->Photos();
794 $oldPhotosIDs = $oldPhotos->getIdList();
795 $newPhotos = $product->xpath('Images/Image');
796 foreach($newPhotos as $newPhoto) {
797 $importedImage = $this->importFile(0, $newPhoto->attributes()->path);
798 if ($importedImage) {
799 if ($mwPhoto = $oldPhotos->find('PhotoID', $importedImage->ID)) {
800 unset($oldPhotosIDs[$mwPhoto->ID]);
801 } else {
802 $mwPhoto = new MediawebPage_Photo();
803 $mwPhoto->PhotoID = $importedImage->ID;
804 }
805 $mwPhoto->MediawebPageID = $newProduct->ID;
806 $mwPhoto->Caption = (string)$newPhoto;
807 $mwPhoto->write();
808 }
809 }
810
811 if (count($oldPhotosIDs) > 0) {
812 foreach($oldPhotosIDs as $oldPhotosID) {
813 if ($oldPhoto = DataObject::get_by_id('MediawebPage_Photo', $oldPhotosID)) {
814 $oldPhoto->delete();
815 }
816 }
817 }
818 }
819
820
821 if ($product->xpath('Files')) {
822 $oldFiles = $newProduct->Files();
823 $oldFilesIDs = $oldFiles->getIdList();
824 $newFiles = $product->xpath('Files/File');
825 foreach($newFiles as $newFile) {
826 $importedFile = $this->importFile(0, $newFile->attributes()->path, 'File');
827 if ($importedFile) {
828 if ($mwFile = $oldFiles->find('FileID', $importedFile->ID)) {
829 unset($oldFilesIDs[$mwFile->ID]);
830 } else {
831 $mwFile = new MediawebPage_File();
832 $mwFile->AttachID = $importedFile->ID;
833 }
834 $mwFile->MediawebPageID = $newProduct->ID;
835 $mwFile->Caption = $importedFile->Title;
836 $mwFile->write();
837 }
838 }
839
840 if (count($oldFilesIDs) > 0) {
841 foreach($oldFilesIDs as $oldFilesID) {
842 if ($oldFile = DataObject::get_by_id('MediawebPage_File', $oldFilesID)) {
843 $oldFile->delete();
844 }
845 }
846 }
847 }
848
849
850 if (class_exists('SpecialCatalog') && $product->xpath('SpecialCatalogs')) {
851 $specCatalogs = $this->importListedSpecCatalogs($product->xpath('SpecialCatalogs/SpecialCatalog'));
852 $productSpecialCatalogs = array();
853
854 $allSpecialCatalogs = DataObject::get('SpecialCatalog');
855 if ($allSpecialCatalogs) {
856 foreach($allSpecialCatalogs as $specialCatalog) {
857 $specialCatalogProducts = $specialCatalog->LinkedProducts()->getIdList();
858 if (isset($specialCatalogProducts[$newProduct->ID])) {
859 $productSpecialCatalogs[$specialCatalog->ID] = $specialCatalog->ID;
860 }
861 }
862 }
863
864 foreach($specCatalogs as $specialCatalog) {
865 if (isset($productSpecialCatalogs[$specialCatalog->ID])) {
866 unset($productSpecialCatalogs[$specialCatalog->ID]);
867 } else {
868 $specialCatalog->LinkedProducts()->add($newProduct);
869 }
870 }
871
872 if (count($productSpecialCatalogs)) {
873 foreach($productSpecialCatalogs as $productSpecialCatalog) {
874 if ($oldSpecialCatalog = $allSpecialCatalogs->find('ID', $productSpecialCatalog)) {
875 $oldSpecialCatalog->LinkedProducts()->remove($newProduct);
876 }
877 }
878 }
879 }
880
881
882 if ($product->xpath('Params')) {
883
884 $oldParamValues = DataObject::get('ProductParamValue', "ProductID = {$newProduct->ID}");
885 if ($oldParamValues) {
886 foreach($oldParamValues as $oldParamValue)
887 $oldParamValue->delete();
888 }
889
890
891 $params = $product->xpath('Params/Param');
892 foreach($params as $param) {
893 if (!$param->attributes()->name) {
894 $this->importLog->addLog('У элемента param отсутвует обязательный атрибут name!', 'warning');
895 continue;
896 }
897
898 $paramData = DataObject::get_one('ProductParam', "TechTitle = '{$param->attributes()->name}'");
899 if ($paramData) {
900 $paramValueID = DB::query("SELECT \"ID\" FROM \"ProductParamValue\" WHERE \"ProductParamID\" = {$paramData->ID} AND ProductID = {$newProduct->ID}")->value();
901 if ($paramValueID) {
902 $paramValue = DataObject::get_by_id('ProductParamValue', $paramValueID);
903 } else {
904 $paramValue = new ProductParamValue();
905 $paramValue->ProductParamID = $paramData->ID;
906 $paramValue->ProductID = $newProduct->ID;
907 }
908
909 $value = (string)$param;
910
911 if ($paramData->Type == 'bool') {
912 if ($value == 'true') {
913 $value = 1;
914 }
915 if ($value == 'false') {
916 $value = 0;
917 }
918 }
919 $paramValue->Value = $value;
920 $paramValue->write();
921
922 } else {
923
924 $this->importLog->addLog(sprintf('У категории %s отсутствует параметр: %s', $newProduct->Parent()->Title, $param->attributes()->name), 'warning');
925 }
926 }
927 unset($params);
928 unset($oldParamValues);
929 }
930 $this->productCount++;
931 $this->importLog->addStatus('process', "Импортировано категорий: {$this->categoryCount}, товаров: {$this->productCount}");
932
933 unset($product);
934 unset($newProduct);
935 unset($oldPhotos);
936 unset($newPhotos);
937 }
938
939 function importListedSpecCatalogs($data) {
940
941 $catalogs = false;
942 if (isset($data)) {
943 $catalogs = new DataObjectSet();
944 foreach ($data as $catalog) {
945 $specialCatalog = false;
946 $id = (int)($catalog->attributes()->id);
947 $url = Convert::raw2sql($catalog->attributes()->url);
948 if ($id > 0)
949 $specialCatalog = DataObject::get_by_id('SpecialCatalog', $id);
950 if (!$specialCatalog && $url) {
951 $specialCatalog = DataObject::get_one('SpecialCatalog', "URLSegment = '$url'");
952 }
953 if ($specialCatalog) {
954 $catalogs->push($specialCatalog);
955 }
956 else {
957 $this->importLog->addLog(sprintf(_t('ProductCatalogImportTask.NoSpecialCatalog', 'No specila catalog %s'), self::xml2log($catalog)));
958 }
959 }
960 }
961 return $catalogs;
962 }
963
964 965 966 967 968 969 970
971 function unpublishNotUpdated($importStartTime) {
972 $this->importLog->addStatus('process', _t('ProductCatalogImportTask.HidingUnavailable', 'Hiding Unavailable categories and products'));
973
974 $needUnpublish = DataObject::get('Product', 'LastEdited < \'' . date("Y-m-d H:i:s", $importStartTime) . '\'');
975 if ($needUnpublish)
976 foreach ($needUnpublish as $product) {
977 switch ($this->importSettings->OldItemAction) {
978 case 'hide':
979 $product->AllowPurchase = 0;
980 $product->writeToStage('Stage');
981 if ($product->isPublished()) {
982 $product->publish('Stage', 'Live');
983 }
984 break;
985 case 'unpublish':
986 $product->doUnpublish();
987 break;
988 case 'delete':
989 $product->doUnpublish();
990 $product->delete();
991 break;
992 default:
993 $product->doUnpublish();
994 $product->delete();
995 break;
996 }
997 }
998
999 $needUnpublish = DataObject::get('Catalog', 'ClassName = \'Catalog\' AND LastEdited < \'' . date("Y-m-d H:i:s", $importStartTime) . '\'');
1000 if ($needUnpublish)
1001 foreach ($needUnpublish as $catalog) {
1002 switch ($this->importSettings->OldItemAction) {
1003 case 'hide':
1004 $catalog->ShowInMenus = 0;
1005 $catalog->writeToStage('Stage');
1006 if ($catalog->isPublished()) {
1007 $catalog->publish('Stage', 'Live');
1008 }
1009 break;
1010 case 'unpublish':
1011 $catalog->doUnpublish();
1012 break;
1013 case 'delete':
1014 $catalog->doUnpublish();
1015 $catalog->delete();
1016 break;
1017 default:
1018 $catalog->doUnpublish();
1019 $catalog->delete();
1020 break;
1021 }
1022 }
1023 $this->importLog->addStatus('process', _t('ProductCatalogImportTask.UnavailableIsHide', 'Unavailable categories and products hide'));
1024 return true;
1025 }
1026
1027 1028 1029 1030 1031
1032 function deleteEmptyDBFiles() {
1033 $importPath = self::get_base_url();
1034 $images = DataObject::get('Image', "`Filename` LIKE '{$importPath}%'");
1035 if ($images) {
1036 foreach($images as $image) {
1037 if (!file_exists($image->getFullPath())) {
1038 DB::Query('UPDATE Catalog SET PhotoID = 0 WHERE PhotoID = ' . $image->ID);
1039 DB::Query('UPDATE Catalog_Live SET PhotoID = 0 WHERE PhotoID = ' . $image->ID);
1040 DB::Query('UPDATE Product SET PhotoID = 0 WHERE PhotoID = ' . $image->ID);
1041 DB::Query('UPDATE Product_Live SET PhotoID = 0 WHERE PhotoID = ' . $image->ID);
1042 DB::Query('DELETE FROM MediawebPage_Photo WHERE PhotoID = ' . $image->ID);
1043 $image->deleteFormattedImages();
1044 $image->deleteDatabaseOnly();
1045 }
1046 }
1047 }
1048 return true;
1049 }
1050
1051 1052 1053 1054 1055 1056 1057 1058
1059 function importFile($fileID, $filePath, $type='Image') {
1060 $filePath = trim(str_replace("\\", "/", $filePath));
1061 $filePath = preg_replace('!^/!','', $filePath);
1062 if ($filePath) {
1063
1064 $fullFilePath = BASE_PATH . '/' . $filePath;
1065
1066 $file = false;
1067
1068 if ($fileID != 0) {
1069 $file = DataObject::get_by_id($type, $fileID);
1070 }
1071
1072
1073 if (!$file || $file->ID == 0) {
1074 $file = DataObject::get_one($type, "\"Filename\"='".Convert::raw2sql($filePath)."'", true, 'ID DESC');
1075 }
1076
1077
1078 if (is_file($fullFilePath)) {
1079
1080 if (!$file || $file->ID == 0) {
1081 $file = new $type();
1082 $file->setFilename($filePath);
1083 $file->Title = $file->Name;
1084 $file->write();
1085 }
1086
1087 if ($file->Filename != $filePath) {
1088
1089 $file = DataObject::get_one($type, "\"Filename\"='".Convert::raw2sql($filePath)."'", true, 'ID DESC');
1090
1091 if (!$file) {
1092 $file = new $type();
1093 $file->setFilename($filePath);
1094 $file->Title = $file->Name;
1095 $file->write();
1096 }
1097 }
1098
1099
1100 if (($fileID != $file->ID)) {
1101 if (!$file->Name) {
1102 $file->Name = basename($filePath);
1103 }
1104 if (($type == 'Image') && $file->Filename && $file->Filename != $filePath) {
1105 $file->deleteFormattedImages();
1106 }
1107 $file->setFilename($filePath);
1108 if ($type == 'Image') {
1109 $file->deleteFormattedImages();
1110 }
1111 if (!$file->Title)
1112 $file->Title = $file->Name;
1113 $file->write();
1114 }
1115 elseif ($fileID != 0) {
1116
1117 if (filemtime($fullFilePath) > strtotime($file->LastEdited)) {
1118 if ($type == 'Image') {
1119 $file->deleteFormattedImages();
1120 }
1121
1122 $file->forceChange();
1123 if (!$file->Title)
1124 $file->Title = $file->Name;
1125 $file->write();
1126 }
1127 }
1128 if (!$file->Hidden) {
1129 $file->Hidden = 1;
1130 $file->write();
1131 }
1132 return $file;
1133 } else {
1134
1135 if ($file) {
1136 if ($type == 'Image') {
1137 $file->deleteFormattedImages();
1138 }
1139 $file->deleteDatabaseOnly();
1140 }
1141 $this->importLog->addLog(sprintf('Отсутствует изображение: %s', $filePath), 'warning');
1142 }
1143 }
1144 return false;
1145 }
1146 }
[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the
Silverstripe Forums to ask development related questions.
-