Вход Регистрация
Файл: system/controllers/content/model.php
Строк: 2991
<?php

class modelContent extends cmsModel {

    protected 
$pub_filter_disabled = false;
    protected 
$pub_filtered = false;

    private static 
$all_ctypes = null;

    public function 
__construct() {

        
parent::__construct();

        
$this->loadAllCtypes();

    }

//============================================================================//
//=======================    ТИПЫ КОНТЕНТА   =================================//
//============================================================================//

    
public function addContentType($ctype){

        if(!isset(
$ctype['labels'])){
            
$ctype['labels'] = array(
                
'one'     => $ctype['name'],
                
'two'     => $ctype['name'],
                
'many'    => $ctype['name'],
                
'create'  => $ctype['name'],
                
'list'    => '',
                
'profile' => ''
            
);
        }

        
$id = $this->insert('content_types', $ctype);

        
$config = cmsConfig::getInstance();

        
// получаем структуру таблиц для хранения контента данного типа
        
$content_table_struct      = $this->getContentTableStruct();
        
$fields_table_struct       = $this->getFieldsTableStruct();
        
$props_table_struct        = $this->getPropsTableStruct();
        
$props_bind_table_struct   = $this->getPropsBindTableStruct();
        
$props_values_table_struct = $this->getPropsValuesTableStruct();

        
// создаем таблицы
        
$table_name = $this->table_prefix . $ctype['name'];

        
$this->db->createTable($table_name, $content_table_struct);
        
$this->db->createTable("{$table_name}_fields", $fields_table_struct, $config->db_engine);
        
$this->db->createCategoriesTable("{$table_name}_cats");
        
$this->db->createCategoriesBindsTable("{$table_name}_cats_bind");

        
$this->db->createTable("{$table_name}_props", $props_table_struct, $config->db_engine);
        
$this->db->createTable("{$table_name}_props_bind", $props_bind_table_struct, $config->db_engine);
        
$this->db->createTable("{$table_name}_props_values", $props_values_table_struct, $config->db_engine);

        
//
        // добавляем стандартные поля
        //

        // заголовок
        
$this->addContentField($ctype['name'], array(
            
'name' => 'title',
            
'title' => LANG_TITLE,
            
'type' => 'caption',
            
'ctype_id' => $id,
            
'is_in_list' => 1,
            
'is_in_item' => 1,
            
'is_in_filter' => 1,
            
'is_fixed' => 1,
            
'is_fixed_type' => 1,
            
'is_system' => 0,
            
'options' => array(
                
'label_in_list' => 'none',
                
'label_in_item' => 'none',
                
'min_length' => 3,
                
'max_length' => 255,
                
'is_required' => true
            
)
        ), 
true);

        
// дата публикации
        
$this->addContentField($ctype['name'], array(
            
'name' => 'date_pub',
            
'title' => LANG_DATE_PUB,
            
'type' => 'date',
            
'ctype_id' => $id,
            
'is_in_list' => 1,
            
'is_in_item' => 1,
            
'is_in_filter' => 1,
            
'is_fixed' => 1,
            
'is_fixed_type' => 1,
            
'is_system' => 1,
            
'options' => array(
                
'label_in_list' => 'none',
                
'label_in_item' => 'left',
                
'show_time' => true
            
)
        ), 
true);

        
// автор
        
$this->addContentField($ctype['name'], array(
            
'name' => 'user',
            
'title' => LANG_AUTHOR,
            
'type' => 'user',
            
'ctype_id' => $id,
            
'is_in_list' => 1,
            
'is_in_item' => 1,
            
'is_in_filter' => 0,
            
'is_fixed' => 1,
            
'is_fixed_type' => 1,
            
'is_system' => 1,
            
'options' => array(
                
'label_in_list' => 'none',
                
'label_in_item' => 'left'
            
)
        ), 
true);

        
// фотография
        
$this->addContentField($ctype['name'], array(
            
'name' => 'photo',
            
'title' => LANG_PHOTO,
            
'type' => 'image',
            
'ctype_id' => $id,
            
'is_in_list' => 1,
            
'is_in_item' => 1,
            
'is_fixed' => 1,
            
'options' => array(
                
'size_teaser' => 'small',
                
'size_full' => 'normal',
                
'sizes' => array('micro', 'small', 'normal', 'big')
            )
        ), 
true);

        
// описание
        
$this->addContentField($ctype['name'], array(
            
'name' => 'content',
            
'title' => LANG_DESCRIPTION,
            
'type' => 'text',
            
'ctype_id' => $id,
            
'is_in_list' => 1,
            
'is_in_item' => 1,
            
'is_fixed' => 1,
            
'options' => array(
                
'label_in_list' => 'none',
                
'label_in_item' => 'none'
            
)
        ), 
true);

        
cmsCache::getInstance()->clean('content.types');

        return 
$id;

    }

//============================================================================//
//============================================================================//

    
public function updateContentType($id, $item){

        
cmsCache::getInstance()->clean('content.types');

        return 
$this->update('content_types', $id, $item);

    }

//============================================================================//
//============================================================================//

    
public function deleteContentType($id){

        
$ctype = $this->getContentType($id);

        if (
$ctype['is_fixed']) { return false; }

        
$this->disableDeleteFilter()->disableApprovedFilter()->
                
disablePubFilter()->disablePrivacyFilter();

        
$items = $this->getContentItems($ctype['name']);
        if (
$items){
            foreach(
$items as $item){
                
$this->deleteContentItem($ctype['name'], $item['id']);
            }
        }

        
cmsCore::getModel('tags')->recountTagsFrequency();

        
$this->delete('content_types', $id);
        
$this->delete('content_datasets', $id, 'ctype_id');

        
$table_name = $this->table_prefix . $ctype['name'];

        
$this->db->dropTable("{$table_name}");
        
$this->db->dropTable("{$table_name}_fields");
        
$this->db->dropTable("{$table_name}_cats");
        
$this->db->dropTable("{$table_name}_filters");
        
$this->db->dropTable("{$table_name}_cats_bind");
        
$this->db->dropTable("{$table_name}_props");
        
$this->db->dropTable("{$table_name}_props_bind");
        
$this->db->dropTable("{$table_name}_props_values");

        
cmsCache::getInstance()->clean('content.types');

        
// связь как родитель
        
$relations = $this->getContentRelations($id);

        if(
$relations){
            foreach (
$relations as $relation) {

                
$this->deleteContentRelation($relation['id']);

                
$parent_field_name = "parent_{$ctype['name']}_id";

                if(
$relation['target_controller'] != 'content'){

                    
$this->setTablePrefix('');

                    
$target_ctype = array(
                        
'name' => $relation['target_controller']
                    );

                } else {

                    
$this->setTablePrefix(cmsModel::DEFAULT_TABLE_PREFIX);

                    
$target_ctype = $this->getContentType($relation['child_ctype_id']);

                }

                if (
$this->isContentFieldExists($target_ctype['name'], $parent_field_name)){
                    
$this->deleteContentField($target_ctype['name'], $parent_field_name, 'name', true);
                }

            }
        }

        
// связь как дочка
        
$relations = $this->filterEqual('child_ctype_id', $id)->getContentRelations();
        if(
$relations){
            foreach (
$relations as $relation) {
                
$this->deleteContentRelation($relation['id']);
            }
        }

        return 
true;

    }

//============================================================================//
//============================================================================//

    
public function getContentTypesCount(){

        if(!
self::$all_ctypes){
            return 
0;
        }

        return 
count(self::$all_ctypes);

    }

//============================================================================//
//============================================================================//

    
public function loadAllCtypes() {

        return !isset(
self::$all_ctypes) ? $this->reloadAllCtypes() : $this;

    }

    public function 
reloadAllCtypes($enabled = true) {

        if(
$enabled){
            
$this->filterEqual('is_enabled', 1);
        }

        
self::$all_ctypes = $this->getContentTypesFiltered();
        return 
$this;
    }

    public function 
getContentTypes(){
        return 
self::$all_ctypes;
    }

    public function 
getContentTypesFiltered(){

        
$this->useCache('content.types');

        if (!
$this->order_by){ $this->orderBy('ordering'); }

        return 
$this->get('content_types', function($item, $model){

            
$item['options'] = cmsModel::yamlToArray($item['options']);
            
$item['labels'] = cmsModel::yamlToArray($item['labels']);

            
// YAML некорректно преобразовывает пустые значения массива
            // убрать после перевода всего на JSON
            
if(!empty($item['options']['list_style'])){
                if(
is_array($item['options']['list_style'])){
                    
$list_styles = array();
                    foreach (
$item['options']['list_style'] as $key => $value) {
                        
$list_styles[$key] = is_array($value) ? '' : $value;
                    }
                    
$item['options']['list_style'] = $list_styles;
                }
            }
            if(!empty(
$item['options']['context_list_cover_sizes'])){
                if(
is_array($item['options']['context_list_cover_sizes'])){
                    
$list_styles = array();
                    foreach (
$item['options']['context_list_cover_sizes'] as $key => $value) {
                        
$list_styles[$key?$key:''] = $value;
                    }
                    
$item['options']['context_list_cover_sizes'] = $list_styles;
                }
            }

            return 
$item;

        });

    }

    public function 
getContentTypesCountFiltered(){
        return 
$this->getCount('content_types');
    }

    public function 
getContentTypesNames(){

        if(!
self::$all_ctypes){
            return 
false;
        }

        foreach (
self::$all_ctypes as $ctype_id => $ctype) {
            
$names[$ctype_id] = $ctype['name'];
        }

        return 
$names;

    }

    public function 
reorderContentTypes($ctypes_ids_list){

        
$this->reorderByList('content_types', $ctypes_ids_list);

        
cmsCache::getInstance()->clean('content.types');

        return 
true;

    }

//============================================================================//
//============================================================================//

    
public function getContentType($id, $by_field='id'){

        if(!
self::$all_ctypes){
            return 
false;
        }

        foreach (
self::$all_ctypes as $ctype_id => $ctype) {

            if(
$ctype[$by_field] == $id) {
                return 
$ctype;
            }

        }

        return 
false;

    }

    public function 
getContentTypeByName($name){
        return 
$this->getContentType($name, 'name');
    }

//============================================================================//
//======================    ПАПКИ КОНТЕНТА   =================================//
//============================================================================//

    
public function addContentFolder($ctype_id, $user_id, $title) {
        return 
$this->insert('content_folders', [
            
'ctype_id' => $ctype_id,
            
'user_id'  => $user_id,
            
'title'    => $title
        
]);
    }

    public function 
getContentFolders($ctype_id, $user_id) {
        return 
$this->filterEqual('ctype_id', $ctype_id)->
                
filterEqual('user_id', $user_id)->
                
get('content_folders');
    }

    public function 
getContentFolder($id) {
        return 
$this->joinUserLeft()->getItemById('content_folders', $id, function($item, $model){
            
$item['user'] = array(
                
'id'        => $item['user_id'],
                
'slug'      => $item['user_slug'],
                
'nickname'  => $item['user_nickname'],
                
'avatar'    => $item['user_avatar']
            );
            return 
$item;
        });
    }

    public function 
getContentFolderByTitle($title, $ctype_id, $user_id) {
        return 
$this->filterEqual('ctype_id', $ctype_id)->
                
filterEqual('user_id', $user_id)->
                
getItemByField('content_folders', 'title', $title);
    }

    public function 
updateContentFolder($id, $folder) {
        return 
$this->update('content_folders', $id, $folder);
    }

    public function 
deleteContentFolder($folder, $is_delete_content = true) {

        
$ctype = $this->getContentType($folder['ctype_id']);

        
$this->filterEqual('folder_id', $folder['id']);

        if (!
$is_delete_content) {
            
$table_name = $this->table_prefix . $ctype['name'];
            
$this->updateFiltered($table_name, [
                
'folder_id' => null
            
]);
        }

        if (
$is_delete_content) {

            
$this->disableDeleteFilter()->disableApprovedFilter()->
                    
disablePubFilter()->disablePrivacyFilter();

            
$items = $this->getContentItems($ctype['name']);

            if (
$items) {
                foreach (
$items as $item) {
                    
$this->deleteContentItem($ctype['name'], $item['id']);
                }
            }
        }

        return 
$this->delete('content_folders', $folder['id']);
    }

//============================================================================//
//=======================    ПОЛЯ КОНТЕНТА   =================================//
//============================================================================//

    
public function getDefaultContentFieldOptions() {
        return [
            
'is_required'           => 0,
            
'is_digits'             => 0,
            
'is_number'             => 0,
            
'is_alphanumeric'       => 0,
            
'is_email'              => 0,
            
'is_unique'             => 0,
            
'is_url'                => 0,
            
'disable_drafts'        => 0,
            
'is_date_range_process' => 'hide',
            
'label_in_list'         => 'none',
            
'label_in_item'         => 'none',
            
'wrap_type'             => 'auto',
            
'wrap_width'            => '',
            
'profile_value'         => '',
            
'is_in_item_pos'        => ['page']
        ];
    }

//============================================================================//
//============================================================================//

    
public function addContentField($ctype_name, $field, $is_virtual=false){

        
$content_table_name = $this->table_prefix . $ctype_name;
        
$fields_table_name  = $this->table_prefix . $ctype_name . '_fields';

        
$field['ordering'] = $this->getNextOrdering($fields_table_name);

        if (!
$is_virtual){

            
$field_class  = 'field'.string_to_camel('_', $field['type']);
            
$field_parser = new $field_class(null, (isset($field['options']) ? array('options' => $field['options']) : null));

            
$sql = "ALTER TABLE {#}{$content_table_name} ADD `{$field['name']}` {$field_parser->getSQL()}";
            
$this->db->query($sql);

            
$field_parser->hookAfterAdd($content_table_name, $field, $this);

            if(
$field_parser->is_denormalization){

                
$cfield_name = $field['name'].cmsFormField::FIELD_CACHE_POSTFIX;
                
$sql = "ALTER TABLE {#}{$content_table_name} ADD `{$cfield_name}` {$field_parser->getCacheSQL()}";
                
$this->db->query($sql);

            }

            if (!empty(
$field['is_in_filter']) && $field_parser->allow_index){
                
$this->db->addIndex($content_table_name, $field['name']);
            }

        }

        
$field['id'] = $this->insert($fields_table_name, $field);

        
cmsEventsManager::hook('ctype_field_after_add', array($field, $ctype_name, $this));

        
cmsCache::getInstance()->clean("content.fields.{$ctype_name}");

        
// если есть опция полнотекстового поиска
        
if(!$is_virtual && is_array($field['options']) && !empty($field['options']['in_fulltext_search'])){
            
// получаем полнотекстовый индекс для таблицы, он может быть только один
            
$fulltext_index = $this->db->getTableIndexes($content_table_name, 'FULLTEXT');
            if(
$fulltext_index){
                
// название индекса
                
$index_name = key($fulltext_index);
                
// поля индекса
                
$index_fields = $fulltext_index[$index_name];
                
// ищем, нет ли такого поля уже в индексе, мало ли :-)
                
$key = array_search($field['name'], $index_fields);
                
// не нашли, добавляем
                
if($key === false){
                    
// удаляем старый индекс
                    
$this->db->dropIndex($content_table_name, $index_name);
                    
// создаем новый
                    
$this->createFullTextIndex($ctype_name, $field['name']);
                }
            } else {
                
$this->createFullTextIndex($ctype_name, $field['name']);
            }
        }

        return 
$field['id'];

    }

//============================================================================//
//============================================================================//

    
public function getContentFieldsCount($ctype_name){

        
$table_name = $this->table_prefix . $ctype_name . '_fields';

        
$this->useCache('content.fields.'.$ctype_name);

        return 
$this->getCount($table_name);

    }

//============================================================================//
//============================================================================//

    
public function getContentFields($ctype_name, $item_id = false, $enabled = true) {

        
$table_name = $this->table_prefix . $ctype_name . '_fields';

        
$this->useCache('content.fields.' . $ctype_name);

        if (
$enabled) {
            
$this->filterEqual('is_enabled', 1);
        }

        
$this->orderBy('ordering');

        
$fields = $this->get($table_name, function($item, $model) use ($ctype_name, $item_id) {

            
$item['options']     = cmsModel::yamlToArray($item['options']);
            
$item['options']     = array_merge($model->getDefaultContentFieldOptions(), $item['options']);
            
$item['groups_read'] = cmsModel::yamlToArray($item['groups_read']);
            
$item['groups_add']  = cmsModel::yamlToArray($item['groups_add']);
            
$item['groups_edit'] = cmsModel::yamlToArray($item['groups_edit']);
            
$item['filter_view'] = cmsModel::yamlToArray($item['filter_view']);
            
$item['default']     = $item['values'];

            
$rules = [];
            if (
$item['options']['is_required']) { $rules[] = ['required']; }
            if (
$item['options']['is_digits']) { $rules[] = ['digits']; }
            if (
$item['options']['is_number']) { $rules[] = ['number']; }
            if (
$item['options']['is_alphanumeric']) { $rules[] = ['alphanumeric']; }
            if (
$item['options']['is_email']) { $rules[] = ['email']; }
            if (!empty(
$item['options']['is_url'])) { $rules[] = ['url']; }

            if (
$item['options']['is_unique']) {
                if (!
$item_id) {
                    
$rules[] = ['unique', $model->table_prefix . $ctype_name, $item['name']];
                } else {
                    
$rules[] = ['unique_exclude', $model->table_prefix . $ctype_name, $item['name'], $item_id];
                }
            }

            
$item['rules'] = $rules;

            return 
$item;
        }, 
'name');

        
// чтобы сработала мультиязычность, если необходима
        // поэтому перебираем тут, а не выше
        
if ($fields) {
            foreach (
$fields as $name => $field) {

                
$field_class = 'field' . string_to_camel('_', $field['type']);

                
$field['handler'] = new $field_class($field['name']);

                
$field['handler_title'] = $field['handler']->getTitle();

                
$field_property = $field; unset($field_property['type']);

                
$field['handler']->setOptions($field_property);

                
$fields[$name] = $field;
            }
        }

        return 
$fields;
    }

    public function 
getRequiredContentFields($ctype_name){

        
$fields = $this->getContentFields($ctype_name);

        
$req_fields = array();

        foreach(
$fields as $field){
            if (
$field['options']['is_required']) {
                
$req_fields[] = $field;
            }
        }

        return 
$req_fields;

    }

//============================================================================//
//============================================================================//

    
public function getContentField($ctype_name, $id, $by_field = 'id'){

        
$table_name = $this->table_prefix . $ctype_name . '_fields';

        
$this->useCache('content.fields.'.$ctype_name);

        return 
$this->getItemByField($table_name, $by_field, $id, function($item, $model){

            
$item['options'] = cmsModel::yamlToArray($item['options']);

            if (!
$item['is_system']){
                
$item['options'] = array_merge($model->getDefaultContentFieldOptions(), $item['options']);
            }

            
$item['groups_read'] = cmsModel::yamlToArray($item['groups_read']);
            
$item['groups_add']  = cmsModel::yamlToArray($item['groups_add']);
            
$item['groups_edit'] = cmsModel::yamlToArray($item['groups_edit']);
            
$item['filter_view'] = cmsModel::yamlToArray($item['filter_view']);

            
$field_class = 'field' . string_to_camel('_', $item['type']);

            
$handler = new $field_class($item['name']);

            
$item['parser_title'] = $handler->getTitle();

            
$handler->setOptions($item);

            
$item['parser'] = $handler;

            return 
$item;

        });

    }

    public function 
getContentFieldByName($ctype_name, $name){

        return 
$this->getContentField($ctype_name, $name, 'name');

    }

    public function 
isContentFieldExists($ctype_name, $name){
        return 
$this->getContentField($ctype_name, $name, 'name') !== false;
    }

//============================================================================//
//============================================================================//

    
public function reorderContentFields($ctype_name, $fields_ids_list){

        
$table_name = $this->table_prefix . $ctype_name . '_fields';

        
$this->reorderByList($table_name, $fields_ids_list);

        
cmsCache::getInstance()->clean("content.fields.{$ctype_name}");

        return 
true;

    }

//============================================================================//
//============================================================================//

    
public function updateContentField($ctype_name, $id, $field){

        
$content_table_name = $this->table_prefix . $ctype_name;
        
$fields_table_name = $this->table_prefix . $ctype_name . '_fields';

        
$field_old = $this->getContentField($ctype_name, $id);

        if (!
$field_old['is_system']){

            
$new_lenght = ((isset($field['options']) && !empty($field['options']['max_length'])) ? $field['options']['max_length'] : false);
            
$old_lenght = ((isset($field_old['options']) && !empty($field_old['options']['max_length'])) ? $field_old['options']['max_length'] : false);

            
$field_class   = 'field'.string_to_camel('_', $field['type']);
            
$field_handler = new $field_class(null, (isset($field['options']) ? array('options' => $field['options']) : null));

            
$field_handler->hookAfterUpdate($content_table_name, $field, $field_old, $this);

            if ((
$field_old['name'] != $field['name']) || ($field_old['type'] != $field['type']) || ($new_lenght != $old_lenght)){

                if(
$field_old['type'] != $field['type']){ $this->db->dropIndex($content_table_name, $field_old['name']); }

                
$sql = "ALTER TABLE `{#}{$content_table_name}` CHANGE `{$field_old['name']}` `{$field['name']}` {$field_handler->getSQL()}";
                
$this->db->query($sql);

                if((
$field_old['name'] != $field['name']) || ($field_old['type'] != $field['type'])){

                    
// поля денормализации
                    
$old_cfield_name = $field_old['name'].cmsFormField::FIELD_CACHE_POSTFIX;
                    
$new_cfield_name = $field['name'].cmsFormField::FIELD_CACHE_POSTFIX;

                    
$update_cache_sql = "ALTER TABLE `{#}{$content_table_name}` CHANGE `{$old_cfield_name}` `{$new_cfield_name}` {$field_handler->getCacheSQL()}";

                    
// изменилось только имя поля
                    
if($field_handler->is_denormalization && $field_old['type'] == $field['type']){

                        
$this->db->query($update_cache_sql);

                    }
                    
// изменился тип
                    
if($field_old['type'] != $field['type']){

                        if(
$field_old['parser']->is_denormalization && $field_handler->is_denormalization){

                            
$this->db->query($update_cache_sql);

                        } elseif(
$field_old['parser']->is_denormalization && !$field_handler->is_denormalization){

                            
$this->db->dropTableField($content_table_name, $old_cfield_name);

                        } elseif(!
$field_old['parser']->is_denormalization && $field_handler->is_denormalization){

                            
$sql = "ALTER TABLE {#}{$content_table_name} ADD `{$new_cfield_name}` {$field_handler->getCacheSQL()}";
                            
$this->db->query($sql);

                        }

                    }

                    
// удаляем старый индекс
                    
$this->db->dropIndex($content_table_name, $field_old['name']);

                    
// добавляем новый
                    
if ($field['is_in_filter'] && $field_handler->allow_index){
                        
$this->db->addIndex($content_table_name, $field['name']);
                    }

                }

            }

            if (
$field['is_in_filter'] && $field_handler->allow_index && !$field_old['is_in_filter']){
                
$this->db->addIndex($content_table_name, $field['name']);
            }

            if (!
$field['is_in_filter'] && $field_handler->allow_index && $field_old['is_in_filter']){
                
$this->db->dropIndex($content_table_name, $field_old['name']);
            }

            
// если есть опция полнотекстового поиска и ее значение изменилось
            
if(is_array($field['options']) && array_key_exists('in_fulltext_search', $field['options'])){
                if(
$field['options']['in_fulltext_search'] != @$field_old['options']['in_fulltext_search']){
                    
// получаем полнотекстовый индекс для таблицы, он может быть только один
                    
$fulltext_index = $this->db->getTableIndexes($content_table_name, 'FULLTEXT');
                    if(
$fulltext_index){
                        
// название индекса
                        
$index_name = key($fulltext_index);
                        
// поля индекса
                        
$index_fields = $fulltext_index[$index_name];
                        
// выключили опцию
                        
if(!$field['options']['in_fulltext_search']){
                            
$key = array_search($field['name'], $index_fields);
                            
// нашли - удаляем из массива
                            
if($key !== false){
                                unset(
$index_fields[$key]);
                                
// удаляем индекс
                                
$this->db->dropIndex($content_table_name, $index_name);
                                
// и создаем новый
                                
if($index_fields){
                                    
$this->db->addIndex($content_table_name, $index_fields, '', 'FULLTEXT');
                                }
                            }
                        }
                        
// включили опцию
                        
if($field['options']['in_fulltext_search']){
                            
// ищем, нет ли такого поля уже в индексе, мало ли :-)
                            
$key = array_search($field['name'], $index_fields);
                            
// не нашли, добавляем
                            
if($key === false){
                                
// удаляем старый индекс
                                
$this->db->dropIndex($content_table_name, $index_name);
                                
// создаем новый
                                
$this->createFullTextIndex($ctype_name, $field['name']);
                            }
                        }

                    } elseif(
$field['options']['in_fulltext_search']) {
                        
$this->createFullTextIndex($ctype_name, $field['name']);
                    }
                }
            }

        }

        
$result = $this->update($fields_table_name, $id, $field);

        if (
$result){
            
$field['id'] = $id;
            
cmsEventsManager::hook('ctype_field_after_update', array($field, $ctype_name, $this));
            
cmsEventsManager::hook('ctype_field_'.str_replace(['{','}'], '', $ctype_name).'_after_update', array($field, $this));
        }

        
cmsCache::getInstance()->clean('content.fields.'.$ctype_name);
        
cmsCache::getInstance()->clean('content.list.'.$ctype_name);
        
cmsCache::getInstance()->clean('content.item.'.$ctype_name);

        return 
$result;

    }

    
/**
     * Создает fulltext индекс согласно настроек полей типа контента
     * @param string $ctype_name Название типа контента
     * @param string|null $add_field Название поля, для которого принудительно нужно создать индекс
     * @return boolean
     */
    
public function createFullTextIndex($ctype_name, $add_field=null) {

        
// важен порядок индексов, поэтому создаем их так, как они будут в запросе
        // для этого получаем все поля этого типа контента
        
$fields = $this->getContentFields($ctype_name);

        foreach (
$fields as $field) {

            
$is_text = $field['handler']->getOption('in_fulltext_search') || $field['name'] == $add_field;
            if(!
$is_text){ continue; }

            
$index_fields[] = $field['name'];

        }

        if(
$index_fields){
            
$this->db->addIndex($this->table_prefix . $ctype_name, $index_fields, '', 'FULLTEXT'); return true;
        }

        return 
false;

    }
//============================================================================//
//============================================================================//

    
public function toggleContentFieldVisibility($ctype_name, $id, $mode, $is_visible){

        
$fields_table_name = $this->table_prefix . $ctype_name . '_fields';

        
$result = $this->update($fields_table_name, $id, array(
            
$mode => $is_visible
        
));

        
cmsCache::getInstance()->clean("content.fields.{$ctype_name}");

        return 
$result;

    }

//============================================================================//
//============================================================================//

    
public function deleteContentField($ctype_name_or_id, $id, $by_field='id', $isForced = false){

        if (
is_numeric($ctype_name_or_id)){
            
$ctype = $this->getContentType($ctype_name_or_id);
            
$ctype_name = $ctype['name'];
        } else {
            
$ctype_name = $ctype_name_or_id;
        }

        
$field = $this->getContentField($ctype_name, $id, $by_field);
        if (
$field['is_fixed'] && !$isForced) { return false; }

        
cmsEventsManager::hook('ctype_field_before_delete', array($field, $ctype_name, $this));

        
$content_table_name = $this->table_prefix . $ctype_name;
        
$fields_table_name = $this->table_prefix . $ctype_name . '_fields';

        
$this->delete($fields_table_name, $id, $by_field);
        
$this->reorder($fields_table_name);

        
cmsCache::getInstance()->clean("content.fields.{$ctype_name}");

        
$this->db->dropTableField($content_table_name, $field['name']);

        
$field['parser']->hookAfterRemove($content_table_name, $field, $this);

        if(
$field['parser']->is_denormalization){
            
$this->db->dropTableField($content_table_name, $field['parser']->getDenormalName());
        }

        return 
true;

    }

//============================================================================//
//============================================================================//

    
public function getContentFieldsets($ctype_id){

        if (
is_numeric($ctype_id)){
            
$ctype = $this->getContentType($ctype_id);
            
$ctype_name = $ctype['name'];
        } else {
            
$ctype_name = $ctype_id;
        }

        
$table_name = $this->table_prefix . $ctype_name . '_fields';

        
$this->useCache('content.fields.'.$ctype_name);

        
$this->groupBy('fieldset');

        if (!
$this->order_by){ $this->orderBy('fieldset'); }

        
$fieldsets = $this->get($table_name, function($item, $model){
            
$item = $item['fieldset'];
            return 
$item;
        }, 
false);

        if (
$fieldsets[0] == '') { unset($fieldsets[0]); }

        return 
$fieldsets;

    }

//============================================================================//
//============================    СВОЙСТВА   =================================//
//============================================================================//

    
public function isContentPropsExists($ctype_name){

        
$props_table_name = $this->table_prefix . $ctype_name . '_props';

        return (bool)
$this->getCount($props_table_name);

    }

    public function 
getContentPropsBinds($ctype_name, $category_id = false) {

        
$props_table_name = $this->table_prefix . $ctype_name . '_props';
        
$bind_table_name = $this->table_prefix . $ctype_name . '_props_bind';

        
$this->selectOnly('p.*');
        
$this->select('p.id', 'prop_id');
        
$this->select('i.id', 'id');
        
$this->select('i.cat_id', 'cat_id');

        
$this->join($props_table_name, 'p', 'p.id = i.prop_id');

        if (
$category_id){
            
$this->filterEqual('i.cat_id', $category_id);
        }

        
$this->orderBy('i.ordering');
        
$this->groupBy('p.id');

        return 
$this->get($bind_table_name);

    }

    public function 
getContentProps($ctype_name, $category_id = false) {

        
$props_table_name = $this->table_prefix . $ctype_name . '_props';
        
$bind_table_name = $this->table_prefix . $ctype_name . '_props_bind';

        if (
$category_id){
            
$this->selectOnly('p.*');
            
$this->select('c.title', 'cat_title');
            
$this->select('i.cat_id');
            
$this->join($props_table_name, 'p', 'p.id = i.prop_id');
            
$this->join($this->table_prefix . $ctype_name . '_cats', 'c', 'c.id = i.cat_id');
            if(
is_array($category_id)){
                
$this->filterIn('cat_id', $category_id);
            } else {
                
$this->filterEqual('cat_id', $category_id);
            }
            
$this->orderBy('ordering');
            
$table_name = $bind_table_name;
        } else {
            
$table_name = $props_table_name;
        }

        return 
$this->get($table_name, function($item, $model){
            
$item['options'] = cmsModel::yamlToArray($item['options']);
            return 
$item;
        });

    }

    public function 
getContentProp($ctype_name, $id){

        
$props_table_name = $this->table_prefix . $ctype_name . '_props';
        
$bind_table_name = $this->table_prefix . $ctype_name . '_props_bind';

        
$prop = $this->getItemById($props_table_name, $id, function($item, $model){
            
$item['options'] = cmsModel::yamlToArray($item['options']);
            return 
$item;
        });

        
$this->filterEqual('prop_id', $id);

        
$prop['cats'] = $this->get($bind_table_name, function($item, $model){
           return (int)
$item['cat_id'];
        });

        return 
$prop;

    }

    public function 
addContentProp($ctype_name, $prop){

        
$table_name = $this->table_prefix . $ctype_name . '_props';

        
$cats_list = $prop['cats']; unset($prop['cats']);

        
$prop['id'] = $this->insert($table_name, $prop);

        
$this->bindContentProp($ctype_name, $prop['id'], $cats_list);

        
cmsEventsManager::hook('ctype_prop_after_add', array($prop, $ctype_name, $this));

        return 
$prop['id'];

    }

    public function 
updateContentProp($ctype_name, $id, $prop){

        
$table_name = $this->table_prefix . $ctype_name . '_props';

        
$old_prop = $this->getContentProp($ctype_name, $id);

        
$missed_cats_list = array_diff($old_prop['cats'], $prop['cats']);
        
$added_cats_list = array_diff($prop['cats'], $old_prop['cats']);

        if (
$missed_cats_list) {
            foreach(
$missed_cats_list as $cat_id){
                
$this->unbindContentProp($ctype_name, $id, $cat_id);
            }
        }

        if (
$added_cats_list) {
            
$this->bindContentProp($ctype_name, $id, $added_cats_list);
        }

        unset(
$prop['cats']);

        
$result = $this->update($table_name, $id, $prop);

        
$prop['id'] = $id;
        
cmsEventsManager::hook('ctype_prop_after_update', array($prop, $ctype_name, $this));

        return 
$result;

    }

    public function 
toggleContentPropFilter($ctype_name, $id, $is_in_filter){

        
$table_name = $this->table_prefix . $ctype_name . '_props';

        return 
$this->update($table_name, $id, array(
            
'is_in_filter' => $is_in_filter
        
));

    }


    public function 
deleteContentProp($ctype_name_or_id, $prop_id){

        if (
is_numeric($ctype_name_or_id)){
            
$ctype = $this->getContentType($ctype_name_or_id);
            
$ctype_name = $ctype['name'];
        } else {
            
$ctype_name = $ctype_name_or_id;
        }

        
$table_name = $this->table_prefix . $ctype_name . '_props';

        
$prop = $this->getContentProp($ctype_name, $prop_id);

        
cmsEventsManager::hook('ctype_prop_before_delete', array($prop, $ctype_name, $this));

        foreach(
$prop['cats'] as $cat_id){
            
$this->unbindContentProp($ctype_name, $prop_id, $cat_id);
        }

        
$this->deleteContentPropValues($ctype_name, $prop_id);

        return 
$this->delete($table_name, $prop_id);

    }

    public function 
bindContentProp($ctype_name, $prop_id, $cats_list){

        
$table_name = $this->table_prefix . $ctype_name . '_props_bind';

        foreach(
$cats_list as $cat_id){

            
$this->filterEqual('cat_id', $cat_id);

            
$ordering = $this->getNextOrdering($table_name);

            
$this->insert($table_name, array(
                
'prop_id' => $prop_id,
                
'cat_id' => $cat_id,
                
'ordering' => $ordering
            
));

        }

        return 
true;

    }

    public function 
unbindContentProp($ctype_name, $prop_id, $cat_id){

        
$table_name = $this->table_prefix . $ctype_name . '_props_bind';

        
$this->
            
filterEqual('prop_id', $prop_id)->
            
filterEqual('cat_id', $cat_id)->
            
deleteFiltered($table_name);

        
$this->
            
filterEqual('cat_id', $cat_id)->
            
reorder($table_name);

        return 
true;

    }

    public function 
unbindContentProps($ctype_name, $cat_id){

        
$table_name = $this->table_prefix . $ctype_name . '_props_bind';

        
$this->
            
filterEqual('cat_id', $cat_id)->
            
deleteFiltered($table_name);

        return 
true;

    }

    public function 
deleteContentPropValues($ctype_name, $prop_id){

        
$table_name = $this->table_prefix . $ctype_name . '_props_values';

        
$this->filterEqual('prop_id', $prop_id)->deleteFiltered($table_name);

        return 
true;

    }

    public function 
reorderContentProps($ctype_name, $props_ids_list){

        
$table_name = $this->table_prefix . $ctype_name . '_props_bind';

        
$this->reorderByList($table_name, $props_ids_list);

        return 
true;

    }

    public function 
getContentPropsFieldsets($ctype_id){

        if (
is_numeric($ctype_id)){
            
$ctype = $this->getContentType($ctype_id);
            
$ctype_name = $ctype['name'];
        } else {
            
$ctype_name = $ctype_id;
        }

        
$table_name = $this->table_prefix . $ctype_name . '_props';

        
$this->groupBy('fieldset');
        
$this->orderBy('fieldset');

        
$fieldsets = $this->get($table_name, function($item, $model){
            
$item = $item['fieldset'];
            return 
$item;
        }, 
false);

        if (
is_array($fieldsets) && $fieldsets[0] == '') { unset($fieldsets[0]); }

        return 
$fieldsets;

    }

    public function 
getPropsValues($ctype_name, $item_id){

        
$table_name = $this->table_prefix . $ctype_name . '_props_values';

        
$this->filterEqual('item_id', $item_id);

        return 
$this->get($table_name, function($item, $model){
            return 
$item['value'];
        }, 
'prop_id');

    }

    public function 
addPropsValues($ctype_name, $item_id, $props_values){

        
$table_name = $this->table_prefix . $ctype_name . '_props_values';

        foreach(
$props_values as $prop_id=>$value){

            
$this->insert($table_name, array(
                
'prop_id' => $prop_id,
                
'item_id' => $item_id,
                
'value' => $value
            
));

        }

    }

    public function 
updatePropsValues($ctype_name, $item_id, $props_values){

        
$table_name = $this->table_prefix . $ctype_name . '_props_values';

        
$props_ids = array_keys($props_values);

        
$this->
            
filterEqual('item_id', $item_id)->
            
filterIn('prop_id', $props_ids)->
            
deleteFiltered($table_name);

        
$this->addPropsValues($ctype_name, $item_id, $props_values);

    }

    public function 
deletePropsValues($ctype_name, $item_id){

        
$table_name = $this->table_prefix . $ctype_name . '_props_values';

        
$this->
            
filterEqual('item_id', $item_id)->
            
deleteFiltered($table_name);

    }

//============================================================================//
//===============================   СВЯЗИ   ==================================//
//============================================================================//

    
public function getContentRelations($ctype_id=false){

        if (
$ctype_id) { $this->filterEqual('ctype_id', $ctype_id); }

        
$this->useCache('content.relations');

        
$relations = $this->get('content_relations', function($item){
            
$item['options'] = cmsModel::yamlToArray($item['options']);
            return 
$item;
        });

        return 
$relations;

    }

    public function 
getContentRelation($id, $by_field = 'id'){

        return 
$this->getItemByField('content_relations', $by_field, $id, function($item){
            
$item['options'] = cmsModel::yamlToArray($item['options']);
            return 
$item;
        });

    }

    public function 
getContentRelationByTypes($ctype_id, $child_ctype_id, $target_controller = 'content'){

        
$this->filterEqual('ctype_id', $ctype_id);
        
$this->filterEqual('child_ctype_id', $child_ctype_id);
        
$this->filterEqual('target_controller', $target_controller);

        return 
$this->getItem('content_relations', function($item){
            
$item['options'] = cmsModel::yamlToArray($item['options']);
            return 
$item;
        });

    }

    public function 
addContentRelation($relation){

        
cmsCache::getInstance()->clean('content.relations');

        
$relation['ordering'] = $this->getNextOrdering('content_relations');

        return 
$this->insert('content_relations', $relation);

    }

    public function 
updateContentRelation($id, $relation){

        
cmsCache::getInstance()->clean('content.relations');

        return 
$this->update('content_relations', $id, $relation);

    }

    public function 
deleteContentRelation($id){

        
cmsCache::getInstance()->clean('content.relations');

        return 
$this->delete('content_relations', $id);

    }

    public function 
getContentTypeChilds($ctype_id){

        
$this->useCache('content.relations');

        
$this->selectOnly('i.*');
        
$this->select('c.title', 'child_title');
        
$this->select('c.labels', 'child_labels');
        
$this->select('c.name', 'child_ctype_name');

        
$this->joinLeft('content_types', 'c', 'c.id = i.child_ctype_id');

        
$this->filterEqual('ctype_id', $ctype_id);

        
$this->orderBy('ordering', 'asc');

        return 
$this->get('content_relations', function($item, $model){

            
$item['child_labels'] = cmsModel::yamlToArray($item['child_labels']);
            
$item['options'] = cmsModel::yamlToArray($item['options']);
            if(empty(
$item['child_ctype_name'])){
                
$item['child_ctype_name'] = $item['target_controller'];
            }

            return 
$item;

        });

    }

    public function 
getContentTypeParents($ctype_id, $target_controller = 'content'){

        
$this->selectOnly('i.*');
        
$this->select('c.name', 'ctype_name');
        
$this->select('c.title', 'ctype_title');
        
$this->select('c.id', 'ctype_id');

        
$this->joinLeft('content_types', 'c', 'c.id = i.ctype_id');

        
$this->filterEqual('child_ctype_id', $ctype_id);
        
$this->filterEqual('target_controller', $target_controller);

        
$this->orderBy('ordering', 'asc');

        
$parents = $this->get('content_relations');
        if (!
$parents) { return array(); }

        foreach (
$parents as $id => $parent){
            
$parents[$id]['id_param_name'] = 'parent_'.$parent['ctype_name'].'_id';
        }

        return 
$parents;

    }

    public function 
updateChildItemParentIds($relation){

        
$this->selectOnly('parent_item_id', 'id');

        
$this->filterEqual('parent_ctype_id', $relation['parent_ctype_id']);
        
$this->filterEqual('child_ctype_id', $relation['child_ctype_id']);
        
$this->filterEqual('child_item_id', $relation['child_item_id']);
        
$this->filterEqual('target_controller', (isset($relation['target_controller']) ? $relation['target_controller'] : 'content'));

        
$parent_items_ids = $this->get('content_relations_bind', function($item, $model){
            return 
$item['id'];
        }, 
false);

        if (
$parent_items_ids){

            
$ids = trim(implode(',', $parent_items_ids));

            if (
$ids){

                
$item_table = $this->table_prefix . $relation['child_ctype_name'];

                if(
$item_table == 'users'){ $item_table = '{users}'; }

                
$this->update($item_table, $relation['child_item_id'], array(
                   
'parent_'.$relation['parent_ctype_name'].'_id' => $ids
                
));

                
cmsCache::getInstance()->clean('content.list.'.$relation['child_ctype_name']);
                
cmsCache::getInstance()->clean('content.item.'.$relation['child_ctype_name']);

            }

        }

    }

    public function 
bindContentItemRelation($relation){

        
$id = $this->insert('content_relations_bind', $relation);

        
$this->updateChildItemParentIds($relation);

        return 
$id;

    }

    public function 
unbindContentItemRelation($relation){

        
$this->filterEqual('parent_ctype_id', $relation['parent_ctype_id']);
        
$this->filterEqual('parent_item_id', $relation['parent_item_id']);
        
$this->filterEqual('child_ctype_id', $relation['child_ctype_id']);
        
$this->filterEqual('child_item_id', $relation['child_item_id']);
        
$this->filterEqual('target_controller', (isset($relation['target_controller']) ? $relation['target_controller'] : 'content'));

        
$this->deleteFiltered('content_relations_bind');

        
$this->updateChildItemParentIds($relation);

        return;

    }

    public function 
deleteContentItemRelations($ctype_id, $item_id){

        
$this->filterEqual('child_ctype_id', $ctype_id);
        
$this->filterEqual('child_item_id', $item_id);

        
$this->deleteFiltered('content_relations_bind');

    }

    public function 
getContentItemParents($parent_ctype, $child_ctype, $item_id){

        if(empty(
$child_ctype['controller'])){
            
$child_ctype['controller'] = 'content';
        }

        
$this->selectOnly('i.*');

        
$parent_ctype_table = $this->table_prefix . $parent_ctype['name'];

        if(
$parent_ctype_table == 'users'){
            
$parent_ctype_table = '{users}';
        }

        
$join_on =  "r.parent_ctype_id = '{$parent_ctype['id']}' AND " .
                    
'r.child_ctype_id '.(!empty($child_ctype['id']) ? '='.$child_ctype['id'] : 'IS NULL' ).' AND ' .
                    
"r.child_item_id = '{$item_id}' AND " .
                    
"r.parent_item_id = i.id AND r.target_controller = '{$child_ctype['controller']}'";

        
$this->joinInner('content_relations_bind', 'r', $join_on);

        return 
$this->get($parent_ctype_table);


    }

    public function 
reorderContentRelation($fields_ids_list){

        
$this->reorderByList('content_relations', $fields_ids_list);

        
cmsCache::getInstance()->clean('content.relations');

        return 
true;

    }

//============================================================================//
//=============================   Фильтры   ==================================//
//============================================================================//

    
public function getContentFilters($ctype_name){

        
$this->useCache('content.filters.'.$ctype_name);

        
$table_name = $this->getContentTypeTableName($ctype_name).'_filters';

        return 
$this->get($table_name, function($item, $model){

            
$item['filters'] = cmsModel::stringToArray($item['filters']);

            return 
$item;
        });

    }

    public function 
addContentFilter($filter, $ctype){

        
$table_name = $this->getContentTypeTableName($ctype['name']).'_filters';

        
$filter['filters'] = array_filter_recursive($filter['filters']);
        
array_multisort($filter['filters']);
        
$filter['hash'] = md5(json_encode($filter['filters']));

        
$filter['id'] = $this->insert($table_name, $filter, true);

        
cmsEventsManager::hook('ctype_filter_add', array($filter, $ctype, $this));
        
cmsEventsManager::hook('ctype_filter_'.$ctype['name'].'_add', array($filter, $ctype, $this));

        
cmsCache::getInstance()->clean('content.filters.'.$ctype['name']);

        return 
$filter['id'];

    }

    public function 
updateContentFilter($filter, $ctype){

        list(
$filter, $ctype) = cmsEventsManager::hook('ctype_filter_update', array($filter, $ctype));
        list(
$filter, $ctype) = cmsEventsManager::hook('ctype_filter_'.$ctype['name'].'_update', array($filter, $ctype));

        
$table_name = $this->getContentTypeTableName($ctype['name']).'_filters';

        
$filter['filters'] = array_filter_recursive($filter['filters']);
        
array_multisort($filter['filters']);
        
$filter['hash'] = md5(json_encode($filter['filters']));

        
$this->update($table_name, $filter['id'], $filter, false, true);

        
cmsCache::getInstance()->clean('content.filters.'.$ctype['name']);

        return 
true;

    }

    public function 
getContentFilter($ctype, $id, $by_hash = false){

        if(!
$this->isFiltersTableExists($ctype['name'])){
            return 
false;
        }

        
$table_name = $this->getContentTypeTableName($ctype['name']).'_filters';

        
$this->useCache('content.filters.'.$ctype['name']);

        
$field_name = 'id';
        if(!
is_numeric($id)){
            if(
$by_hash){
                
$field_name = 'hash';
            } else {
                
$field_name = 'slug';
            }
        }

        
$this->filterEqual($field_name, $id);

        return 
$this->getItem($table_name, function($item, $model) use($ctype){

            
$item['filters'] = cmsModel::stringToArray($item['filters']);
            
$item['ctype_name'] = $ctype['name'];

            return 
$item;

        });

    }

    public function 
deleteContentFilter($ctype, $id){

        
$table_name = $this->getContentTypeTableName($ctype['name']).'_filters';

        
$this->delete($table_name, $id);

        
cmsCache::getInstance()->clean('content.filters.'.$ctype['name']);

        return 
true;

    }

//============================================================================//
//==============================   НАБОРЫ   ==================================//
//============================================================================//

    
public function getContentDatasets($ctype_id=false, $only_visible=false, $item_callback=false){

        if (
$ctype_id) {
            if(
is_numeric($ctype_id)){
                
$this->filterEqual('ctype_id', $ctype_id);
            } else {
                
$this->filterEqual('target_controller', $ctype_id);
            }
        }

        if (
$only_visible) { $this->filterEqual('is_visible', 1); }

        
$this->orderBy('ordering');

        
$this->useCache('content.datasets');

        
$datasets = $this->get('content_datasets', function($item, $model) use($item_callback){

            
$item['groups_view'] = cmsModel::yamlToArray($item['groups_view']);
            
$item['groups_hide'] = cmsModel::yamlToArray($item['groups_hide']);
            
$item['cats_view']   = cmsModel::yamlToArray($item['cats_view']);
            
$item['cats_hide']   = cmsModel::yamlToArray($item['cats_hide']);
            
$item['filters']     = $item['filters'] ? cmsModel::yamlToArray($item['filters']) : array();
            
$item['sorting']     = $item['sorting'] ? cmsModel::yamlToArray($item['sorting']) : array();
            
$item['list']        = cmsModel::stringToArray($item['list']);

            if (
is_callable($item_callback)){
                
$item = call_user_func_array($item_callback, array($item, $model));
                if (
$item === false){ return false; }
            }

            return 
$item;

        }, 
'name');

        if (
$only_visible && $datasets){
            
$user = cmsUser::getInstance();
            foreach(
$datasets as $id=>$dataset){
                
$is_user_view = $user->isInGroups($dataset['groups_view']);
                
$is_user_hide = !empty($dataset['groups_hide']) && $user->isInGroups($dataset['groups_hide']) && !$user->is_admin;
                if (!
$is_user_view || $is_user_hide) { unset($datasets[$id]); }
            }
        }

        return 
$datasets;

    }

    public function 
getContentDataset($id){

        return 
cmsEventsManager::hook('ctype_dataset_get', $this->getItemById('content_datasets', $id, function($item, $model){

            
$item['groups_view'] = cmsModel::yamlToArray($item['groups_view']);
            
$item['groups_hide'] = cmsModel::yamlToArray($item['groups_hide']);
            
$item['cats_view']   = cmsModel::yamlToArray($item['cats_view']);
            
$item['cats_hide']   = cmsModel::yamlToArray($item['cats_hide']);
            
$item['filters']     = $item['filters'] ? cmsModel::yamlToArray($item['filters']) : array();
            
$item['sorting']     = $item['sorting'] ? cmsModel::yamlToArray($item['sorting']) : array();
            
$item['list']        = cmsModel::stringToArray($item['list']);

            return 
$item;

        }));

    }

    public function 
deleteContentDatasetIndex($ctype_name, $index_name) {

        
// если используется в других датасетах, не удаляем
        
if($this->getItemByField('content_datasets', 'index', $index_name)){
            return 
false;
        }

        return 
$this->db->dropIndex($this->table_prefix.$ctype_name, $index_name);

    }

    public function 
addContentDatasetIndex($dataset, $ctype_name) {

        
$content_table_name = $this->table_prefix.$ctype_name;
        
$index_name         = 'dataset_'.$dataset['name'];

        
// поля для индекса
        
$filters_fields = $sorting_fields = $fields = array();

        
// создаем индекс
        // параметры выборки
        
if($dataset['filters']){
            foreach (
$dataset['filters'] as $filters) {
                if(
$filters && !in_array($filters['condition'], array('gt','lt','ge','le','nn','ni'))){
                    
$filters_fields[] = $filters['field'];
                }
            }
            
$filters_fields = array_unique($filters_fields);
        }
        
// добавим условия, которые в каждой выборке
        // только для записей типов контента
        
if($this->table_prefix){
            
$filters_fields[] = 'is_pub';
            
$filters_fields[] = 'is_parent_hidden';
            
$filters_fields[] = 'is_deleted';
            
$filters_fields[] = 'is_approved';
        }
        
// сортировка
        
if($dataset['sorting']){
            foreach (
$dataset['sorting'] as $sorting) {
                if(
$sorting){
                    
$sorting_fields[] = $sorting['by'];
                }
            }
            
$sorting_fields = array_unique($sorting_fields);
        }

        
// если поле присутствует и в выборке и в сортировке, оставляем только в сортировке
        
if($filters_fields){
            foreach (
$filters_fields as $key => $field) {
                if(
in_array($field, $sorting_fields)){
                    unset(
$filters_fields[$key]);
                }
            }
        }

        
$fields = array_merge($filters_fields, $sorting_fields);

        if(!
$fields){ return null; }

        if(
$fields == array('date_pub')){
            
$index_name = 'date_pub';
        } elseif(
$fields == array('user_id','date_pub') || $fields == array('user_id')){
            
$index_name = 'user_id';
        } else {

            
// ищем индекс с таким же набором полей
            
$is_found = false;
            
$indexes = $this->db->getTableIndexes($content_table_name);
            foreach (
$indexes as $_index_name => $_index_fields) {
                if(
$fields == $_index_fields){
                    
$is_found = $_index_name; break;
                }
            }

            
// нашли - используем его
            
if($is_found){
                
$index_name = $is_found;
            } else {

                
// если нет, то создаем новый
                
$this->db->addIndex($content_table_name, $fields, $index_name);

            }

        }

        return 
$index_name;

    }

    public function 
addContentDataset($dataset, $ctype){

        
$table_name = 'content_datasets';

        
$dataset['ctype_id'] = $ctype['id'];

        
$this->filterEqual('ctype_id', $dataset['ctype_id']);

        
$dataset['ordering'] = $this->getNextOrdering($table_name);

        
$dataset['index'] = $this->addContentDatasetIndex($dataset, $ctype['name']);

        
$dataset['list'] = cmsModel::arrayToString($dataset['list']);

        
$dataset['id'] = $this->insert($table_name, $dataset);

        
cmsEventsManager::hook('ctype_dataset_add', array($dataset, $ctype, $this));

        
cmsCache::getInstance()->clean('content.datasets');

        return 
$dataset['id'];

    }

    public function 
updateContentDataset($id, $dataset, $ctype, $old_dataset){

        
$dataset['ctype_id'] = $ctype['id'];

        
$dataset['list'] = cmsModel::arrayToString($dataset['list']);

        
$success = $this->update('content_datasets', $id, $dataset);

        
$dataset['id'] = $id;
        
cmsEventsManager::hook('ctype_dataset_update', array($dataset, $ctype, $this));

        
cmsCache::getInstance()->clean('content.datasets');

        if((
$old_dataset['sorting'] != $dataset['sorting']) || ($old_dataset['filters'] != $dataset['filters'])){

            
$this->deleteContentDatasetIndex($ctype['name'], $old_dataset['index']);

            
$index = $this->addContentDatasetIndex($dataset, $ctype['name']);

            
$this->update('content_datasets', $id, array('index'=>$index));

            
cmsCache::getInstance()->clean('content.datasets');

        }

        return 
$success;

    }

    public function 
toggleContentDatasetVisibility($id, $is_visible){

        return 
$this->update('content_datasets', $id, array(
            
'is_visible' => $is_visible
        
));

    }

    public function 
reorderContentDatasets($fields_ids_list){

        
$this->reorderByList('content_datasets', $fields_ids_list);

        
cmsCache::getInstance()->clean('content.datasets');

        return 
true;

    }

    public function 
deleteContentDataset($id){

        
$dataset = $this->getContentDataset($id);
        if(!
$dataset){ return false; }

        if(
$dataset['ctype_id']){
            
$ctype = $this->getContentType($dataset['ctype_id']);
            if (!
$ctype) { return false; }
        } else {
            
$ctype = array(
                
'title' => string_lang($dataset['target_controller'].'_controller'),
                
'name'  => $dataset['target_controller'],
                
'id'    => null
            
);
            
$this->setTablePrefix('');
        }

        
cmsEventsManager::hook('ctype_dataset_before_delete', array($dataset, $ctype, $this));

        
$this->delete('content_datasets', $id);

        
$this->deleteContentDatasetIndex($ctype['name'], $dataset['index']);

        
cmsCache::getInstance()->clean('content.datasets');

        return 
true;

    }

//============================================================================//
//=============================   КОНТЕНТ   ==================================//
//============================================================================//

    
public function resetFilters(){
        
parent::resetFilters();
        
$this->pub_filtered = false;
        return 
$this;
    }

    public function 
enablePubFilter(){
        
$this->pub_filter_disabled = false;
        return 
$this;
    }

    public function 
disablePubFilter(){
        
$this->pub_filter_disabled = true;
        return 
$this;
    }

    public function 
filterPublishedOnly(){

        if (
$this->pub_filtered) { return $this; }

        
$this->pub_filtered = true;

        return 
$this->filterEqual('is_pub', 1);

    }

    public function 
isFiltersTableExists($ctype_name) {

        
$table_name = $this->getContentTypeTableName($ctype_name).'_filters';

        return 
$this->db->isTableExists($table_name);

    }
//============================================================================//

    
public function filterPropValue($ctype_name, $prop, $value){

        
$table_name  = $this->table_prefix.$ctype_name.'_props_values';
        
$table_alias = 'p'.$prop['id'];

        if(
$prop['handler']->setName($table_alias.'.value')->applyFilter($this, $value) !== false){

            
$this->joinInner($table_name, $table_alias, "{$table_alias}.item_id = i.id")->setStraightJoin();

            return 
$this->filterEqual($table_alias.'.prop_id', $prop['id']);

        }

        return 
false;

    }

//============================================================================//
//============================================================================//

    
public function addContentItem($ctype, $item, $fields){

        
$table_name = $this->table_prefix . $ctype['name'];

        
$item['user_id'] = empty($item['user_id']) ? cmsUser::getInstance()->id : $item['user_id'];

        if (!empty(
$item['props'])){
            
$props_values = $item['props'];
            unset(
$item['props']);
        }

        if(!isset(
$item['category_id'])){
            
$item['category_id'] = 0;
        }

        if(!empty(
$item['is_approved'])){
            
$item['date_approved'] = null; // будет CURRENT_TIMESTAMP
        
}

        if (!empty(
$item['new_category'])){
            
$category = $this->addCategory($ctype['name'], array(
                
'title' => $item['new_category'],
                
'parent_id' => $item['category_id']
            ));
            
$item['category_id'] = $category['id'];
        }

        unset(
$item['new_category']);

        if (!empty(
$item['new_folder'])){
            
$folder_exists = $this->getContentFolderByTitle($item['new_folder'], $ctype['id'], $item['user_id']);
            if(!
$folder_exists){
                
$item['folder_id'] = $this->addContentFolder($ctype['id'], $item['user_id'], $item['new_folder']);
            } else {
                
$item['folder_id'] = $folder_exists['id'];
            }
        }

        unset(
$item['new_folder']);

        
$add_cats = [];

        if (isset(
$item['add_cats'])){
            foreach(
$item['add_cats'] as $cat_id){
                if(!
$cat_id){
                    continue;
                }
                
$add_cats[] = $cat_id;
            }
            unset(
$item['add_cats']);
        }

        
$item['id'] = $this->insert($table_name, $item);

        
$this->updateContentItemCategories($ctype['name'], $item['id'], $item['category_id'], $add_cats);

        if (isset(
$props_values)){
            
$this->addPropsValues($ctype['name'], $item['id'], $props_values);
        }

        if (empty(
$item['slug'])){
            
$item = array_merge($item, $this->getContentItem($ctype['name'], $item['id']));
            
$item['slug'] = $this->getItemSlug($ctype, $item, $fields);
        }

        
$this->update($table_name, $item['id'], array(
            
'slug' => $item['slug'],
            
'date_last_modified' => null
        
));

        
cmsCache::getInstance()->clean('content.list.'.$ctype['name']);
        
cmsCache::getInstance()->clean('content.item.'.$ctype['name']);

        
$this->fieldsAfterStore($item, $fields, 'add');

        return 
$item;

    }

//============================================================================//
//============================================================================//

    
public function updateContentItem($ctype, $id, $item, $fields){

        
$table_name = $this->table_prefix . $ctype['name'];

        if(
array_key_exists('date_pub_end', $item)){
            if(
$item['date_pub_end'] === null){
                
$item['date_pub_end'] = false;
            }
        }

        if (!
$ctype['is_fixed_url']){

            if (
$ctype['is_auto_url']){
                
$item['slug'] = $this->getItemSlug($ctype, $item, $fields);
            } elseif(!empty(
$item['slug'])) {
                
$item['slug'] = lang_slug($item['slug']);
            }

            if(!empty(
$item['slug'])) {
                
$this->update($table_name, $id, array( 'slug' => $item['slug'] ));
            }

        }

        if (!empty(
$item['new_category'])){
            
$category = $this->addCategory($ctype['name'], array(
                
'title' => $item['new_category'],
                
'parent_id' => $item['category_id']
            ));
            
$item['category_id'] = $category['id'];
        }

        unset(
$item['new_category']);

        if (!empty(
$item['new_folder'])){
            
$folder_exists = $this->getContentFolderByTitle($item['new_folder'], $ctype['id'], $item['user_id']);
            if(!
$folder_exists){
                
$item['folder_id'] = $this->addContentFolder($ctype['id'], $item['user_id'], $item['new_folder']);
            } else {
                
$item['folder_id'] = $folder_exists['id'];
            }
        }

        unset(
$item['new_folder']);
        unset(
$item['folder_title']);

        
// удаляем поле SLUG из перечня полей для апдейта,
        // посколько оно могло быть изменено ранее
        
$update_item = $item; unset($update_item['slug']);

        if (!empty(
$update_item['props'])){
            
$this->updatePropsValues($ctype['name'], $id, $update_item['props']);
        }

        unset(
$update_item['props']);
        unset(
$update_item['user']);
        unset(
$update_item['user_nickname']);

        
$add_cats = [];

        if (isset(
$update_item['add_cats'])){
            foreach(
$update_item['add_cats'] as $cat_id){
                if(!
$cat_id){
                    continue;
                }
                
$add_cats[] = $cat_id;
            }
            unset(
$update_item['add_cats']);
        }

        
$update_item['date_last_modified'] = null;

        
$this->update($table_name, $id, $update_item);

        
$this->updateContentItemCategories($ctype['name'], $id, $item['category_id'], $add_cats);

        
cmsCache::getInstance()->clean('content.list.'.$ctype['name']);
        
cmsCache::getInstance()->clean('content.item.'.$ctype['name']);

        
$this->fieldsAfterStore($item, $fields, 'edit');

        return 
$item;

    }

    public function 
updateContentItemTags($ctype_name, $id, $tags){

        
$table_name = $this->table_prefix . $ctype_name;

        
$this->update($table_name, $id, array(
            
'tags' => $tags
        
));

        
cmsCache::getInstance()->clean('content.list.'.$ctype_name);
        
cmsCache::getInstance()->clean('content.item.'.$ctype_name);

    }

    public function 
replaceCachedTags($ctype_name, $ids, $new_tag, $old_tag) {

        
$table_name = $this->table_prefix . $ctype_name;

        
$old_tag = $this->db->escape($old_tag);
        
$new_tag = $this->db->escape($new_tag);

        if(!
is_array($ids)){
            
$ids = array($ids);
        }

        foreach(
$ids as $k=>$v){
            
$v = $this->db->escape($v);
            
$ids[$k] = "'{$v}'";
        }
        
$ids = implode(',', $ids);

        
$this->db->query("UPDATE `{#}{$table_name}` SET `tags` = REPLACE(`tags`, '{$old_tag}', '$new_tag') WHERE id IN ({$ids})");

        
cmsCache::getInstance()->clean('content.list.'.$ctype_name);
        
cmsCache::getInstance()->clean('content.item.'.$ctype_name);

    }

//============================================================================//
//============================================================================//

    
public function getItemSlug($ctype, $item, $fields, $check_slug = true){

        
$content_table_struct = $this->getContentTableStruct();
        
$slug_len = $content_table_struct['title']['size'];

        
$pattern = trim($ctype['url_pattern'], '/');

        
preg_match_all('/{([a-z0-9_]+)}/i', $pattern, $matches);

        if (!
$matches) { return lang_slug($item['id'], false); }

        list(
$tags, $names) = $matches;

        if (
in_array('category', $names)){
            
$category = $this->getCategory($ctype['name'], $item['category_id']);
            
$pattern = str_replace('{category}', $category['slug'], $pattern);
            unset(
$names[ array_search('category', $names) ]);
        }

        
$pattern = trim($pattern, '/');

        foreach(
$names as $idx=>$field_name){
            if (!empty(
$item[$field_name])){

                
$value = str_replace('/', '', $item[$field_name]);

                if (isset(
$fields[$field_name])){

                    
$value = $fields[$field_name]['handler']->getStringValue($value);

                    
$value = lang_slug(trim($value, '/'), false);

                }

                
$pattern = str_replace($tags[$idx], $value, $pattern);

            }
        }

        
$slug = $pattern;

        
$slug = mb_substr($slug, 0, $slug_len);

        if(!
$check_slug){
            return 
$slug;
        }

        
$slug = $this->checkCorrectEqualSlug($this->getContentTypeTableName($ctype['name']), $slug, $item['id'], $slug_len);

        return 
$slug;
    }

//============================================================================//
//============================================================================//

    
public function getContentItemCategories($ctype_name, $id){

        
$table_name = $this->table_prefix . $ctype_name . '_cats_bind';

        return 
$this->filterEqual('item_id', $id)->get($table_name, function($item, $model){
            return 
$item['category_id'];
        }, 
false);

    }

    public function 
getContentItemCategoriesList($ctype_name, $id){

        
$bind_table_name = $this->table_prefix . $ctype_name . '_cats_bind';
        
$cats_table_name = $this->table_prefix . $ctype_name . '_cats';

        
$this->join($bind_table_name, 'b', 'b.category_id = i.id');

        
$this->filterEqual('b.item_id', $id);

        
$this->orderBy('ns_left');

        
$this->useCache('content.categories');

        return 
$this->get($cats_table_name);

    }

    public function 
moveContentItemsToCategory($ctype, $category_id, $items_ids, $fields){

        
$table_name = $this->table_prefix . $ctype['name'];
        
$binds_table_name = $this->table_prefix . $ctype['name'] . '_cats_bind';

        
$items = $this->filterIn('id', $items_ids)->get($table_name);

        foreach(
$items as $item){

            
$this->
                
filterEqual('item_id', $item['id'])->
                
filterEqual('category_id', $item['category_id'])->
                
deleteFiltered($binds_table_name);

            
$is_bind_exists = $this->
                                
filterEqual('item_id', $item['id'])->
                                
filterEqual('category_id', $category_id)->
                                
getCount($binds_table_name, 'item_id');

            
$this->resetFilters();

            if (!
$is_bind_exists){

                
$this->insert($binds_table_name, array(
                    
'item_id' => $item['id'],
                    
'category_id' => $category_id
                
));

            }

            
$item['category_id'] = $category_id;

            if (!
$ctype['is_fixed_url'] && $ctype['is_auto_url']){
                
$item['slug'] = $this->getItemSlug($ctype, $item, $fields);
                
$this->update($table_name, $item['id'], array( 'slug' => $item['slug'] ));
            }

        }

        
$this->filterIn('id', $items_ids)->updateFiltered($table_name, array(
            
'category_id' => $category_id
        
));

        
cmsCache::getInstance()->clean('content.list.'.$ctype['name']);
        
cmsCache::getInstance()->clean('content.item.'.$ctype['name']);

        return 
true;

    }

    public function 
updateContentItemCategories($ctype_name, $id, $category_id, $add_cats){

        
$table_name = $this->table_prefix . $ctype_name . '_cats_bind';

        
$new_cats = empty($add_cats) ? array() : $add_cats;

        if (!
$category_id) { $category_id = 1; }

        if (!
in_array($category_id, $new_cats)){
            
$new_cats[] = $category_id;
        }

        
$current_cats = $this->
                            
filterEqual('item_id', $id)->
                            
get($table_name, function($item, $model){
                                return 
$item['category_id'];
                            }, 
false);

        if (
$current_cats){
            foreach(
$current_cats as $current_cat_id){

                if (!
in_array($current_cat_id, $new_cats)){
                    
$this->
                        
filterEqual('item_id', $id)->
                        
filterEqual('category_id', $current_cat_id)->
                        
deleteFiltered($table_name);
                }

            }
        }

        foreach(
$new_cats as $new_cat_id){
            if (!
$current_cats || !in_array($new_cat_id, $current_cats)){
                
$this->insert($table_name, array(
                    
'item_id' => $id,
                    
'category_id' => $new_cat_id
                
));
            }
        }

    }

//============================================================================//
//============================================================================//

    
public function restoreContentItem($ctype_name, $id){

        
$table_name = $this->table_prefix . $ctype_name;

        if(
is_numeric($id)){
            
$item = $this->getContentItem($ctype_name, $id);
            if(!
$item){ return false; }
        } else {
            
$item = $id;
        }

        
cmsCore::getController('activity')->addEntry('content', "add.{$ctype_name}", array(
            
'user_id'          => $item['user_id'],
            
'subject_title'    => $item['title'],
            
'subject_id'       => $item['id'],
            
'subject_url'      => href_to_rel($ctype_name, $item['slug'] . '.html'),
            
'is_private'       => isset($item['is_private']) ? $item['is_private'] : 0,
            
'group_id'         => isset($item['parent_id']) ? $item['parent_id'] : null,
            
'is_parent_hidden' => $item['is_parent_hidden'],
            
'date_pub'         => $item['date_pub'],
            
'is_pub'           => $item['is_pub']
        ));

        
cmsCore::getModel('comments')->setCommentsIsDeleted('content', $ctype_name, $item['id'], null);

        
cmsCache::getInstance()->clean('content.list.'.$ctype_name);
        
cmsCache::getInstance()->clean('content.item.'.$ctype_name);

        
$this->update($table_name, $item['id'], array('is_deleted' => null));

        
cmsEventsManager::hook('content_after_restore', array($ctype_name, $item));
        
cmsEventsManager::hook("content_{$ctype_name}_after_restore", $item);

        return 
true;

    }

    public function 
toTrashContentItem($ctype_name, $id){

        
$table_name = $this->table_prefix . $ctype_name;

        if(
is_numeric($id)){
            
$item = $this->getContentItem($ctype_name, $id);
            if(!
$item){ return false; }
        } else {
            
$item = $id;
        }

        
cmsCore::getController('activity')->deleteEntry('content', "add.{$ctype_name}", $item['id']);

        
cmsCore::getModel('comments')->setCommentsIsDeleted('content', $ctype_name, $item['id']);

        
cmsCache::getInstance()->clean('content.list.'.$ctype_name);
        
cmsCache::getInstance()->clean('content.item.'.$ctype_name);

        
$this->update($table_name, $item['id'], array('is_deleted' => 1));

        
cmsEventsManager::hook('content_after_trash_put', array($ctype_name, $item));
        
cmsEventsManager::hook("content_{$ctype_name}_after_trash_put", $item);

        return 
true;

    }

    public function 
deleteContentItem($ctype_name, $id){

        
$table_name = $this->table_prefix . $ctype_name;

        
$item = $this->getContentItem($ctype_name, $id);
        if(!
$item){ return false; }

        
$ctype = $this->getContentTypeByName($ctype_name);

        
cmsEventsManager::hook('content_before_delete', array('ctype_name'=>$ctype_name, 'item'=>$item));
        
cmsEventsManager::hook("content_{$ctype_name}_before_delete", $item);

        
$fields = $this->getContentFields($ctype_name, $id);

        foreach(
$fields as $field){
            
$field['handler']->delete($item[$field['name']]);
        }

        
cmsCore::getController('activity')->deleteEntry('content', "add.{$ctype_name}", $id);

        
cmsCore::getModel('comments')->deleteComments('content', $ctype_name, $id);
        
cmsCore::getModel('rating')->deleteVotes('content', $ctype_name, $id);
        
cmsCore::getModel('tags')->deleteTags('content', $ctype_name, $id);

        
cmsCache::getInstance()->clean('content.list.'.$ctype_name);
        
cmsCache::getInstance()->clean('content.item.'.$ctype_name);

        
$this->deletePropsValues($ctype_name, $id);

        
$this->deleteContentItemRelations($ctype['id'], $id);

        
$this->filterEqual('item_id', $item['id'])->deleteFiltered($table_name.'_cats_bind');

        
$success = $this->delete($table_name, $id);

        if(
$success){
            
cmsEventsManager::hook('content_after_delete', array('ctype_name'=>$ctype_name, 'item'=>$item));
            
cmsEventsManager::hook("content_{$ctype_name}_after_delete", $item);
        }

        return 
$success;

    }

    public function 
deleteUserContent($user_id){

        
$ctypes = $this->getContentTypes();

        foreach(
$ctypes as $ctype){

            
$this->disableDeleteFilter()->disableApprovedFilter()->
                    
disablePubFilter()->disablePrivacyFilter();

            
$items = $this->filterEqual('user_id', $user_id)->getContentItems($ctype['name']);

            if (
is_array($items)){
                foreach(
$items as $item){
                    
$this->deleteContentItem($ctype['name'], $item['id']);
                }
            }

        }

        
$this->filterEqual('user_id', $user_id)->deleteFiltered('content_folders');
        
$this->filterEqual('user_id', $user_id)->deleteFiltered('moderators');

    }

//============================================================================//
//============================================================================//

    
public function applyPrivacyFilter($ctype, $allow_view_all = false) {

        
$hide_except_title = (!empty($ctype['options']['privacy_type']) && $ctype['options']['privacy_type'] == 'show_title');

        
// Сначала проверяем настройки типа контента
        
if (!empty($ctype['options']['privacy_type']) && in_array($ctype['options']['privacy_type'], array('show_title', 'show_all'), true)) {
            
$this->disablePrivacyFilter();
            if(
$ctype['options']['privacy_type'] != 'show_title'){
                
$hide_except_title = false;
            }
        }

        
// А потом, если разрешено правами доступа, отключаем фильтр приватности
        
if ($allow_view_all) {
            
$this->disablePrivacyFilter(); $hide_except_title = false;
        }

        return 
$hide_except_title;

    }

    public function 
getContentItemsCount($ctype_name){

        
$table_name = $this->table_prefix . $ctype_name;

        if (!
$this->privacy_filter_disabled) { $this->filterPrivacy(); }
        if (!
$this->approved_filter_disabled) { $this->filterApprovedOnly(); }
        if (!
$this->delete_filter_disabled) { $this->filterAvailableOnly(); }
        if (!
$this->pub_filter_disabled) { $this->filterPublishedOnly(); }
        if (!
$this->hidden_parents_filter_disabled) { $this->filterHiddenParents(); }

        
$this->useCache("content.list.{$ctype_name}");

        return 
$this->getCount($table_name);

    }

    public function 
getContentItemsForSitemap($ctype_name, $fields = array()){

        
$table_name = $this->table_prefix . $ctype_name;

        
$this->selectOnly('slug');
        
$this->select('date_last_modified');
        
$this->select('title');
        if(
$fields){
            foreach (
$fields as $field) {
                
$this->select($field);
            }
        }

        if (!
$this->privacy_filter_disabled) { $this->filterPrivacy(); }
        if (!
$this->approved_filter_disabled) { $this->filterApprovedOnly(); }
        if (!
$this->delete_filter_disabled) { $this->filterAvailableOnly(); }
        if (!
$this->pub_filter_disabled) { $this->filterPublishedOnly(); }
        if (!
$this->hidden_parents_filter_disabled) { $this->filterHiddenParents(); }

        if (!
$this->order_by){ $this->orderBy('date_pub', 'desc')->forceIndex('date_pub'); }

        return 
$this->get($table_name, false, false);

    }

    public function 
getContentItems($ctype_name, $callback = null){

        
$table_name = $this->table_prefix . $ctype_name;

        
$this->select('u.nickname', 'user_nickname');
        
$this->select('u.slug', 'user_slug');
        
$this->select('u.avatar', 'user_avatar');
        
$this->select('u.groups', 'user_groups');
        
$this->select('f.title', 'folder_title');
        
$this->join('{users}', 'u', 'u.id = i.user_id');
        
$this->joinLeft('content_folders', 'f', 'f.id = i.folder_id');

        if (!
$this->privacy_filter_disabled) { $this->filterPrivacy(); }
        if (!
$this->approved_filter_disabled) { $this->filterApprovedOnly(); }
        if (!
$this->delete_filter_disabled) { $this->filterAvailableOnly(); }
        if (!
$this->pub_filter_disabled) { $this->filterPublishedOnly(); }

        if (!
$this->order_by){ $this->orderBy('date_pub', 'desc')->forceIndex('date_pub'); }

        
$this->useCache('content.list.'.$ctype_name);

        
$user = cmsUser::getInstance();

        return 
$this->get($table_name, function($item, $model) use ($user, $callback, $ctype_name){

            
$item['user'] = array(
                
'id'        => $item['user_id'],
                
'slug'      => $item['user_slug'],
                
'nickname'  => $item['user_nickname'],
                
'avatar'    => $item['user_avatar'],
                
'groups'    => $item['user_groups'],
                
'is_friend' => $user->isFriend($item['user_id'])
            );

            if (
is_callable($callback)){
                
$item = $callback($item, $model, $ctype_name, $user);
            }

            return 
$item;

        });

    }

//============================================================================//
//============================================================================//

    
public function getContentItem($ctype_name, $id, $by_field = 'id'){

        if(
is_numeric($ctype_name)){

            
$ctype = $this->getContentType($ctype_name);

            if(!
$ctype){ return false; }

            
$ctype_name = $ctype['name'];

        }

        
$table_name = $this->table_prefix . $ctype_name;

        
$this->select('f.title', 'folder_title');

        
$this->joinUser();
        
$this->joinLeft('content_folders', 'f', 'f.id = i.folder_id');

        
$this->useCache("content.item.{$ctype_name}");

        return 
$this->getItemByField($table_name, $by_field, $id, function($item, $model) use($ctype_name){

            
$item['user'] = array(
                
'id'       => $item['user_id'],
                
'groups'   => $item['user_groups'],
                
'slug'     => $item['user_slug'],
                
'nickname' => $item['user_nickname'],
                
'avatar'   => $item['user_avatar']
            );

            
$item['is_draft'] = false;

            if (!
$item['is_approved']){
                
$item['is_draft'] = $model->isDraftContentItem($ctype_name, $item);
            }

            return 
$item;

        }, 
$by_field);

    }

    public function 
getContentItemBySLUG($ctype_name, $slug){

        return 
$this->getContentItem($ctype_name, $slug, 'slug');

    }

//============================================================================//
//============================================================================//

    
public function getUserContentItemsCount($ctype_name, $user_id, $is_only_approved = true){

        
$this->filterEqual('user_id', $user_id);

        if (!
$is_only_approved) { $this->approved_filter_disabled = true; }

        
$count = $this->getContentItemsCount( $ctype_name );

        
$this->resetFilters();

        return 
$count;

    }

    public function 
getUserContentItemsCount24($ctype_name, $user_id){

        
$this->filter("DATE(DATE_FORMAT(i.date_pub, '%Y-%m-%d')) = CURDATE()");

        return 
$this->getUserContentItemsCount($ctype_name, $user_id, false);

    }

    public function 
getUserContentCounts($user_id, $is_filter_hidden=false, $access_callback = false){

        
$counts = array();

        
$ctypes = $this->getContentTypes();

        
$this->filterEqual('user_id', $user_id);

        if (
$is_filter_hidden){
            
$this->enableHiddenParentsFilter();
        }

        if (!
$is_filter_hidden){
            
$this->disableApprovedFilter();
            
$this->disablePubFilter();
            
$this->disablePrivacyFilter();
        }

        foreach(
$ctypes as $ctype){

            if(
is_callable($access_callback) && !$access_callback($ctype)){
                continue;
            }

            if(!
$ctype['options']['profile_on']){
                continue;
            }

            
$count = $this->getContentItemsCount( $ctype['name'] );

            if (
$count) {

                
$counts[ $ctype['name'] ] = array(
                    
'count' => $count,
                    
'is_in_list' => $ctype['options']['profile_on'],
                    
'title' => empty($ctype['labels']['profile']) ? $ctype['title'] : $ctype['labels']['profile']
                );

            }

        }

        
$this->resetFilters();

        return 
$counts;

    }

//============================================================================//
//============================================================================//

    
public function publishDelayedContentItems($ctype_name){

        return 
$this->filterIsNull('is_deleted')->
                    
filterNotEqual('is_pub', 1)->
                    
filter('i.date_pub <= NOW()')->
                    
updateFiltered($this->table_prefix.$ctype_name, array(
                        
'is_pub' => 1
                    
));

    }

    public function 
hideExpiredContentItems($ctype_name){

        return 
$this->filterIsNull('is_deleted')->
                    
filterEqual('is_pub', 1)->
                    
filterNotNull('date_pub_end')->
                    
filter('i.date_pub_end <= NOW()')->
                    
updateFiltered($this->table_prefix.$ctype_name, array(
                        
'is_pub' => 0
                    
));

    }

    public function 
deleteExpiredContentItems($ctype_name){

        return 
$this->
                    
filterNotNull('date_pub_end')->
                    
filter('i.date_pub_end <= NOW()')->
                    
get($this->table_prefix.$ctype_name, function($item, $model) use($ctype_name){
                        
$model->deleteContentItem($ctype_name, $item['id']);
                        return 
$item['id'];
                    });

    }

    public function 
toTrashExpiredContentItems($ctype_name){

        return 
$this->
                    
filterNotNull('date_pub_end')->
                    
filter('i.date_pub_end <= NOW()')->
                    
get($this->table_prefix.$ctype_name, function($item, $model) use($ctype_name){
                        
$model->toTrashContentItem($ctype_name, $item);
                        return 
$item['id'];
                    });

    }

    public function 
toggleContentItemPublication($ctype_name, $id, $is_pub){

        
$this->update($this->table_prefix.$ctype_name, $id, array(
            
'is_pub' => $is_pub
        
));

        
cmsCache::getInstance()->clean('content.list.'.$ctype_name);
        
cmsCache::getInstance()->clean('content.item.'.$ctype_name);

        return 
true;

    }

    public function 
incrementHitsCounter($ctype_name, $id){

        
cmsCache::getInstance()->clean('content.item.'.$ctype_name);

        return 
$this->filterEqual('id', $id)->increment($this->table_prefix.$ctype_name, 'hits_count');

    }

//============================================================================//
//============================================================================//

    
public function deleteCategory($ctype_name, $id, $is_delete_content=false){

        
$category = $this->getCategory($ctype_name, $id);

        
$this->filterCategory($ctype_name, $category, true);

        if (!
$is_delete_content){
            
$table_name = $this->table_prefix . $ctype_name;
            
$this->updateFiltered($table_name, array(
                
'category_id' => 1
            
));
        }

        if (
$is_delete_content){

            
$this->disableDeleteFilter()->disableApprovedFilter()->
                    
disablePubFilter()->disablePrivacyFilter();

            
$items = $this->getContentItems($ctype_name);

            if (
$items){
                foreach(
$items as $item){
                    
$this->deleteContentItem($ctype_name, $item['id']);
                }
            }

        }

        
$this->unbindContentProps($ctype_name, $id);

        
parent::deleteCategory($ctype_name, $id);

    }

//============================================================================//
//============================================================================//

    
public function getRatingTarget($ctype_name, $id){

        
$table_name = $this->table_prefix . $ctype_name;

        
$item = $this->getItemById($table_name, $id);

        if(
$item){
            
$item['page_url'] = href_to($ctype_name, $item['slug'].'.html');
        }

        return 
$item;

    }

    public function 
updateRating($ctype_name, $id, $rating){

        
$table_name = $this->table_prefix . $ctype_name;

        
$this->update($table_name, $id, array('rating' => $rating));

        
cmsCache::getInstance()->clean('content.list.'.$ctype_name);
        
cmsCache::getInstance()->clean('content.item.'.$ctype_name);

    }

//============================================================================//
//============================================================================//

    
public function updateCommentsCount($ctype_name, $id, $comments_count){

        
$table_name = $this->table_prefix . $ctype_name;

        
$this->update($table_name, $id, array('comments' => $comments_count));

        
cmsCache::getInstance()->clean('content.list.'.$ctype_name);
        
cmsCache::getInstance()->clean('content.item.'.$ctype_name);

        return 
true;

    }

    public function 
getCommentsOptions($ctype_name) {

        
$ctype = $this->getContentTypeByName($ctype_name);

        return [
            
'enable' => $ctype['is_comments'],
            
'title_pattern' => (!empty($ctype['options']['comments_title_pattern']) ? $ctype['options']['comments_title_pattern'] : ''),
            
'labels' => (!empty($ctype['options']['comments_labels']) ? $ctype['options']['comments_labels'] : []),
            
'template' => (!empty($ctype['options']['comments_template']) ? $ctype['options']['comments_template'] : '')
        ];

    }

    public function 
getTargetItemInfo($ctype_name, $id){

        
$item = $this->getContentItem($ctype_name, $id);

        if (!
$item){ return false; }

        return array(
            
'url' => href_to_rel($ctype_name, $item['slug'].'.html'),
            
'title' => $item['title'],
            
'is_private' => $item['is_private'] || $item['is_parent_hidden']
        );

    }

//============================================================================//
//============================================================================//

    
public function toggleParentVisibility($parent_type, $parent_id, $is_hidden){

        
$ctypes_names = $this->getContentTypesNames();

        
$is_hidden = $is_hidden ? 1 : null;

        foreach(
$ctypes_names as $ctype_name){

            
$table_name = $this->table_prefix . $ctype_name;

            
$this->
                
filterEqual('parent_type', $parent_type)->
                
filterEqual('parent_id', $parent_id)->
                
updateFiltered($table_name, array('is_parent_hidden' => $is_hidden));

            
cmsCache::getInstance()->clean('content.list.'.$ctype_name);
            
cmsCache::getInstance()->clean('content.item.'.$ctype_name);

        }

    }

    public function 
isDraftContentItem($ctype_name, $item) {

        if(!empty(
$item['is_approved'])){ return false; }

        return !(bool)
$this->selectOnly('id')->filterEqual('ctype_name', $ctype_name)->
                    
filterEqual('item_id', $item['id'])->getItem('moderators_tasks');

    }

    public function 
getDraftCounts($user_id){

        
$counts = array();

        
$ctypes = $this->getContentTypes();

        foreach(
$ctypes as $ctype){

            
$this->useCache("content.list.{$ctype['name']}");

            
$this->filterEqual('user_id', $user_id);
            
$this->filterEqual('is_approved', 0);
            
$this->disableApprovedFilter();
            
$this->disablePubFilter();
            
$this->disablePrivacyFilter();

            
$this->joinExcludingLeft('moderators_tasks', 't', 't.item_id', 'i.id', "t.ctype_name = '{$ctype['name']}'");

            
$count = $this->getContentItemsCount($ctype['name']);

            
$this->resetFilters();

            if (
$count) {
                
$counts[ $ctype['name'] ] = $count;
            }

        }

        return 
$counts;

    }

    public function 
approveContentItem($ctype_name, $id, $moderator_user_id){

        
$table_name = $this->table_prefix . $ctype_name;

        
$this->update($table_name, $id, array(
            
'is_approved'   => 1,
            
'approved_by'   => $moderator_user_id,
            
'date_approved' => ''
        
));

        
cmsCache::getInstance()->clean('content.list.'.$ctype_name);
        
cmsCache::getInstance()->clean('content.item.'.$ctype_name);

        return 
true;

    }

    public function 
unbindParent($ctype_name, $id){

        
$table_name = $this->table_prefix . $ctype_name;

        
$this->update($table_name, $id, array(
            
'parent_id'        => null,
            
'parent_type'      => null,
            
'parent_title'     => null,
            
'parent_url'       => null,
            
'is_parent_hidden' => null
        
));

        
cmsCache::getInstance()->clean('content.list.'.$ctype_name);
        
cmsCache::getInstance()->clean('content.item.'.$ctype_name);

        return 
true;

    }

    
/**
     * @deprecated
     *
     * Метод для совместимости
     * @param string $ctype_name
     * @param integer $user_id
     * @return boolean
     */
    
public function userIsContentTypeModerator($ctype_name, $user_id){
        return 
cmsCore::getModel('moderation')->userIsContentModerator($ctype_name, $user_id);
    }

}
Онлайн: 2
Реклама