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

  • Announcement_Controller
  • AnnouncementHolder_Controller
  • BookingAdminPage_Controller
  • BookingPage_Controller
  • Cart_Controller
  • CartPage_Controller
  • Catalog_Controller
  • CheckoutPage_Controller
  • ChequePayment_Handler
  • ContactsPage_Controller
  • ContentController
  • ContentNegotiator
  • Controller
  • DataObjectManager_Controller
  • DatePickerField_Controller
  • Director
  • DocPage_Controller
  • DocumentsPage_Controller
  • Event_Controller
  • EventHolder_Controller
  • FileDataObjectManager_Controller
  • FindCyrillic_Controller
  • HomePage_Controller
  • LastDoc_Controller
  • LiveCalendarWidget_Controller
  • MapObject_Controller
  • MapObjectGroup_Controller
  • MapPage_Controller
  • MediawebPage_Controller
  • ModelAsController
  • MultiUploadControls
  • NewsArchive
  • Orders1CExchange_Controller
  • Page_Controller
  • Payment_Handler
  • PhotoAlbumManager_Controller
  • Product_Controller
  • ProductSearchPage_Controller
  • ProfilePage_Controller
  • PublHolder_Controller
  • Publication_Controller
  • RatingExtension_Controller
  • RegistrationPage_Controller
  • RemoveOrphanedPagesTask
  • RequestHandler
  • Room_Controller
  • RoomCatalog_Controller
  • RootURLController
  • SapphireInfo
  • Search_Controller
  • Session
  • SimpleOrderPage_Controller
  • SiteMap_Controller
  • SpecialCatalog_Controller
  • SS_HTTPRequest
  • SS_HTTPResponse
  • StartCatalog_Controller
  • SubsitesSelectorPage_Controller
  • VideoBankPage_Controller

Interfaces

  • NestedController

Exceptions

  • SS_HTTPResponse_Exception
  1 <?php
  2 
  3 /**
  4  * Раздел каталога, содержит товары и другие разделы
  5  *
  6  * @package Catalog
  7  * @author inxo, dvp
  8  */
  9 class Catalog extends Page implements ImportInterface {
 10 
 11     static $icon = array('cms/images/treeicons/folder', 'folder');
 12     static $allowed_children = array('Product', 'Catalog', 'SpecialCatalog');
 13     static $default_child = 'Product';
 14 
 15     static $db = array(
 16         'Description' => 'Text',
 17         'OwnFilter' => 'Boolean',
 18         'FilterFields' => 'Varchar(255)',
 19         'StandartView' => 'Varchar',
 20         'ImportID' => 'Varchar' // ID-импорта, не редактируется и не отображается
 21     );
 22 
 23     static $defaults = array(
 24         'AutoChild' => 0,
 25         'FilterType' => false,
 26     );
 27 
 28     static $has_one = array(
 29         'Photo' => 'Image'
 30     );
 31 
 32     static $indexes = array(
 33         'ImportID' => true,
 34     );
 35 
 36     static $subpage_children = 'Product';
 37 
 38     /**
 39      * Настройки отображения товаров в рубриках
 40      */
 41     private static $view_options = array('table');
 42 
 43     /**
 44      * Изменяет список отображений товаров.
 45      * Используется в _config.php для настройки параметров каталога
 46      *
 47      * @param array $data - новый список отображений
 48      */
 49     static function set_view_options(array $data) {
 50         self::$view_options = $data;
 51     }
 52 
 53     /**
 54      * Возвращает текущий список отображений товаров
 55      *
 56      * @return array - текущий список отображений
 57      */
 58     static function get_view_options() {
 59         return self::$view_options;
 60     }
 61 
 62     /**
 63      * Возвращает локализованный список отображений для использования в селектах
 64      *
 65      * @param bool $addDefault - добавлять ли пункт "по-умолчанию"
 66      *
 67      * @return array - список для селектов
 68      */
 69     static function view_options_dropdown_map($addDefault = false) {
 70         $map = array();
 71         if ($addDefault) {
 72             $map[''] = _t('Catalog.ViewOption_default', 'Default');
 73         }
 74         foreach (self::get_view_options() as $key) {
 75             $map[$key] = _t('Catalog.ViewOption_'.$key, ucfirst($key));
 76         }
 77         return $map;
 78     }
 79 
 80     /**
 81      * Флаг может ли пользователь сам менять отображание товаров в рубриках
 82      */
 83     private static $user_can_change_view = false;
 84 
 85     /**
 86      * Устанавливает флаг $user_can_change_view
 87      *
 88      * @param bool $val
 89      */
 90     static function allow_user_change_view($val = true) {
 91         self::$user_can_change_view = $val;
 92     }
 93 
 94     /**
 95      * Флаг Показывать товары из подкатегорий
 96      */
 97     private static $show_products_from_subcategories = false;
 98 
 99     /**
100      * Возвращает флаг $show_products_from_subcategories
101      *     
102      */
103     static function get_show_products_from_subcategories() {
104         return self::$show_products_from_subcategories;
105     }
106     
107     /**
108      * Устанавливает флаг $show_products_from_subcategories
109      *
110      * @param bool $val
111      */
112     static function set_show_products_from_subcategories($val = true) {
113         self::$show_products_from_subcategories = $val;
114     }
115     
116     /**
117      * Флаг скрытия в каталоге товаров с AllowPurchase == 0
118      */
119     static $hide_allow_purchase_products = false;
120 
121     /**
122      * Устанавливает флаг показа в каталога товаров с AllowPurchase == 0
123      *
124      * @param bool $val
125      */
126     static function hide_allow_purchase_products($val) {
127         self::$hide_allow_purchase_products = $val;
128     }
129 
130     /*--------------- Функции для импорта -------------------*/
131     /**
132      * Список полей, которые могут быть в данных импорта
133      */
134     static $possibleFields = array('Title', 'Description', 'Content', 'URLSegment', 'MenuTitle', 'MetaTitle', 'MetaDescription', 'MetaKeywords', 'Sort');
135 
136     /**
137      * Добавление полей, которые могут быть в данных импорта
138      *
139      * @param array $fields
140      */
141     static function addPossibleFields($fields) {
142         if ($fields)
143             foreach($fields as $field)
144                 self::$possibleFields[] = $field;
145     }
146 
147      /**
148       * Возвращает объект по его ImportID
149       * @return DataObject||null
150       */
151     static function import_find($importID) {
152         return DataObject::get_one('Catalog', "ImportID = '" . Convert::raw2sql($importID) . "'");
153     }
154 
155     /**
156      * Обновляет объект
157      *
158      * @param $importLog - объект для протоколиорвания импорта (или сама задача), для возможности записать сообщения об ошибках
159      * @param $data - массив с данными для импорта
160      * @return bool - флаг можно ли продолжать импорт
161      */
162     function importUpdate($importLog, $data) {
163         if (!$this->importValidate($importLog, $data)) {
164             return false;
165         }
166 
167         $rs = $this->extend('onBeforeImport', $importLog, $data);
168         if ($rs && max($rs) == false) return false;
169 
170         if (isset($data['id'])) {
171             $this->ImportID = $data['id'];
172         }
173 
174         foreach(self::$possibleFields as $field) {
175             if (isset($data[$field]))
176             $this->{$field} = $data[$field];
177         }
178 
179         if (isset($data['Photo'])) {
180             $this->PhotoID = ($data['Photo']) ? $data['Photo']->ID : 0;
181         }
182 
183         if (isset($data['ParentID']) && !$this->ParentID) { //не обновляем родителя у уже импортированных разделов  (для возможности ручного изменения структуры каталога на сайте).
184             $this->ParentID = $data['ParentID'];
185         }
186         if (isset($data['Publish'])) {
187             if ($data['Publish']) { // Если нету родителя, хотя должен быть (не корневой раздел), то не публикуем
188                 $this->doPublish();
189             } else {
190                 $this->doUnpublish();
191             }
192         } else {
193             //если флаг публикациии не задан, то сохраняем текущее состояние
194             if ($this->isPublished()) {
195                 $this->doPublish();
196             } else {
197                 $this->write();
198             }
199         }
200         if (isset($data['Unpublish']) && $data['Unpublish']) {
201             $this->doUnpublish();
202         }
203         $this->extend('onAfterImport', $importLog, $data);
204         return true;
205     }
206 
207     /**
208      * Проверяет данные полей объекта на соответствие типам
209      * @param $importLog - объект для протоколиорвания импорта (или сама задача), для возможности записать сообщения об ошибках
210      * @return bool - флаг можно ли продолжать импорт
211      */
212     function importValidate($importLog, $data) {
213         if ((!$this->Title) && (!isset($data['Title']) || trim($data['Title']) == '')) { // если у каталога нет Title и Title нет в импорте, то ругаемся
214             $importLog->addLog("Раздел каталога с id='{$data['id']}' не имеет названия!", 'error');
215             return false;
216         }
217         if (isset($data['Title']) && trim($data['Title']) == '') { // если тег <Title> задан, но пустой, то ругаемся
218             $importLog->addLog("Раздел каталога с id='{$data['id']}' не имеет названия!", 'error');
219             return false;
220         }
221         if ((!$this->Title) && (!isset($data['Title']) || trim($data['Title']) == '')) {
222             $importLog->addLog("Раздел каталога с id='{$data['id']}' не имеет названия!", 'error');
223             return false;
224         }
225         if (isset($data['Sort']) && $data['Sort'] != (int)$data['Sort']) {
226             $importLog->addLog("Параметр Sort раздела каталога {$data['Title']} не является целым числом!", 'warning');
227             $data['Sort'] = 0;
228         }
229         $rs = $this->extend('importValidate', $importLog, $data);
230         if ($rs && max($rs) == false) return false;
231         return true;
232     }
233 
234      /**
235      * Выполняет удаление всех объектов перед импортом
236      */
237     function importClearAll($importLog) {
238         $oldMode = Versioned::get_reading_mode();
239         
240         Versioned::reading_stage('Stage');
241         $catalogs = DataObject::get('Catalog', "ClassName <> 'SpecialCatalog'");
242         if ($catalogs)
243             foreach($catalogs as $catalog) {
244                 $catalog->doUnpublish();
245                 $catalog->delete(); // !!! что будет с детьми ?
246             }
247         unset($catalogs);
248     
249         Versioned::reading_stage('Live');
250         $catalogs = DataObject::get('Catalog', "ClassName <> 'SpecialCatalog'");
251         if ($catalogs)
252             foreach($catalogs as $catalog) {
253                 $catalog->doUnpublish();
254                 $catalog->delete(); // !!! что будет с детьми ?
255             }
256             
257         Versioned::set_reading_mode($oldMode);
258     }
259     /*--------------- Конец функций для импорта -------------------*/
260 
261     /*
262      * Bмеет ли дочерний Товар заданной поле
263      *
264      * @param string $field
265      * @return bool
266      */
267     static function productFieldTable($field) {
268         $classes = array_reverse(ClassInfo::ancestry(singleton(static::$subpage_children)));
269         foreach($classes as $class) {
270             $fields = Object::uninherited_static($class, 'db');
271             if (isset($fields[$field])) {
272                 return $class;
273             }
274             $fields = Object::uninherited_static($class, 'has_one'); // фильтрация по has_one параметрам
275             if ((substr($field, -2) == 'ID') && ($hasOneField = substr($field, 0, -2))) { // выкидываем ID
276                 if (isset($fields[$hasOneField])) {
277                     return $class;
278                 }
279             }
280         }
281         return false;
282     }
283     
284     function getCMSFields() {
285         SiteTree::disableCMSFieldsExtensions();
286         $fields = parent::getCMSFields();
287         SiteTree::enableCMSFieldsExtensions();
288 
289         $fields->addFieldToTab('Root.Content.Main', new ImageField('Photo', $this->fieldLabel('Photo')), 'Content');
290         $fields->addFieldToTab('Root.Content.Main', new TextareaField('Description', $this->fieldLabel('Description')), 'Content');
291 
292         //$fields->addFieldToTab('Root.Content', new Tab('FullContent', _t('Catalog.tab_FullContent', 'Content')), 'Metadata');
293         //$fields->addFieldToTab('Root.Content.FullContent', $fields->dataFieldByName('Content'));
294         $product_class = static::$subpage_children;
295 
296         if (count(self::get_view_options()) > 1 || $product_class::filter_fields_list()) {
297             $fields->addFieldToTab('Root.Content', new Tab('ViewSetup', _t('Catalog.tab_ViewSetup', 'View Setup')), 'Metadata');
298             if (count(self::get_view_options()) > 1)
299                 $fields->addFieldToTab('Root.Content.ViewSetup', new DropdownField("StandartView", $this->fieldLabel('StandartView'), self::view_options_dropdown_map(true)));
300 
301             if ($product_class::filter_fields_list()) {
302                 $fields->addFieldToTab("Root.Content.ViewSetup", new CheckboxField('OwnFilter', $this->fieldLabel('OwnFilter')));
303                 $fields->addFieldToTab("Root.Content.ViewSetup", new CheckboxSetField('FilterFields', $this->fieldLabel('FilterFields'), $product_class::filter_fields_list()));
304             }
305         }
306 
307         Requirements::css('catalog/css/ProductAdmin.css');
308 //        Requirements::javascript('catalog/javascript/ProductAdmin.js');
309 
310         /// Товары в виде вкладки
311         $fields->addFieldToTab('Root.Content', new Tab('tabSubPages', _t('Catalog.tab_Products', 'Products')), 'Metadata');
312 
313 
314         // Таблица с товарами
315         $sp = new SubpageListField('Subpages', $this, $product_class);
316         $sp->Dragable = true;
317         $url = '<a href=\"admin/show/$ID\">$value</a>';
318         $sp->setFieldFormatting(array_combine(array_keys(singleton($product_class)->summaryFields()), array_fill(0, count(singleton($product_class)->summaryFields()), $url)));
319         $fields->addFieldToTab('Root.Content.tabSubPages', $sp);
320 
321         // Виртуальные товары
322         /*  Убираем, в таком виде виртуальные товары не используем.
323         $fields->addFieldToTab('Root.Content.Products', new CheckboxField('Copy', _t('Catalog.CopyOrMove', 'Create virtual product on drag-and-drop'), 0));
324 
325         $fields->addFieldToTab('Root.Content', new Tab('Virtual', _t('Catalog.tab_Virtual', 'Virtual products')), 'Metadata');
326         $vsp = new SubpageListField('VirtualSubpages', $this, 'VirtualProduct');
327         $url = '<a href=\"admin/show/$CopyContentFromID\">$value</a>';
328         $vsp->Markable = false;
329         $vsp->setFieldFormatting(array_combine(array_keys(singleton('VirtualProduct')->summaryFields()), array_fill(0, count(singleton('VirtualProduct')->summaryFields()), $url)));
330         $fields->addFieldToTab('Root.Content.Virtual', $vsp);
331         */
332 
333         $this->extend('updateCMSFields', $fields);
334 
335         return $fields;
336     }
337 
338     /**
339      * Метод для шаблонов для проверки флага $user_can_change_view
340      *
341      * @return bool
342      */
343     public function AllowChangeView() {
344         return self::$user_can_change_view && (count(self::$view_options) > 1);
345     }
346 
347     /**
348      * Возвращает список подрубрик
349      *
350      * @return DataObjectSet
351      */
352     public function Subcats() {
353         return DataObject::get("Catalog", "ParentID = {$this->ID} AND ShowInMenus=1", "Sort");
354     }
355 
356     /**
357      * Число товаров в рубрике
358      *
359      * @return int
360      */
361     public function CountItems() {
362         $count = 0;
363         if ($items = DataObject::get("Product", "ParentID = {$this->ID}")) {
364             $count += $items->Count();
365         }
366         if ($virtualitems = DataObject::get("VirtualProduct", "ParentID = {$this->ID}")) {
367             $count += $virtualitems->Count();
368         }
369         return $count;
370     }
371 
372     /**
373      * Возвращает режим сортировки без учета выбора пользователя
374      *
375      * @return string
376      */
377     public function defaultSort() {
378         return ($this->SiteConfig->CatalogDefaultSort)? $this->SiteConfig->CatalogDefaultSort : 'title';
379     }
380 
381     /**
382      * Возвращает режим отображения без учета выбора пользователя
383      *
384      * @return string
385      */
386     public function defaultView() {
387         if ($this->StandartView)
388             return $this->StandartView;
389 
390         return ($this->SiteConfig->CatalogDefaultView) ? $this->SiteConfig->CatalogDefaultView : 'table';
391     }
392 
393     /**
394      * Возвращает список полей для фильтра
395      *
396      * @return array список полей товара для фильтра в текущей рубрике
397      */
398     public function catalogFilterFields() {
399         if ($this->OwnFilter) {
400             return explode(',', $this->FilterFields);
401         }
402         if ($this->ParentID && ($parent = $this->Parent()) && $parent->ID && is_a($parent, 'Catalog')) {
403             return $parent->catalogFilterFields();
404         }
405         return explode(',', $this->SiteConfig->CatalogDefaultFilter);
406     }
407 
408     /**
409      * Возвращает список товаров с учетом фильтрации и сортировки
410      *
411      * @param string $order - текущая сортировка
412      * @param array $filters - массив условий фильтрации (из формы)
413      * @param string $limit  - строка для sql limit
414      *
415      * @return DataObjectSet - текущая страница выборки товаров
416      */
417     public function filteredProducts($order=null, $filters=null, $limit=null) {
418         $orderby = false;
419         if (!is_null($order)) {
420             $orderby = Product::sort_options_orderby($order);
421         }
422         if (!$orderby) {
423             $orderby = Product::sort_options_orderby($this->defaultSort());
424         }
425 
426         $query = $this->getProductsListQuery($orderby, $limit);
427         if ($filters) {
428             $context = $this->productSearchContext();
429             $query = $context->getQuery($filters, $orderby, $limit, $query);
430         }
431         $this->extend('updateFilteredProductsQuery', $query, $filters);
432 
433         $results = DataObject::buildDataObjectSet($query->execute());
434         if($results) $results->parseQueryLimit($query);
435         return $results;
436     }
437     
438     function getProductsListQuery($orderby, $limit) {
439         $where = $this->getProductsListWhere();
440         $query = singleton(self::$subpage_children)->extendedSQL(implode(' AND ', $where), $orderby, $limit);
441         return $query;
442     }
443     
444     /*
445      * Базовое условие для получения список товаров данной рубрики (всех)
446      *   
447      * @return  array
448      */
449     function getProductsListWhere() {
450         $where = array();
451         $productClasses = array();
452         if($subclasses = ClassInfo::subclassesFor(self::$subpage_children)) {
453             foreach ($subclasses as $subclass)
454                 $productClasses[] = $subclass;
455         } else {
456             $productClasses[] = self::$subpage_children;
457         }
458         $where[] = "(ClassName IN ('".implode("','", $productClasses)."'))";
459         
460         $categoryIDs = array($this->ID);
461         // товары из подкатегорий
462         if (Catalog::get_show_products_from_subcategories()) {
463             $categoryIDs = $this->getDescendantIDList();
464             $categoryIDs[] = $this->ID;
465         }
466         $where[] = "(ParentID IN (".implode(',', $categoryIDs) ."))";
467 
468         // скрытие товаров с AllowPurchase == 0
469         if (self::$hide_allow_purchase_products) {
470             $where[] = "(AllowPurchase = 1)";
471         }
472         return $where;
473     }
474     
475     // общее количество продуктов в текущей рубрике
476     function TotalProductsCount() {
477         $where = $this->getProductsListWhere();
478         $stage = (Versioned::current_stage() == 'Live' ? '_Live' : '');
479         $query = "SELECT SiteTree{$stage}.ID FROM SiteTree{$stage} JOIN Product{$stage} ON SiteTree{$stage}.ID = Product{$stage}.ID WHERE " . implode(' AND ', $where);
480         return DB::Query($query)->numRecords();
481     }
482 
483     /**
484      * Возвращает поисковый контекст для фильтрации товаров в каталоге
485      *
486      * @return SearchContext
487      */
488     public function productSearchContext() {
489         $product = singleton(static::$subpage_children);
490         return new SearchContext(
491             static::$subpage_children,
492             $product->scaffoldSearchFields(array('restrictFields'=> $this->catalogFilterFields())),
493             $product->defaultSearchFilters()
494         );
495     }
496 }
497 
498 class Catalog_Controller extends Page_Controller {
499 
500     public $CurrentSort;
501     public $CurrentView;
502 
503     function init() {
504         parent::init();
505 
506         $this->CurrentSort = $this->defaultSort();
507         if ($sort = Session::get('Catalog.Sort')) {
508             $this->CurrentSort = $sort;
509         }
510 
511         $this->CurrentView = $this->defaultView();
512         if ($this->AllowChangeView() && $view = Session::get('Catalog.View')) {
513             $this->CurrentView = $view;
514         }
515     }
516 
517     function isEmptyContent() {
518         return parent::isEmptyContent() && $this->CountItems() == 0;
519     }
520 
521     /**
522      * Устанавливает пользовательское значение CurrentView (если разрешено)
523      *
524      * @param string $value
525      */
526     public function setView($value = '') {
527         if (!$value || !$this->AllowChangeView())
528             return;
529         if ($value == 'default') {
530             Session::set('Catalog.View', '');
531             $this->CurrentView = $this->defaultView();
532         }
533         elseif (in_array($value, Catalog::get_view_options())) {
534             Session::set('Catalog.View', $value);
535             $this->CurrentView = $value;
536         }
537     }
538 
539     /**
540      * Устанавливает пользовательский вариант CurrentSort
541      *
542      * @param string $value
543      */
544     public function setSort($value = '') {
545         if (!$value) return;
546 
547         if ($value == 'default') {
548             Session::set('Catalog.Sort', '');
549             $this->CurrentSort = $this->defaultSort();
550         }
551         elseif (array_key_exists($value, Product::get_sort_options())) {
552             Session::set('Catalog.Sort', $value);
553             $this->CurrentSort = $value;
554         }
555     }
556 
557     /**
558      * Возвращает параметры для формирования списка товаров
559      * 
560      * @param array $values - массив кандидатов на параметры
561      * 
562      * @return array
563      */
564     function cleanParams($values = false) {
565         if (!$values)
566             $values = $this->getRequest()->requestVars();
567 
568         unset($values['url']);
569         unset($values['start']);
570         unset($values['sort']);
571         unset($values['view']);
572         unset($values['action_filter']);
573         unset($values['action_filterclear']);
574         return $values;
575     }
576     
577     /**
578      * Возвращает правильную ссылку на каталог с учетом фильтров и других параметров
579      * 
580      * @param array $params - параметры для формирования ссылки
581      * 
582      * @return string
583      */
584     function linkWithParams($params = array()) {
585         if (!$params)
586             $params = $this->cleanParams();
587         if (!$params)
588             return $this->Link();
589 
590         $action = ($this->IsFilterActive()) ? 'filter' : '';
591         return $this->Link($action) . '?' . http_build_query($params);
592     }
593     
594     /*
595      * Получение возможных вариантов сортировок
596      */
597     function Sorts($default = false) {
598         $values = $this->cleanParams();
599         
600         $items = new DataObjectSet();
601         foreach (Product::sort_options_dropdown_map($default) as $id => $title) {
602             if (!$id) $id = 'default';
603             $values['sort'] = $id;
604             $items->push(new ArrayData(array(
605                 'ID' => $id,
606                 'Title' => $title,
607                 'Link' => $this->linkWithParams($values),
608                 'isCurrent' => ($id == $this->CurrentSort),
609                 'LinkOrCurrent' => ($id == $this->CurrentSort) ? 'current' : 'link',
610             )));
611         }
612         return $items;
613     }
614 
615     /*
616      * Получение возможных вариантов отображений
617      */
618     function Views($default = false) {
619         if (!$this->AllowChangeView()) return false;
620 
621         $values = $this->cleanParams();
622         
623         $items = new DataObjectSet();
624         foreach (Catalog::view_options_dropdown_map($default) as $id => $title) {
625             if (!$id) $id = 'default';
626             $values['view'] = $id;
627             $items->push(new ArrayData(array(
628                 'ID' => $id,
629                 'Title' => $title,
630                 'Link' => $this->linkWithParams($values),
631                 'isCurrent' => ($id == $this->CurrentView),
632                 'LinkOrCurrent' => ($id == $this->CurrentView) ? 'current' : 'link',
633             )));
634         }
635         return $items;
636     }
637 
638     /**
639      * Проверяет включен ли режим "все товары"
640      *
641      * @return bool
642      */
643     function ShowAll() {
644         return isset($_GET['all']);
645     }
646 
647     /**
648      * Возвращает ссылку на режим просмотра "все товары"/"постраничный" в зависимости от текущего режима
649      *
650      * @return string
651      */
652     function ShowAllLink() {
653         $values = $this->cleanParams();
654         if ($this->ShowAll())
655             unset($values['all']);
656         else
657             $values['all'] = 1;
658         
659         return $this->linkWithParams($values);
660     }
661 
662     /**
663      * Возвращает название для ссылки на переключение режима просмотра "все товары"/"постраничный" в зависимости от текущего режима
664      *
665      * @return string
666      */
667     function ShowAllTitle() {
668         return ($this->ShowAll()) ? _t('Catalog.ShowAll_on', 'show all') : _t('Catalog.ShowAll_off', 'show by page');
669     }
670 
671     function getSeoCanonicalLink() {
672         if (!$this->data()->SeoIsAlternative) return false;
673         //if ($start = $this->getRequest()->requestVar('start')) return $this->Link() . '?start=' . $start;
674         return $this->Link();
675     }
676 
677     /*
678      * Получение формы фильтра товаров
679      *
680      * @return Form
681      */
682     function Filters() {
683         if ($this->TotalProductsCount() == 0) {
684             return false;
685         }
686         
687         $context = $this->productSearchContext();
688         $fields = $context->getSearchFields();
689         $this->extend("updateFilterFields", $fields);
690 
691         $actions = new FieldSet(
692             new FormAction('filter', _t('Catalog.FILTER_SELECT','Выбрать')),
693             new FormAction('filterclear', _t('Catalog.FILTER_CLEAR','Очистить'))
694         );
695 
696         $validator = new RequiredFields();
697         $validator->setJavascriptValidationHandler('none');
698         $form = new Form($this, 'Filters', $fields, $actions, $validator);
699         $form->disableSecurityToken();
700         $form->setFormMethod('GET');
701         $form->addExtraClass('filters');
702         $form->setTemplate('CatalogFilterForm');
703         
704         $this->extend('updateFilterForm', $form);
705 
706         if ($form->Fields()->Count() == 0)
707             return false;
708         
709         if ($this->getRequest()) {
710             $form->loadDataFrom($this->getRequest()->requestVars());
711         }
712         
713         return $form;
714     }
715 
716     function index() {
717         $request = $this->getRequest();
718         $start = intval($request->getVar('start'));
719 
720         // Установка режима просмотра
721         if ($view = $request->requestVar('view')) {
722             $this->setView($view);
723             $this->data()->SeoIsAlternative = true;
724         }
725 
726         // Установка сортировки
727         $sort = $request->requestVar('sort');
728         if ($sort) {
729             $this->data()->SeoIsAlternative = true;
730             if ($sort != $this->CurrentSort) {
731                 $this->setSort($sort);
732                 $start = 0;
733             }
734         }
735 
736         // Pagenation
737         $limit = '';
738         if (!$this->ShowAll()) {
739             $count = ($this->SiteConfig->ProductPerPage) ? $this->SiteConfig->ProductPerPage : 20;
740             $limit =  "$start,$count";
741         }
742         else {
743             $this->data()->SeoIsAlternative = true;
744         }
745 
746         // Если фильтр задействован - то выставляем флаг SeoIsAlternative
747         if ($this->IsFilterActive()) {
748             $this->data()->SeoIsAlternative = true;
749         }
750         
751         $this->Products = $this->filteredProducts($this->CurrentSort, $this->cleanParams(), $limit);
752         if ($this->hasMethod('setSEOVars')) {
753             $this->setSEOVars($this->Products); //Выставляем SEO-переменные (ф-я setSEOVars находится в Webylon Page_Controller)
754         }
755 
756         if (Director::is_ajax()) {
757             $this->getResponse()->addHeader('X-Set-Url', $this->linkWithParams());
758         }
759 
760         $action = (Director::is_ajax()) ? 'ajax' : 'index';
761         return parent::defaultAction($action);
762     }
763 
764     /**
765      * Обработчик формы - сбрасывает фильтр
766      *
767      * @param array $data - Данные формы
768      * @param Form $form - форма
769      *
770      * @return HTTP редирект на страницу каталога
771      */
772     function filterclear($data) {
773         // Чистка фильтров (просто убирает GET-параметры)
774         return $this->redirect($this->Link());
775     }
776 
777     /*
778      * Обработчик формы - Фильтрация в пределах одной рубрики
779      */
780     function filter($data, $form = null) {
781         if (Director::is_ajax()) {
782             $this->getResponse()->addHeader('X-Set-Url', $this->linkWithParams());
783         }
784         
785         if ($form) 
786             return $this->redirect($this->linkWithParams());
787         
788         return $this->index();
789     }
790 
791     function IsFilterActive() {
792         $data = $this->cleanParams();
793         if (!count($data)) return false;
794 
795         $filters = $this->Filters();
796         if (!$filters) {
797             return false;
798         }
799         $filterFields = $filters->Fields();
800         foreach ($data as $k => $v) {
801             if ($filterFields->fieldByName($k)) {
802                 if ($v !== '') return true;
803             }
804         }
805         return false;
806     }
807 }
808 
[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