Интеграция SMC в интерфейс

SMC - это новый элемент управления, который появился в версии 2.7 . Он позволяет отображать элементы иерархии, объекты и типы в виде таблицы или дерева (для метода list-view), упрощает навигацию по ним а также дает возможность произвести с сущностями некоторые действия (вроде перемещения в дереве, смены активности, назначения шаблонов и т.п.) без перезагрузки страницы. В стандартной поставке системы SMC используется практически повсеместно.

Модификация PHP кода

Для интеграции SMC в ваш модуль необходимо не только изменить шаблон, но и внести некоторые правки в PHP код модуля. Для начала включим поддержку сортировок и поиска в метод, выводящий список объектов (модификация затронет всего 2 строки):

abstract class __users extends baseModuleAdmin {
  public function users_list_all() {
    $per_page  = 50;
    /**
    * Обратите внимание!
    * Эта строка необходима для корректного определения 
    * текущей страницы
    */
    $curr_page = getRequest('p');
 
    $objectTypes = umiObjectTypesCollection::getInstance();
    $type_id = $objectTypes->getBaseType("users", "user");
 
    
    $sel = new umiSelection;
    $sel->addObjectType($type_id);
    $sel->addLimit($per_page, $curr_page);
    
    /**
    * Обратите внимание!
    * Непосредственно перед выборкой необходимо вызвать
    * специальный метод, который отвечает за подстановку
    * дополнительных параметров выборки
    */
    $this->autoDetectAllFilters($sel);
    
    $result = umiSelectionsParser::runSelection($sel);
    $total  = umiSelectionsParser::runSelectionCounts($sel);
 
    $this->setDataType("list");
    $this->setActionType("view");
    $this->setDataRange($per_page, $curr_page * $per_page); 
    
    $data = $this->prepareData($result, "objects");
 
    $this->setData($data, $total);
    return $this->doData();
  }
};

Еще одно изменение, которое необходимо произвести, заключается в добавлении нового метода в класс, отвечающий за административный интерфейс. Этот метод будет вызван системой и предоставит SMC необходимые параметры инициализации:

abstract class __users extends baseModuleAdmin {
  public function getDatasetConfiguration($param = '') {      
    return array(
      'methods' => array(
                         array('title'=>'Загрузка',  'forload'=>true, '#__name'=>'users_list_all'),
                         array('title'=>'Удалить',   '#__name'=>'del', 'aliases' => 'tree_delete_element,delete,del'),
                         array('title'=>'Сменить активность',  '#__name'=>'activity', 'aliases' => 'tree_set_activity,activity'),
                         array('title'=>'Копировать', 'module'=>'content', '#__name'=>'tree_copy_element'),
      'types' => array(
                         array('common' => 'true', 'id' => 'user')
                      ),
      'stoplist' => array('avatar', 'userpic', 'user_settings_data', 'user_dock', 
                         'orders_refs', 'activate_code', 'password', 'last_request_time', 
                         'login', 'is_online', 'nazvanie', 'delivery_addresses'),
      'default' => 'fname[99px]|lname[81px]|e-mail[96px]|groups[141px]|is_activated[100px]'
    );
 }
};

Содержимое результирующего массива

methods

Список методов (действий над сущностями), доступных из SMC

Методы описываются массивами со следующими элементами:

title

Заголовок метода, его увидят пользователи

forload

Может присутствовать только у одного метода из списка. Всегда true. Определяет метод модуля, который вернет xml с нужным для загрузки сущностей списком

#__name

Обязательный элемент описания. Определяет название метода модуля (то есть то, что будет вызвано на стороне сервера, например, если написано '#__name'=>'users_list_all', то будет вызван метод по адресу http://mysite/admin/users/users_list_all/)

module

Определяет модуль, из которого будет вызван метод, если этот модуль отличается от текущего. Параметр бывает полезен, чтобы избавиться от дублирования функционала (например модуль "Новости" для перемещения новостей по структуре пользуется методами, предоставляемыми модулем "Структура")

aliases

Определяет алиасы (дополнительные имена), с помощью которых также можно вызвать этот же метод с клиентской стороны (из SMC). Бывает полезно, когда один и тот же функционал в разных модулях выполняется методами с разными именами. Это позволяет не переписывать много раз такие вещи, как тулбары и контекстные меню из-за незначительных изменений

types

Список типов, с которыми работает метод

Типы описываются массивами из следующих элементов

id

Числовой идентификатор типа объектов (umiObjectType) или текстовое названия метода его базового типа (umiHierarchyType)

common

Зарезервировано для последующего использования. Всегда true. Должно присутствовать у одного типа из списка

stoplist

Массив, состоящий из названия полей, которые не будут отображаться в расширенном поиске и списке доступных колонок таблицы

default

Строка, определяющая список и ширину колонок, отображаемых в таблице по-умолчанию. Формат следующий: имя поля, за ним в квадратных скобках ширина колонки в пикселях (с обязательной припиской px), если требуется более одной колонки, то ставится вертикальная черта и подобным образом описывается вторая колонка; пробелы в описании не допускаются.

Модификация шаблона

Теперь для того, чтобы SMC отобразилась в нашем модуле, нам необходимо перегрузить шаблон list-view для метода users_list_all . Для этого в list.view.xsl, который находится в папке шаблонов нашего модуля (в данном случае /data/modules/users/list.view.xsl), напишем такой код:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:spry="http://ns.adobe.com/spry">

  <xsl:template match="result[@method = 'users_list_all']">
      
    <script type="text/javascript">
      // Вешаем событие на загрузку страницы, в котором будет инициализирован SMC
      Event.observe(window, 'load', function() {
      
      // Сначала создадим DataSet
      // Параметры: 
      //    _sModuleName  имя модуля, предоставляющего данные и некоторую функциональность
      //    _bSuspendInit true - отложить инициализацию до явного вызова Init, false - немедленная инициализация
      //    _sInitParam   параметр, который будет передан в метод getDatasetConfiguration
      var oDataSet = new dataSet('<xsl:value-of select="$module"/>', true, '<xsl:value-of select="$param0"/>');
      
      // Здесь регистрируем стандартные подтверждения действий (удаления и т.п.)
      oDataSet.addEventHandler('onBeforeExecute', createConfirm(oDataSet));

      // Создаем начальный пустой фильтр
      var oInitFltr = new filter();

      // Создаем фильтр по-умолчанию, который будет применяться к каждой выборке
      var oDefaultFilter = new filter();             
      oDefaultFilter.setVirtualCopyChecking(true);                     // Включаем проверку информации о виртуальных копиях
      oDefaultFilter.setViewMode(true);                                // Зарезервировано
      oDefaultFilter.setLang('<xsl:value-of select="$lang-id"/>');     // Устанавливаем язык выборки
      oDefaultFilter.setDomain('<xsl:value-of select="$domain-id"/>'); // Устанавливаем домен выборки

      oDataSet.setDefaultFilter(oDefaultFilter);                       // Выставляем фильтр по-умолчанию

      // Создаем элемент управления
      // Параметры:
      //    DataSet
      //    Тип содержимого (для таблицы TableItem, для дерева TreeItem)
      //    Дополнительные параметры
      var oTable = new Control(oDataSet, TableItem, {
        id : 'tree-<xsl:value-of select="$module" />-<xsl:value-of select="$method" />',  // ID элемента управления на странице (уникальный)
        toolbar : TableToolbar,                      // Тулбар (TableToolbar для таблицы, TreeToolbar для дерева)
        allowDrag : false,                           // Перетаскивание содержимого (true - разрешить, false - запретить)
        iconsPath : '/images/cms/admin/mac/tree/',   // Путь к интерфейсным иконкам
        container : document.getElementById('table_container_catalog'), // Элемент, в котором будет рисоваться SMC (см. листинг ниже)
        flatMode : true,                             // Режим отображения (true - плоский, false - в виде дерева) 
        enableObjectsActivity : true                 // Визуализация активности объектов (true - включено, false - выключено)
      });

      // Создадим корневой элемент (он необходим!)
      var oRoot = oTable.setRootNode({
        'id' : 0,                   // id корневого элемента
        'allow-drag' : false,       // Разрешить перетаскивание (действует локально на этот элемент)
        'force-draw' : false,       // Рисовать корневой элемент или нет
        'iconbase' : '/images/cms/admin/mac/tree/ico_domain.png', // Путь к иконке элемента
        'name' : '<xsl:value-of select="/result/@domain"/>',      // Название
        'is-active' : '1',                                        // Активность
        'allow-copy' : false,                                     // Разрешить копирование
        'allow-activity' : false,                                 // Разрешить изменение активности
        'create-link' : '/admin/content/add/0/page/?domain=<xsl:value-of select="@host"/>' // Ссылка на добавление подэлемента 
                                                                                           // (не видна если force-draw = false)
      });

      // Применяем начальный фильтр к корневому элементу
      oRoot.filter = oInitFltr;

      // Создаем контроллер фильтра (рисует форму поиска)
      // Параметры:
      //    объект-контролл, к которому прикрепляется фильтр
      //    Идентификатор типа, по которому производится фильтрация
      //    Отложенная инициализация
      //    контейнер на странице (см. листинг ниже)
      var oFilterController = new filterController(oTable, 4, true, $('table_filter'));

      });


      // Выставляем код инициализацию всех элементов управления на загрузку пользовательских настроек
      SettingsStore.getInstance().addEventHandler("onloadcomplete", function() {
                for (var i = 0; i &lt; Control.instances.length; i++) {
                    Control.instances[i].init();
                }
      });
    </script>


    
    <div id="table_filter">
    </div>

    <div style="width:850px;" class="tableItemContainer">
       
      <table class="table-container allowContextMenu" id="table_container_catalog" width="100%"
         cellspacing="0" cellpadding="0" border="0">
      </table>
    </div>

  </xsl:template>
</xsl:stylesheet>

За примерами интеграции вы можете обратиться к существующему в системе коду. Все модули, в которых объекты выводятся в табличном виде или в виде дерева, используют SMC.