Webylon 3.1 API Docs
  • Package
  • Class
  • Tree
  • Deprecated
  • Download
Version: current
  • 3.2
  • 3.1

Packages

  • auth
  • Booking
  • cart
    • shipping
    • steppedcheckout
  • Catalog
  • cms
    • assets
    • batchaction
    • batchactions
    • bulkloading
    • comments
    • content
    • core
    • export
    • newsletter
    • publishers
    • reports
    • security
    • tasks
  • Dashboard
  • DataObjectManager
  • event
  • faq
  • forms
    • actions
    • core
    • fields-basic
    • fields-dataless
    • fields-datetime
    • fields-files
    • fields-formatted
    • fields-formattedinput
    • fields-relational
    • fields-structural
    • transformations
    • validators
  • googlesitemaps
  • guestbook
  • installer
  • newsletter
  • None
  • photo
    • gallery
  • PHP
  • polls
  • recaptcha
  • sapphire
    • api
    • bulkloading
    • control
    • core
    • cron
    • dev
    • email
    • fields-formattedinput
    • filesystem
    • formatters
    • forms
    • i18n
    • integration
    • misc
    • model
    • parsers
    • search
    • security
    • tasks
    • testing
    • tools
    • validation
    • view
    • widgets
  • seo
    • open
      • graph
  • sfDateTimePlugin
  • spamprotection
  • stealth
    • captha
  • subsites
  • userform
    • pagetypes
  • userforms
  • webylon
  • widgets

Classes

  • Address
  • AddSubsiteTask
  • Announcement
  • AnnouncementDecorator
  • AnnouncementHolder
  • AssociatedFolderDecorator
  • AttachedFiles
  • AudioPhpCaptcha
  • AutoCompleteField
  • BaseMediawebImportParser
  • BookingOrder_StatusLog
  • CalendarUtil
  • CalendarWidgetExtention
  • CatalogCatalogLinkedDecorator
  • CatalogImportTask
  • CatalogOldFieldsDecorator
  • CatalogProductLinkedDecorator
  • CheckoutStep_ShippingMethod
  • ChequePayment
  • ClearHistoryTask
  • ClientEmailOrderNotification_SiteConfig
  • ClientVKOrderNotification_SiteConfig
  • CommentsSiteConfig
  • ContactsPage
  • CookieExtention
  • CostTableShippingRate
  • CustomMenu
  • CustomMenuAdmin
  • CustomMenuDecorator
  • CustomMenuHolder
  • CustomMenuItem
  • CustomPayment
  • DataObjectLoggerExtension
  • DataObjectSubsites
  • DefaultCMSActionDecorator
  • DeleteOldAssociatedFoldersTask
  • DocPage
  • DocumentDirection
  • DocumentHaving
  • DocumentSearchForm
  • DocumentSiteConfig
  • DocumentsPage
  • DocumentType
  • DOMUtil
  • ExtendPageAnnouncement
  • ExtendPageNews
  • ExtendPagePoll
  • FileOversizeDecorator
  • FindCyrillicSiteConfig
  • FolderRenameTask
  • GDMergeAlpha
  • HomePage
  • ImageAutoResize
  • ImageResizeTask
  • ImportAdmin
  • ImportAutoStartTask
  • ImportCatalogMessage
  • ImportConfig
  • ImportLog
  • ImportSiteConfig
  • ImportTask
  • LoggerAdmin
  • LogItem
  • ManagerEmailOrderNotification_SiteConfig
  • MapObject
  • MapObjectGroup
  • MapPage
  • MediawebForm
  • MediawebImportParser
  • MemberLoggerExtension
  • MultiUploadConfig
  • NearestEventExtention
  • NewDocumentsAdmin
  • News_Controller
  • NewsEntry
  • NewsEntry_Controller
  • NewsHolder
  • NewsHolder_Controller
  • NewsLiveCalendarWidget
  • NewsSiteConfig
  • Order_StatusLog
  • Orders1CExchange
  • PageHideFieldsExtension
  • PageInformerRates
  • PageInformerWeather
  • Payment_Failure
  • Payment_Processing
  • Payment_Result
  • Payment_Success
  • PaymentMethod
  • PaymentSiteConfig
  • PayPalPayment
  • PayPalPayment_Handler
  • PhoneField
  • PhotoAlbumExtentions
  • PhpCaptcha
  • PhpCaptchaColour
  • PhpCaptchaField
  • phpMorphy
  • phpMorphy_AncodesResolver_AsIs
  • phpMorphy_AncodesResolver_Proxy
  • phpMorphy_AncodesResolver_ToDialingAncodes
  • phpMorphy_AncodesResolver_ToText
  • phpMorphy_AnnotDecoder_Base
  • phpMorphy_AnnotDecoder_Common
  • phpMorphy_AnnotDecoder_Factory
  • phpMorphy_AnnotDecoder_Predict
  • phpMorphy_FilesBundle
  • phpMorphy_Fsa
  • phpMorphy_Fsa_Decorator
  • phpMorphy_Fsa_Proxy
  • phpMorphy_Fsa_Sparse_File
  • phpMorphy_Fsa_Sparse_Mem
  • phpMorphy_Fsa_Sparse_Shm
  • phpMorphy_Fsa_Tree_File
  • phpMorphy_Fsa_Tree_Mem
  • phpMorphy_Fsa_Tree_Shm
  • phpMorphy_Fsa_WordsCollector
  • phpMorphy_GramInfo
  • phpMorphy_GramInfo_AncodeCache
  • phpMorphy_GramInfo_Decorator
  • phpMorphy_Graminfo_File
  • phpMorphy_Graminfo_Mem
  • phpMorphy_GramInfo_Proxy
  • phpMorphy_GramInfo_Proxy_WithHeader
  • phpMorphy_GramInfo_RuntimeCaching
  • phpMorphy_Graminfo_Shm
  • phpMorphy_GrammemsProvider_Base
  • phpMorphy_GrammemsProvider_Decorator
  • phpMorphy_GrammemsProvider_Empty
  • phpMorphy_GrammemsProvider_Factory
  • phpMorphy_GrammemsProvider_ForFactory
  • phpMorphy_GrammemsProvider_ru_RU
  • phpMorphy_GramTab
  • phpMorphy_GramTab_Empty
  • phpMorphy_GramTab_Proxy
  • phpMorphy_Link
  • phpMorphy_Link_Annot
  • phpMorphy_Link_Base
  • phpMorphy_Morphier_Base
  • phpMorphy_Morphier_Bulk
  • phpMorphy_Morphier_Common
  • phpMorphy_Morphier_Empty
  • phpMorphy_Morphier_Finder_Base
  • phpMorphy_Morphier_Finder_Common
  • phpMorphy_Morphier_Finder_Predict_Databse
  • phpMorphy_Morphier_Finder_Predict_Suffix
  • phpMorphy_Morphier_Helper
  • phpMorphy_Morphier_Predict_Database
  • phpMorphy_Morphier_Predict_Suffix
  • phpMorphy_Morphier_PredictCollector
  • phpMorphy_Semaphore
  • phpMorphy_Semaphore_Empty
  • phpMorphy_Semaphore_Nix
  • phpMorphy_Semaphore_Win
  • phpMorphy_Shm_Cache
  • phpMorphy_Shm_Cache_FileDescriptor
  • phpMorphy_Shm_Header
  • phpMorphy_Source_Dba
  • phpMorphy_Source_Fsa
  • phpMorphy_State
  • phpMorphy_Storage
  • phpMorphy_Storage_Factory
  • phpMorphy_Storage_File
  • phpMorphy_Storage_Mem
  • phpMorphy_Storage_Proxy
  • phpMorphy_Storage_Shm
  • phpMorphy_UnicodeHelper
  • phpMorphy_UnicodeHelper_Base
  • phpMorphy_UnicodeHelper_MultiByteFixed
  • phpMorphy_UnicodeHelper_singlebyte
  • phpMorphy_UnicodeHelper_ucs_2be
  • phpMorphy_UnicodeHelper_ucs_2le
  • phpMorphy_UnicodeHelper_ucs_4be
  • phpMorphy_UnicodeHelper_ucs_4le
  • phpMorphy_UnicodeHelper_utf_16_Base
  • phpMorphy_UnicodeHelper_utf_16be
  • phpMorphy_UnicodeHelper_utf_16le
  • phpMorphy_UnicodeHelper_utf_32_Base
  • phpMorphy_UnicodeHelper_utf_32be
  • phpMorphy_UnicodeHelper_utf_32le
  • phpMorphy_UnicodeHelper_utf_8
  • phpMorphy_WordDescriptor
  • phpMorphy_WordDescriptor_Collection
  • phpMorphy_WordDescriptor_Collection_Serializer
  • phpMorphy_WordForm
  • ProductImportV1Decorator
  • ProductOldFieldsDecorator
  • ProductProductLinkedDecorator
  • PublHolder
  • Publication
  • PublicationSiteTree
  • RatingDataObject
  • RatingExtension
  • RealtySiteConfigDecorator
  • RecentComments
  • RecentFiles
  • RecentPages
  • RelatedPageLink
  • RepairImportTask
  • RoomRate_PriceField
  • RussianUpLower
  • SberbankPayment
  • SberbankPayment_Handler
  • SetMainSiteHomePageTypeTask
  • ShippingEstimator
  • ShippingPackage
  • ShowUserFromExtension
  • SiteConfigDecorator
  • SiteConfigSubsites
  • SiteTreeSubsites
  • SMSCOrderNotification_SiteConfig
  • SMSOrderNotification_SiteConfig
  • SortCMSActionDecorator
  • SS_Report_FakeQuery
  • SSMorphy
  • SSNController
  • SteppedCheckout_PageMessages
  • SubpageListField
  • SubscribeFormAllPagesExtension
  • SubsiteDropdownField
  • SubsiteReportWrapper
  • TableShippingRate
  • UnitellerPayment
  • UnitellerPayment_Handler
  • UnmoderatedComments
  • VideoManager
  • VideoSiteConfig
  • WatermarkImage
  • WatermarkSiteConfig
  • WebylonImportAdmin
  • WebylonImportCatalog
  • WeightTableShippingRate
  • XMLValidate
  • YaMoneyPayment
  • YaMoneyPayment_Handler
  • YMLExporter
  • YMLSiteConfig

Interfaces

  • ImportInterface
  • PaymentObjectInterface
  • phpMorphy_AncodesResolver_Interface
  • phpMorphy_AnnotDecoder_Interface
  • phpMorphy_Fsa_Interface
  • phpMorphy_GramInfo_Interace
  • phpMorphy_GrammemsProvider_Interface
  • phpMorphy_GramTab_Interface
  • phpMorphy_Morphier_Finder_Interface
  • phpMorphy_Morphier_Interface
  • phpMorphy_Shm_Cache_Interface
  • phpMorphy_Source_Interface

Exceptions

  • phpMorphy_Exception

Functions

  • column_sort
  • column_sort_callback_basic
  • encodeFileForEmail
  • encodeMultipart
  • getMimeType
  • htmlEmail
  • loadMimeTypes
  • phpmorphy_overload_mb_funcs
  • plaintextEmail
  • processHeaders
  • QuotedPrintable_encode
  • supressOutput
  • validateError
  • validEmailAddr
  • wrapImagesInline
  • wrapImagesInline_rewriter
  1 <?php
  2 /**
  3  * Процесс импортирования нашего каталога
  4  * sake /CatalogImportTask
  5  *
  6  * @author menedem, dvp
  7  */
  8 class CatalogImportTask extends ScheduledTask {
  9 
 10     protected $importCategories; //для хранения импортируемых категорий
 11     protected $importProducts;//для хранения импортируемых продуктов
 12 
 13     private static $base_import_url = 'offers';
 14     private static $import_filename = 'import.xml';
 15 
 16     private static $max_version_count = 1;
 17     private static $max_log_count = 30;
 18 
 19 
 20     protected static $possibleSettings = array('ImportMode','ClearProducts','ClearCategory','StartPage','BaseFileURL','OldItemAction');
 21 
 22     protected static $possibleStartPages = array('StartCatalog', 'Catalog', 'DocPage');
 23     
 24     protected static $option_xml_validate_file_path = false; // свой файл валидации xml (.xsd) - используется при добавлении полей импорта (через Product::addPossibleFields)
 25 
 26     protected $importLog;
 27 
 28     protected $importSettings = false;
 29 
 30     protected $categoryMap = array();
 31     protected $newCategories = array();
 32 
 33     //Добавлено
 34     protected $importedCategoriesCount = 0;
 35     protected $importedProductsCount = 0;
 36 
 37     //Обновлено
 38     protected $updatedCategoriesCount = 0;
 39     protected $updatedProductsCount = 0;
 40 
 41     protected $deletedCategoriesCount = 0;
 42     protected $deletedProductsCount = 0;
 43 
 44     // !!! unpublish
 45     protected $unpublishCategoriesCount = 0;
 46     protected $unpublishProductsCount = 0;
 47     
 48     /**
 49      * Установка нестандартного каталога для импорта
 50      * 
 51      * @param string $url - путь к базовому каталогу импорта относительно корня сайта
 52      */
 53     static function set_base_url($url) {
 54         self::$base_import_url = trim($url, '/');
 55     }
 56 
 57     static function get_base_url() {
 58         return self::$base_import_url . '/';
 59     }
 60 
 61     /**
 62      * Установка нестандартного имени XML файла с данными импорта
 63      * 
 64      * @param string $val новое имя файла
 65      */
 66     static function set_import_filename($val) {
 67         self::$import_filename = basename($val);
 68     }
 69 
 70     static function import_filename() {
 71         return self::$import_filename;
 72     }
 73 
 74     static function import_filepath() {
 75         return self::absolute_import_path(self::$import_filename);
 76     }
 77     
 78     /**
 79      * Установка количества хранимых логов импорта
 80      *
 81      * @param int $count     
 82      */
 83     static function set_log_count($count) {
 84         self::$max_log_count = $count;
 85     }
 86     
 87     /**
 88      * Установка количества хранимых версий страниц (чтоб не плодить версии при импорте)
 89      *
 90      * @param int $count     
 91      */
 92     static function set_version_count($count) {
 93         self::$max_version_count = $count;
 94     }
 95     
 96     /**
 97      * Установка пути к своему файл валидации XML
 98      *
 99      * @param int $count     
100      */
101     static function set_xml_validate_file_path($path) {
102         self::$option_xml_validate_file_path = $path;
103     }
104 
105     /**
106      * Полный путь к папке импорта (может быть указано имя файла в этой папке)
107      *
108      * @param string $filename
109      *
110      * @return string
111      */
112     static function absolute_import_path($filename) {
113         return BASE_PATH . '/' . self::get_base_url() . $filename;
114     }
115 
116     /**
117      * Получение допустимых типов корневых страниц для импорта
118      *
119      * @return Array
120      */
121     static function get_possible_start_pages() {
122         return self::$possibleStartPages;
123     }
124 
125     /**
126      * Полный путь к корню модуля (может быть указано имя файла в этой папке) - например для получения catalog2.xsd
127      *
128      * @param string $filename
129      *
130      * @return string
131      */
132     static function module_path($filename) {
133         return BASE_PATH . '/catalog_import/' . $filename;
134     }
135     
136     /**
137      * Полный путь к файлу валидации xml (по умолчанию catalog2.xsd)          
138      *
139      * @return string
140      */
141     static function xml_validate_file_path() {
142         if (self::$option_xml_validate_file_path) {
143             return self::$option_xml_validate_file_path;
144         }
145         return self::module_path('catalog2.xsd');
146     }
147 
148     /**
149      * Преобразование XML-строки в текстовую для записи в лог
150      *
151      * @param SimpleXml $element
152      *
153      * @return string
154      */
155     static function xml2log($element) {
156         $result = '<' . $element->getName();
157         foreach ($element->attributes() as $name => $val) {
158             $result .= ' ' . $name . '="' . $val . '"';
159         }
160         $result .= '>';
161         return $result;
162     }
163 
164     /**
165      * Преобразует xml объект в массив
166      *
167      * @param SimpleXMLElement $xml
168      *
169      * @return array - представление xml в виде массива
170      */
171     static function xml2array($xml) {
172         $attr = (array) $xml->attributes();
173         $data = (array) $xml;
174         unset($data['@attributes']);
175         return array_merge($attr['@attributes'], $data);
176     }
177 
178     /**
179      * Путь к файлу относительно корня сайта
180      * 
181      * @param string $path часть пути файла
182      * 
183      * @return string
184      */
185     function getFilePath($path) {
186         if ($path[0] == '/') $path = substr($path, 1);
187         
188         // если он задан префикс
189         if ($this->importSettings->BaseFileURL) {
190             return $this->importSettings->BaseFileURL . '/' . $path;
191         }
192 
193         // только имя - добавим базовый каталог импорта
194         // !!!! как быть с ситуацией offfers/filename ????
195         if (strpos($path, '/') === false) {
196             return self::get_base_url() . $path;
197         }
198         
199         return $path;
200     }
201 
202     /** !!!!!!!!!!!!
203      * Не используется (возможно пока)
204      *
205      * @param string $message
206      *     
207      */
208     function fatalError($message) {
209         $this->importLog->addLog($message);
210         $this->importLog->setStatusType('error', $this->TotalMessage());
211 
212         $pidFile = TEMP_FOLDER . '/import.pid';
213 
214         if (is_file($pidFile)) {
215             unlink($pidFile);
216         }
217         //trigger_error($message, E_USER_ERROR);
218         print "Ошибка импорта: {$message}\n";
219         exit;
220     }
221 
222     /**
223      * Сообщение о результатх импорта
224      *
225      * @return string
226      */
227     function TotalMessage() {
228         return sprintf(
229             _t('CatalogImportTask.TotalMessage', 'Added %d categories, %d products; Updated %d categories, %d products; Deleted %d categories, %d products.'), 
230             $this->importedCategoriesCount, $this->importedProductsCount,
231             $this->updatedCategoriesCount,$this->updatedProductsCount,
232             $this->deletedCategoriesCount,$this->deletedProductsCount
233         );
234     }
235 
236     /**
237      * Заполняем настройки импорта
238      *
239      * @param SimpleXMLElement $settings
240      *     
241      */
242     function fillImportSettings($settings, $srcName) {
243         if (!$this->checkImportSettings($settings)) {
244             $this->importLog->addLog(sprintf(_t('CatalogImportTask.ErrorIn', 'Error In %s'), $srcName), 'warning');
245             return false;
246         }   
247         if (!$this->importSettings)
248             $this->importSettings = new DataObject();
249 
250         foreach (self::$possibleSettings as $set) {
251             if (!isset($this->importSettings->{$set}) && isset($settings->{$set})) {
252                 // !!! для правильной интерпретации строк true и false в xml
253                 $value = trim((string)$settings->{$set});
254                 if ($value === 'true') $value = 1;
255                 if ($value === 'false') $value = 0;
256                 $this->importSettings->{$set} = $value;
257             }
258         }       
259         if (isset($this->importSettings->StartPage))
260             $this->importSettings->StartPage = (int) $this->importSettings->StartPage;
261         
262         if (isset($this->importSettings->BaseFileURL))
263             $this->importSettings->BaseFileURL = trim($this->importSettings->BaseFileURL, '/');
264         return true;
265     }
266 
267     /**
268      * Проверяем настройки импорта
269      *
270      * @return boolean
271      */
272     function checkImportSettings($settings) {       
273         if (!$settings) {
274             $this->importLog->addLog(_t('CatalogImportTask.WrongSettings', 'Wrong Settings'), 'warning');
275             return false;
276         }
277         if ($settings->StartPage) {
278             $startPage = DataObject::get_by_id('SiteTree', (int) $settings->StartPage);
279             if (!$startPage) {
280                 $this->importLog->addLog(_t('CatalogImportTask.NoRoot', 'No Root'), 'warning');
281                 return false;
282             }
283         }
284         if ($settings->ImportMode != 'full' && $settings->ImportMode != 'incremental' ) {
285             $this->importLog->addLog(
286                 sprintf(_t('CatalogImportTask.WrongImportParam', 'Wrong Import Param') . '  ImportMode: "%s"!', $settings->ImportMode),
287                 'warning'
288             );
289             return false;
290         }       
291         if ($settings->ClearCategory && !in_array($settings->ClearCategory, array('true', 'false'))) {
292             $this->importLog->addLog(
293                 sprintf(_t('CatalogImportTask.WrongImportParam', 'Wrong Import Param') . ' ClearCategory: "%s"!', $settings->ClearCategory),
294                 'warning'
295             );
296             return false;
297         }       
298         if ($settings->ClearProducts && !in_array($settings->ClearProducts, array('true', 'false'))) {
299             $this->importLog->addLog(
300                 sprintf(_t('CatalogImportTask.WrongImportParam', 'Wrong Import Param') . ' ClearProducts: "%s"!', $settings->ClearProducts),
301                 'warning'
302             );
303             return false;
304         }
305         if ($settings->ClearCategory == 'true' && $settings->ClearProducts == 'false' ) {
306             $this->importLog->addLog(
307                 _t('CatalogImportTask.WrongImportParamClearProducts', 'Wrong Import Param ClearProducts'),
308                 'warning'
309             );
310             return false;
311         }
312         if ($settings->ImportMode == 'incremental' && ($settings->ClearCategory == 'true' || $settings->ClearProducts == 'true')) {
313             $this->importLog->addLog(
314                 _t('CatalogImportTask.WrongImportParamImportMode', 'Wrong Import Param ImportMode'),
315                 'warning'
316             );
317             return false;
318         }
319         if ($settings->OldItemAction && !in_array($settings->OldItemAction, array('hide', 'unpublish', 'delete'))) {
320             $this->importLog->addLog(
321                 sprintf(_t('CatalogImportTask.WrongOldItemActionParam', 'Wrong Old Item Action') . ' OldItemAction: "%s"!', $settings->OldItemAction),
322                 'warning'
323             );
324             return false;
325         }
326         return true;
327     }
328 
329     /**
330      * Возвращает Catalog ID по ImportID
331      * 
332      * @param int $importID 
333      * 
334      * @return int - ID каталога из БД
335      */
336     function findCatalogID($importID) {
337         return (isset($this->categoryMap[$importID])) ? $this->categoryMap[$importID] : false;
338     }
339     
340     /**
341      * Процесс импорта
342      * 
343      */
344     function process() {                
345         $pidFile = TEMP_FOLDER . '/import.pid';
346         
347         // проверка на работающий импорт
348         if (is_file($pidFile)) {
349             $pid = (int) file_get_contents($pidFile);
350             if (posix_getsid($pid) !== false) {
351                 // импорт уже работает
352                 return;
353             }
354             else {
355                 // предыдущий импорт сдох?
356                 $log = ImportLog::get_last_log();
357                 if ($log && $log->Status == 'process') {
358                     $log->EndTime = $log->LastEdited;
359                     $log->setStatusType('error', _t('CatalogImportTask.ScriptUnexpectStop', 'Script Unexpect Stop'));
360                 }
361                 unlink($pidFile);
362             }
363         }
364 
365         // есть ли флаг запуска
366         if (!is_file(self::absolute_import_path('start_ok'))) return;
367         
368         //Можно работать
369         unlink(self::absolute_import_path('start_ok'));
370         file_put_contents($pidFile, getmypid());
371         $startTime = time();
372         
373         // отключаем протоколирование
374         if (class_exists('LogItem')) {
375             LogItem::enable(false);
376         }
377         
378         // установить кол-во версий для товаров
379         Versioned::$versions_ttl = self::$max_version_count;
380         
381         // отключаем автосоздание папок каталогов и товаров -  в импорте мы не копируем туда прикрепленный фото и файлы
382         AssociatedFolderDecorator::$createFolders = false;
383         
384         // почистим старые ImportLog      
385         ImportLog::optimize_log_tables(self::$max_log_count);
386                 
387         //Запускаем импорт       
388         $this->importLog = new ImportLog();
389         $this->importLog->write();
390         $this->importLog->addLog( _t('CatalogImportTask.StartAt', 'Start At') .  date("Y-m-d H:i:s", $startTime));
391 
392         if (!is_file(self::import_filepath())) {
393             $this->importLog->setStatusType('error', _t('CatalogImportTask.NoImportFile', 'NoImportFile') . self::import_filename());
394             return false;
395         }
396 
397         $validator = new XMLValidate(self::import_filepath(), self::xml_validate_file_path());
398         $rs = $validator->validate();
399         if ($rs != 'validated') {
400             foreach ($rs as $error)
401                 $this->importLog->addLog( _t('CatalogImportTask.ValidationError', 'Validation Error') . ': '.$error, 'error');
402             $this->importLog->setStatusType('error', _t('CatalogImportTask.ValidationError', 'Validation Error'));
403             return false;
404         }
405         
406         // config.xml генерируем из админки - максимальный приоритет
407         if (is_file(self::absolute_import_path('config.xml'))) {
408             $settings = simplexml_load_file(self::absolute_import_path('config.xml'));
409             //удаляем файл настроек, сгенереный из админки, чтобы при след.импорте не воспользоваться случайно            
410             unlink(self::absolute_import_path('config.xml')); 
411             if (!$this->fillImportSettings($settings, 'config.xml'))
412                 return false;
413         }
414 
415         if ($this->importCatalog($startTime)) {
416             $this->importLog->EndTime = date('Y-m-d H:i:s');
417             $this->importLog->setStatusType('ok', $this->TotalMessage());
418         }
419         else {
420             $this->importLog->EndTime = date('Y-m-d H:i:s');
421             $this->importLog->setStatusType('error', _t('CatalogImportTask.ImportError', 'Import Error'));
422         }
423         unlink($pidFile);
424     }
425 
426     /**
427      * Импорт каталога
428      *
429      * @return boolean - флаг успешного завершения
430      */
431     function importCatalog($startTime) {
432         $productCount = 0;
433         
434         //Настройки из файла импорта
435         $importData = simplexml_load_file(self::import_filepath());
436         rename(self::import_filepath(), self::import_filepath() . '.bak');
437 
438         // настройки из самого xml - вторые по приоритету
439         if ($importData->config && !$this->fillImportSettings($importData->config, self::import_filename()))
440             return false;
441 
442         //Настройки из SiteConfig - самые последние
443         $sc = SiteConfig::current_site_config();
444         if (!$this->fillImportSettings($sc, 'SiteConfig'))
445             return false;
446 
447         $this->importLog->addLog(sprintf(
448             _t('CatalogImportTask.ImportSettings', 'Import Settings: ImportMode = %s; ClearCategory = %s; ClearProducts = %s; BaseFileURL = %s; StartPage = %s; OldItemAction = %s'), 
449             $this->importSettings->ImportMode, $this->importSettings->ClearCategory, $this->importSettings->ClearProducts, $this->importSettings->BaseFileURL, $this->importSettings->StartPage, $this->importSettings->OldItemAction
450         ));
451         
452         $this->importLog->CatalogDate = (string) $importData->attributes()->date;
453 
454         $this->importCategories = $importData->xpath('categories/category');
455         $this->importLog->addLog(_t('CatalogImportTask.CategoriesInImport', 'Categories In Import') . count($this->importCategories));
456 
457         $this->importProducts = $importData->xpath('offers/offer');
458         $this->importLog->addLog(_t('CatalogImportTask.ProductsInImport', 'Products In Import') . count($this->importProducts));
459         unset($importData);
460 
461         //импортируем категории
462         $this->importLog->addLog(_t('CatalogImportTask.CaregotiesImport', 'Caregoties Import...'));
463         if (!$this->importCategories()) return false;
464 
465         //импортируем продукты
466         $this->importLog->addLog(_t('CatalogImportTask.CaregotiesImport', 'Caregoties Import...'));
467         if (!$this->importProducts()) return false;
468 
469         if ($this->importSettings->ImportMode == 'full') {
470             $this->importLog->addLog(_t('CatalogImportTask.ClearUnupdated', 'Clear Unupdated...'));
471             $this->unpublishNotUpdated($startTime);
472         }
473         
474         // Чистим записи в БД - картинки, которые есть в БД, но нет на диске.
475         $this->deleteEmptyDBFiles();
476 
477         $this->importLog->addLog(sprintf(_t('CatalogImportTask.ImportDoneAt', 'Import done at %s'), date("Y-m-d H:i:s")));
478         return true;
479     }
480 
481     /**
482      * Импорт категорий
483      *
484      * @return boolean - флаг успешного завершения
485      */
486     function importCategories() {
487         //Строим карту категорий
488         $allRubricsCount = count($this->importCategories);
489 
490         if ($this->importSettings->ClearCategory) {
491             singleton('Catalog')->importClearAll($this->importLog);
492         }
493 
494         // 1-й проход: сохраняем категории, запоминаем связки ImportID -> CatalogID
495         foreach ($this->importCategories as $key => $xmlCategory) {
496             unset($catalog); //удаляем, чтоб предыдущая итерация не влияла
497             $category = self::xml2array($xmlCategory);
498             
499             // Проверка каталога:
500             if (isset($category['parent']) && $category['id'] == $category['parent']) {
501                 $this->importLog->addLog(sprintf(_t('CatalogImportTask.YourselfLinkedCategory', 'Category %s ID= %s linked to yourself'), $category['Title'], $category['id']), 'error');
502                 return false; // Ошибка импорта структуры каталога - зацикливание
503             }
504             
505             if (!$this->importSettings->ClearCategory) {
506                 $catalog = Catalog::import_find($category['id']);
507             }
508             
509             //Удаляем до обновления, чтоб не делать лишних действий 
510             if (isset($category['delete']) && ($category['delete'] == 'true')) {
511                 if (isset($catalog) && $catalog) {
512                     $catalog->doUnpublish();
513                     $catalog->delete();
514                     $this->deletedCategoriesCount++;
515                 }
516                 continue;
517             }
518             
519             // Обновление
520             if (!isset($catalog) || !$catalog) {
521                 $catalog = new Catalog();
522                 $this->newCategories[] = $category;
523                 $isNew = true;
524             }
525             else {
526                 $isNew = false;
527             }
528 
529             //при первом проходе только сохраняем категории в БД и не публикуем
530             $category['Publish'] = true;
531             
532             if (isset($category['unpublish']) && ($category['unpublish'] == 'true'))
533                 $category['Publish'] = false;               
534 
535             //Цепляем фотку, если есть                      
536             if (isset($category['Photo'])) {
537                 if ($category['Photo']) {
538                     $category['Photo'] = $this->importPhoto($catalog->PhotoID, $category['Photo']);
539                 } 
540                 else {
541                     $category['Photo'] = false; //если тег <Photo> пустой - то отцепляем картинку
542                 }               
543             }           
544             
545             if (!$catalog->importUpdate($this->importLog, $category)) { //если что-то пошло не так, то пропускаем раздел
546                 unset($this->importCategories[$key]); //удаляем каталог из карты каталогов
547                 continue;
548             }           
549             // сохраняем связь по ID
550             $this->categoryMap[$category['id']] = $catalog->ID;     
551 
552             if ($isNew) {
553                 $this->importedCategoriesCount++;
554             }
555             else {
556                 $this->updatedCategoriesCount++;
557             }
558             $this->importLog->setStatusType('process', sprintf(_t('CatalogImportTask.ImportedRubrics', 'Imported %d rubrics from %d'), $this->importedCategoriesCount, $allRubricsCount));
559         }       
560         
561         // 2-й проход: проставление родителей для новых каталогов
562         foreach ($this->newCategories as $category) {
563             $unpublish = false; //снимаем с публикации плохие категории
564             $parentID = $this->importSettings->StartPage;
565             // Получаем родителя
566             if (isset($category['parent']) && $category['parent']) {
567                 $parentID = $this->findCatalogID($category['parent']);              
568                 if ($parentID === false) {
569                     $this->importLog->addLog(sprintf(_t('CatalogImportTask.CategoryWithoutParent', 'Category %s ID = %s has no parent!'), $category['Title'], $category['id']), 'warning');
570                     $unpublish = true;
571                     $parentID = $this->importSettings->StartPage;
572                 }
573             }
574             if ($catalog = Catalog::import_find($category['id'])) {
575                 $data = array(
576                     'Unpublish' => $unpublish,
577                     'ParentID' => $parentID,
578                 );
579                 if (!$catalog->importUpdate($this->importLog, $data)) { //если что-то пошло не так, то пропускаем раздел
580                     continue;
581                 }
582             } 
583             else {
584                 $this->importLog->addLog(sprintf(_t('CatalogImportTask.CategoryNotFound', 'Category %s ID = %s not found!'), $category['Title'], $category['id']), 'error');
585                 return false; // Ошибка импорта структуры каталога - не найдена категория
586             }
587         }
588         return true;
589     }
590 
591     /**
592      * Импорт продуктов
593      *
594      * @return boolean - флаг успешного завершения
595      */
596     function importProducts() {
597         $allProductsCount = count($this->importProducts);
598         
599         //если чистим категории, то чистим и товары
600         if ($this->importSettings->ClearProducts || $this->importSettings->ClearCategory) { 
601             singleton('Product')->importClearAll($this->importLog);
602         }
603         
604         foreach ($this->importProducts as $importProduct) {
605             unset($product); //удаляем, чтоб предыдущая итерация не влияла
606             $productData = self::xml2array($importProduct);
607 
608             if (!$this->importSettings->ClearProducts) {
609                 $product = Product::import_find($productData['id']);
610             }           
611             //Удаляем до обновления, чтоб не делать лишних действий
612             if (isset($productData['delete']) && ($productData['delete'] == 'true')) {
613                 if (isset($product) && $product) {
614                     $product->doUnpublish();
615                     $product->delete();
616                 $this->deletedProductsCount++;
617                 }
618                 continue;
619             }           
620             // Получаем родителя (товары без родителей игнорим, только пишем в лог) !!! только для новых товаров
621             if (!isset($product) || !$product) {
622                 if (isset($productData['parent']) && $productData['parent']) {
623                     $parentID = $this->findCatalogID($productData['parent']);
624                     if (!$parentID) {
625                         $this->importLog->addLog(sprintf(_t('CatalogImportTask.ProductWithoutParent', 'Product %s ID = %s has no parent!'), $productData['Title'], $productData['id']), 'warning');
626                         continue;
627                     }
628                 } 
629                 else {
630                     //Если не указан $productData['parent'] и у $product нету ParentID, то ругаемся
631                     $this->importLog->addLog(sprintf(_t('CatalogImportTask.ProductWithoutParent', 'Product %s ID = %s has no parent!'), $productData['Title'], $productData['id']), 'warning');
632                     continue;
633                 }
634                 
635                 $productData['ParentID'] = $parentID;
636                 $product = new Product();
637                 $isNew = true;
638             } 
639             else {
640                 $isNew = false;
641             }           
642             
643             //Цепляем фотку, если есть                      
644             if (isset($productData['Photo'])) {
645                 if ($productData['Photo']) {
646                     $productData['Photo'] = $this->importPhoto($product->PhotoID, $productData['Photo']);
647                 } 
648                 else {
649                     $productData['Photo'] = false; //если тег <Photo> пустой - то отцепляем картинку
650                 }               
651             }           
652             
653             
654             //Импортируем списочные фотки
655             $productData['Photos'] = (isset($productData['Photos'])) ? $this->importListedPhotos($productData['Photos']) : false;
656             $productData['Files'] = (isset($productData['Files'])) ? $this->importListedFiles($productData['Files']) : false;
657             $productData['SpecialCatalogs'] = (isset($productData['SpecialCatalogs'])) ? $this->importListedSpecCatalogs($productData['SpecialCatalogs']) : false;
658             
659             if (!$product->importUpdate($this->importLog, $productData)) { //если что-то пошло не так, то пропускаем раздел
660                 continue;
661             }
662             
663             if (isset($productData['unpublish']) && ($productData['unpublish'] == 'true')) {                
664                 $product->doUnpublish();
665             }               
666 
667             if ($isNew) {
668                 $this->importedProductsCount++;
669             } 
670             else {
671                 $this->updatedProductsCount++;
672             }           
673             $this->importLog->setStatusType('process', sprintf(_t('CatalogImportTask.ImportedProducts', 'Imported %d products from %d'), $this->importedProductsCount, $allProductsCount));
674         }
675         return true;
676     }
677 
678     /**
679      * Импорт списочных фоток
680      *
681      * @param  SimpleXMLElement $data
682      *
683      * @return DataObjectSet
684      */
685     function importListedPhotos($data) {
686         //Обрабатываем списочные фотографии (если есть)
687         $photos = false;
688         if (isset($data)) {
689             $photos = new DataObjectSet();
690             foreach ($data as $photo) {
691                 $path = (string) $photo->attributes()->path;
692                 $caption = (string) $photo;
693                 $importedImage = $this->importPhoto(0, $path); 
694                 if ($importedImage) {
695                     $importedImage->Title = $caption;
696                     $photos->push($importedImage);
697                 }
698             }
699         }
700         return $photos;
701     }
702 
703     /**
704      * Импорт списочных файлов
705      *
706      * @param  SimpleXMLElement $data
707      *
708      * @return DataObjectSet
709      */
710     function importListedFiles($data) {
711         //Обрабатываем списочные файлы (если есть)
712         $files = false;
713         if (isset($data)) {
714             $files = new DataObjectSet();
715             foreach ($data as $file) {
716                 $path = $file->attributes()->path;
717                 $caption = (string) $file;
718                 $importedFile = $this->importFile($path); 
719                 if ($importedFile) {
720                     $importedFile->Title = $caption;
721                     $files->push($importedFile);
722                 }
723             }
724         }
725         return $files;
726     }
727 
728     /**
729      * Импорт списочных Спец.каталогов
730      *
731      * @param  SimpleXMLElement $data
732      *
733      * @return DataObjectSet
734      */
735     function importListedSpecCatalogs($data) {
736         //Обрабатываем списочные спец.каталоги (если есть)
737         $catalogs = false;
738         if (isset($data)) {
739             $catalogs = new DataObjectSet();
740             foreach ($data as $catalog) {
741                 $id = (int)($catalog->attributes()->id);
742                 $url = Convert::raw2sql($catalog->attributes()->url);
743                 if ($id > 0)
744                     $specialCatalog = DataObject::get_by_id('SpecialCatalog', $id);
745                 if (!$specialCatalog && $url) {
746                     $specialCatalog = DataObject::get_one('SpecialCatalog', "URLSegment = '$url'");
747                 }
748                 if ($specialCatalog) {
749                     $catalogs->push($specialCatalog);
750                 }
751                 else {
752                     $this->importLog->addLog(sprintf(_t('CatalogImportTask.NoSpecialCatalog', 'No specila catalog %s'), self::xml2log($catalog)), 'warning');
753                 }
754             }
755         }
756         return $catalogs;
757     }
758 
759     /**
760      * Скрываем элементы, которых не нужно отображать после импорта
761      *
762      * @param string $importStartTime
763      *
764      * @return boolean
765      */
766     function unpublishNotUpdated($importStartTime) {
767         $this->importLog->setStatusType('process', _t('CatalogImportTask.HidingUnavailable', 'Hiding Unavailable categories and products'));
768 // !!! тоже нужны счетчики
769         // Скрываем не обновленные товары
770         $needUnpublish = DataObject::get('Product', 'LastEdited < \'' . date("Y-m-d H:i:s", $importStartTime) . '\'');
771         if ($needUnpublish)
772             foreach ($needUnpublish as $product) {
773                 switch ($this->importSettings->OldItemAction) {
774                     case 'hide':
775                         $product->AllowPurchase = 0;
776                         $product->writeToStage('Stage');
777                         if ($product->isPublished()) {
778                             $product->publish('Stage', 'Live');
779                         }
780                         break;
781                     case 'unpublish':
782                         $product->doUnpublish();
783                         break;
784                     case 'delete':
785                         $product->doUnpublish();
786                         $product->delete();
787                         break;
788                     default:
789                         $product->doUnpublish();
790                         $product->delete();
791                         break;                  
792                 }
793             }
794         // Скрываем не обновленные рубрики
795         $needUnpublish = DataObject::get('Catalog', 'ClassName = \'Catalog\' AND LastEdited < \'' . date("Y-m-d H:i:s", $importStartTime) . '\'');
796         if ($needUnpublish)
797             foreach ($needUnpublish as $catalog) {
798                 switch ($this->importSettings->OldItemAction) {
799                     case 'hide':
800                         $catalog->ShowInMenus = 0;
801                         $catalog->writeToStage('Stage');
802                         if ($catalog->isPublished()) {
803                             $catalog->publish('Stage', 'Live');
804                         }
805                         break;
806                     case 'unpublish':
807                         $catalog->doUnpublish();
808                         break;
809                     case 'delete':
810                         $catalog->doUnpublish();
811                         $catalog->delete();
812                         break;
813                     default:
814                         $catalog->doUnpublish();
815                         $catalog->delete();
816                         break;                  
817                 }
818             }
819         $this->importLog->setStatusType('process', _t('CatalogImportTask.UnavailableIsHide', 'Unavailable categories and products hide'));
820         return true;
821     }
822     
823     /**
824      * Чистим записи в БД - картинки, которые есть в БД, но нет на диске.         
825      *
826      * @return boolean
827      */
828     function deleteEmptyDBFiles() {
829         $importPath = self::get_base_url();
830         if ($this->importSettings->BaseFileURL) {
831             $importPath = $this->importSettings->BaseFileURL;
832         }
833         $images = DataObject::get('Image', "`Filename` LIKE '{$importPath}%'");
834         if ($images) {
835             foreach($images as $image) {
836                 if (!file_exists($image->getFullPath())) {
837                     DB::Query('UPDATE Catalog SET PhotoID = 0 WHERE PhotoID = ' . $image->ID);
838                     DB::Query('UPDATE Catalog_Live SET PhotoID = 0 WHERE PhotoID = ' . $image->ID);
839                     DB::Query('UPDATE Product SET PhotoID = 0 WHERE PhotoID = ' . $image->ID);
840                     DB::Query('UPDATE Product_Live SET PhotoID = 0 WHERE PhotoID = ' . $image->ID);
841                     DB::Query('DELETE FROM MediawebPage_Photo WHERE PhotoID = ' . $image->ID);                  
842                     $image->deleteFormattedImages();
843                     $image->deleteDatabaseOnly();
844                 }
845             }   
846         }
847         return true;
848     }
849 
850     /**
851      * Импорт изображений
852      *
853      * @param DataObject $object
854      * @param string $path
855      *
856      * @return Image
857      */
858     function importPhoto($photoID, $imagePath) {
859         $imagePath = trim(str_replace("\\", "/", $imagePath));
860         if ($imagePath) { // !!! $imagePath всегда true
861             $imagePath = $this->getFilePath($imagePath);
862             $fullImagePath = Director::getAbsFile($imagePath);      
863             // получим объект для картинки
864             $file = false;
865             // сначала вытащим по ID
866             if ($photoID != 0) {
867                 $file = DataObject::get_by_id('Image', $photoID);
868             }
869             
870             // не нашли по ID - поищем по пути к файлу
871             if (!$file || $file->ID == 0) {
872                 $file = DataObject::get_one('Image', "\"Filename\"='".Convert::raw2sql($imagePath)."'", true, 'ID DESC');
873             }
874             
875             // картинка физически есть на диске
876             if (is_file($fullImagePath)) {
877                 // не нашли по пути - создаем новую
878                 if (!$file || $file->ID == 0) {
879                     $file = new Image();
880                     $file->setFilename($imagePath);
881                     $file->Title = $file->Name;
882                     $file->write();
883                 }
884                 
885                 if ($file->Filename != $imagePath) {
886                     //если поменялось имя файла, то сначала пытаемся найти имеющийся
887                     $file = DataObject::get_one('Image', "\"Filename\"='".Convert::raw2sql($imagePath)."'", true, 'ID DESC');
888                     //a если не находим создаем новый (старый может быть привязан к другому объекту!)
889                     if (!$file) {
890                         $file = new Image();
891                         $file->setFilename($imagePath);
892                         $file->Title = $file->Name;
893                         $file->write();
894                     }
895                 }
896                 
897                 // Если поменялось имя файла (или новый файл)
898                 if (($photoID != $file->ID)) {
899                     if (!$file->Name) {
900                         $file->Name = basename($imagePath);
901                     }
902                     if ($file->Filename && $file->Filename != $imagePath) { // для старого имени
903                         $file->deleteFormattedImages();
904                     }
905                     $file->setFilename($imagePath);
906                     $file->deleteFormattedImages(); // для нового имени
907                     if (!$file->Title)
908                         $file->Title = $file->Name;
909                     $file->write();
910                 }
911                 elseif ($photoID != 0) {
912                     // Если файл обновлен по ftp, а путь к нему не поменялся
913                     if (filemtime($fullImagePath) > strtotime($file->LastEdited)) {
914                         //!!!! не особо работает (если скопировать WinSCP, то дата файла не обновляется, может обновится при использовании ftp)
915                         // dvp: так ты и копируешь тотже самый файл, если исходный файл измениться winscp дату изменит
916                         $file->deleteFormattedImages();
917                         //Чтобы LastEdited обновился )))))
918                         $file->forceChange();
919                         if (!$file->Title)
920                             $file->Title = $file->Name;
921                         $file->write();
922                     }
923                 }
924                 if (!$file->Hidden) {
925                     $file->Hidden = 1;
926                     $file->write();
927                 }
928                 return $file;
929             }
930             else {
931                 //Если физически файла нет, а запись в БД есть
932                 if ($file) {
933                     $file->deleteFormattedImages();
934                     $file->deleteDatabaseOnly(); // то удаляем запись
935                 }
936                 $this->importLog->addLog(sprintf(_t('CatalogImportTask.NoImage', 'No Image %s'), $imagePath), 'warning');
937             }
938         }
939         return false;
940     }
941 
942     /**
943      * Импорт файлов
944      *
945      * @param string $path
946      *
947      * @return File
948      */
949     function importFile($filePath) {
950         $filePath = trim(str_replace("\\", "/", $filePath));
951         if ($filePath) { // !!! $filePath всегда true         
952             $filePath = $this->getFilePath($filePath);
953             $fullFilePath = Director::getAbsFile($filePath);
954             //поищем по пути к файлу
955             $file = DataObject::get_one('File', "\"Filename\"='".Convert::raw2sql($filePath)."'", true, 'ID DESC');         
956             // файл физически есть на диске
957             if (is_file($fullFilePath)) {
958                 // не нашли по пути - создаем новый
959                 if (!$file || $file->ID == 0) {
960                     $file = new File();
961                     $file->setFilename($filePath);
962                     if (!$file->Title)
963                         $file->Title = $file->Name;
964                     $file->write();
965                 }
966                 if (!$file->Hidden) {
967                     $file->Hidden = 1;
968                     $file->write();
969                 }
970                 return $file;
971             }
972             else {
973                 if ($file) {
974                     $file->deleteDatabaseOnly(); // то удаляем запись
975                 }
976                 $this->importLog->addLog(sprintf(_t('CatalogImportTask.NoFile', 'No File %s'), $filePath), 'warning');
977             }
978         }
979         return false;
980     }
981 }
[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. -
Webylon 3.1 API Docs API documentation generated by ApiGen 2.8.0