2012-08-08

Расширяем пользовательские свойства в 1С-Битрикс

Лирическое отступление

Когда только начинаешь работать с чем-нибудь новым программным средством, то, зачастую, от недостаточного понимания, складывается негативное впечатление - все не так, как мне хочется и тут ограничены возможности и тут поставленная задача не реализуема. Но со временем приходит понимание, узнаешь больше о системе и все встает на свои места. Все больше осваиваешь и привыкаешь и задачи решаются быстрее и в рамках используемого в системе подхода.


Ближе к делу

В рамках одного проекта на Битриксе понадобилось иметь свое необычное пользовательское свойство, ни числовое, ни строковое, ни какое-либо другое стандартное не подходило. Сначала задача казалась не выполнимой в системе, но поиск решения в Интернете принес свои плоды и мы стали реализовывать требуемый функционал.
Чтобы не быть голословным далее рассмотрим простой пример реализации пользовательского свойства для элементов и разделов инфоблоков.
Процесс разработки был не простой, но нет ничего невозможного для специалиста, это всего лишь вопрос времени. К сожалению, пользовательские свойства элементов инфоблоков и разделов относятся к разным модулям (инфоблоки и главный модуль соответственно), отсюда и разный подход в их реализации. К тому же, поиск в Интернете выдал только простой пример и за дополнительной информацией пришлось обращаться к исходным кодам Битрикса, к счастью, они местами хорошо документированы.

Осветим поподробней эту полезную тему

Для примера попробуем реализовать свойство для выбора цвета с помощью jQuery UI. Свойствао можно будет использовать как для элемента так и для раздела инфоблока. Сразу замечу, что код примера нужно доводить до ума для использования на реальном проекте. По уму, нужно выделить общий класс с методами для представления и редактирования свойства, затем унаследовать от него еще два класса - на каждую отличающуюся реализацию.
Общий алгоритм примерно следующий:
  • прописываем обработчики событий для OnIBlockPropertyBuildList (из инфоблока) и OnUserTypeBuildList (из главного модуля);
  • реализуем методы описывающие свойства, отдельные для каждого из двух модулей;
  • реализуем общие методы представления и редактирования свойства.

Собственно, сам код с небольшими комментариями

<?
// расширим свой класс от числового типа
class UserDataColor extends CUserTypeInteger
{

 // инициализация пользовательского свойства для главного модуля
 function GetUserTypeDescription()
 {
  return array(
   "USER_TYPE_ID" => "color",
   "CLASS_NAME" => "UserDataColor",
   "DESCRIPTION" => "Цвет",
   "BASE_TYPE" => "int",
  );
 }

 // инициализация пользовательского свойства для инфоблока
 function GetIBlockPropertyDescription()
 {
  return array(
           "PROPERTY_TYPE" => "S",
           "USER_TYPE" => "color",
           "DESCRIPTION" => "Цвет",
   'GetPropertyFieldHtml' => array('UserDataColor', 'GetPropertyFieldHtml'),
   'GetAdminListViewHTML' => array('UserDataColor', 'GetAdminListViewHTML'),
  );
 }

 // представление свойства
 function getViewHTML($name, $value)
 {
  return '<div style="display: block; width: 16px; height: 16px; background-color: #'.str_pad(dechex($value), 6, '0', STR_PAD_LEFT).';">&nbsp;</div>';
 }

 // редактирование свойства
 function getEditHTML($name, $value, $is_ajax = false)
 {
  $uid = 'x'.uniqid();
  $dom_id = $uid;
  $colorHex = str_pad(dechex($value), 6, '0', STR_PAD_LEFT);
  $colorRGB = array();
  $colorRGB[0] = hexdec(substr($colorHex, -6, 2));
  $colorRGB[1] = hexdec(substr($colorHex, -4, 2));
  $colorRGB[2] = hexdec(substr($colorHex, -2, 2));
  $init = $is_ajax ? 'init();' : '$(init);';
return <<<SSS
  <input type="hidden" id="{$dom_id}" name="{$name}" value="{$value}">
  <link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.21/jquery-ui.min.js"></script>
  <style>
   #red{$uid}, #green{$uid}, #blue{$uid} {float: left; clear: left; width: 300px; margin: 15px;}
   #swatch{$uid} {width: 120px; height: 100px; margin-top: 18px; margin-left: 350px; background-image: none;}
   #red{$uid} .ui-slider-range, #red{$uid} .ui-slider-handle { background: #ef2929; }
   #green{$uid} .ui-slider-range, #green{$uid} .ui-slider-handle { background: #8ae234; }
   #blue{$uid} .ui-slider-range, #blue{$uid} .ui-slider-handle { background: #729fcf; }
  </style>
  <div>
   <div id="red{$uid}"></div>
   <div id="green{$uid}"></div>
   <div id="blue{$uid}"></div>
   <div id="swatch{$uid}" class="ui-widget-content ui-corner-all"></div>
  </div>
  <script>
   (function(){
   function hexFromRGB(r, g, b) {
    var hex = [
     r.toString( 16 ),
     g.toString( 16 ),
     b.toString( 16 )
    ];
    $.each( hex, function( nr, val ) {
     if ( val.length === 1 ) {
      hex[ nr ] = "0" + val;
     }
    });
    return hex.join( "" ).toUpperCase();
   }
   function refreshSwatch() {
    var red = $( "#red{$uid}" ).slider( "value" ),
     green = $( "#green{$uid}" ).slider( "value" ),
     blue = $( "#blue{$uid}" ).slider( "value" ),
     hex = hexFromRGB( red, green, blue );
    $( "#{$dom_id}" ).val(red*256*256+green*256+blue);
    $( "#swatch{$uid}" ).css( "background-color", "#" + hex );
   }
   var init = function() {
    $( "#red{$uid}, #green{$uid}, #blue{$uid}" ).slider({
     orientation: "horizontal",
     range: "min",
     max: 255,
     value: 0,
     slide: refreshSwatch,
     change: refreshSwatch
    });
    $( "#red{$uid}" ).slider( "value", {$colorRGB[0]} );
    $( "#green{$uid}" ).slider( "value", {$colorRGB[1]} );
    $( "#blue{$uid}" ).slider( "value", {$colorRGB[2]} );
   };
   {$init}
   })();
  </script>
SSS;
 }

 // редактирование свойства в форме (главный модуль)
 function GetEditFormHTML($arUserField, $arHtmlControl)
 {
  return self::getEditHTML($arHtmlControl['NAME'], $arHtmlControl['VALUE'], false);
 }

 // редактирование свойства в списке (главный модуль)
 function GetAdminListEditHTML($arUserField, $arHtmlControl)
 {
  return self::getViewHTML($arHtmlControl['NAME'], $arHtmlControl['VALUE'], true);
 }

 // представление свойства в списке (главный модуль, инфоблок)
 function GetAdminListViewHTML($arProperty, $value, $strHTMLControlName)
 {
  return self::getViewHTML($strHTMLControlName['VALUE'], $value['VALUE']);
 }

 // редактирование свойства в форме и списке (инфоблок)
 function GetPropertyFieldHtml($arProperty, $value, $strHTMLControlName)
 {
  return $strHTMLControlName['MODE'] == 'FORM_FILL'
         ? self::getEditHTML($strHTMLControlName['VALUE'], $value['VALUE'], false)
         : self::getViewHTML($strHTMLControlName['VALUE'], $value['VALUE'])
  ;
 }

}

// добавляем тип для инфоблока
AddEventHandler("iblock", "OnIBlockPropertyBuildList", array("UserDataColor", "GetIBlockPropertyDescription"));
// добавляем тип для главного модуля
AddEventHandler("main", "OnUserTypeBuildList", array("UserDataColor", "GetUserTypeDescription"));

?>

А вот что у нас получилось

Представление свойства для раздела в списке

Редактирование свойства для раздела

Представление свойства для элемента

Редактирование свойства для элемента

По желанию, еще можно реализовать редактирование свойств прямо в списках, в примере в этом режиме выводится представление.