Механизм инфо-блоков достаточно удобная в использовании штука, но есть некоторые мелочи, которые не дают в полной мере расслабиться и получать удовольствие. Одна из таких штук - это кастомный тип "Файл" свойств инфо-блока.
Всем хорош этот тип, но только в штатной реализации в админ-зоне нельзя посмотреть, что же именно находится в поле типа "Файл". Штатный редактор отображает только размер и имя файла, а чтобы найти файл по имени требуется вручную перелопатить папку upload.
Предварительное изучение системы кастомных типов Bitrix.NET показало, что особого труда доработать штатный редактор не составит. Так что, как только выдалась свободная минутка, я потратил пару часов на доработку штатного редактора.
Основной подход к модификации был следующий - с минимальными усилиями разработать собственный кастомный тип с поведением, аналогичным стандартному типу File, но чтобы он в админ-зоне отображал изображение из файла. Штатная реализация типа File - более 500 строк кода, поэтому было решено отнаследоваться от существующей реализации и далее модифицировать ее поведение. А план работ следующий:
- Создать собственный тип
- Добиться поведения, аналогичного штатному типу File
- Модифицировать поведение в соответствии с собственными задачами
Кастомные типы в Bitrix.NET реализованы в виде UserControl'ов, которые расположены в папке ~/bitrix/customtypes. Для каждого кастомного типа в этой папке должна быть создана подпапка, содержащая UserControl в виде файла с именем customtype.ascx.
Собственно, загрузкой кастомных типов в Bitrix.NET ведает класс Bitrix.BXCustomTypeManager. Этот класс содержит статическую коллекцию объектов, производных от Bitrix.BXCustomType. Заполнение коллекции класс выполняет при первом обращении к ней и не содержит никаких средств для ее перезагрузки во время работы сайта. А отсюда следует, что если Вы вносите изменения в код кастомного типа, то Вам следует перезапустить сайт, чтобы BXCustomTypeManager положил в коллекцию новую реализацию Вашего типа.
UserControl, располагающийся в файле customtype.ascx, должен быть отнаследован от Bitrix.BXCustomType. Bitrix.BXCustomType содержит несколько абстрактных свойств, которые следует реализовать, но так как в мои планы входило всего лишь доработать поведение штатного типа, то я отнаследовал свой тип от BXCustomTypeFile. BXCustomTypeFile это реализация кастомного типа файл. Чтобы мой UserControl смог его увидеть, необходимо добавить в ascx ссылку на кастомный тип.
<%@ Reference VirtualPath="~/bitrix/customtypes/file/customtype.ascx" %> |
Теперь надо подправить идентифицирующие мой тип данные. Идентификация типа системой Bitrix.NET выполняется по двум параметрам - имени типа и его видимому названию. Точнее, Bitrix.NET идентифицирует тип по его имени, но видимое название тоже штука важная, так как по нему тип идентифицирует оператор сайта. Имя типа возвращает свойство TypeName, а заголовок типа свойство Title. Я переопределил в своей реализации типа только свойство TypeName, а реализацию свойства Title оставил унаследованной от типа File. В типе File видимое название типа возвращается с учетом текущего языка из файла локализации, поэтому для замены имени достаточно создать в своем типе папки с файлами локализации и указать в них свои значения соответствующих свойств.
(Не обращайте внимания на файлы edit.*, это скриншот уже окончательной реалиазации, я ниже напишу, как они появляются)
Первый путь плана выполнен. Того, что сделано к этому месту, уже достаточно для того, чтобы увидеть свой тип в конструкторе инфо-блока и создать свойство в инфо-блоке собственного типа. Но нет возможности настроить параметры свойства и само значение свойства не отображается в редакторе. Это все потому, что настройка параметров свойства типа и отображение значения свойства выполняется UserControl'ами, которые также должны быть определены в кастомном типе.
Кастомный тип содержит два свойства типа System.Web.UI.Control - Settings и Edit (вернее четыре свойства, еще AdvancedSettings и GetFilter, но в нашем случае мы используем только два). Реализация их в типе File достаточно проста:
public override Control Settings
{
get {return LoadControl("settings.ascx");}
}
public override Control Edit
{
get {return LoadControl("edit.ascx");
}
|
Всего лишь загрузка соответствующего UserControl'а, и в папке типа File можно увидеть эти ascx'ы.
Но в моем случае папка, из которой следует загружать UserControl'ы, не является текущей папкой UserControl'а, поэтому эти свойства следует переопределить указать полные пути. Как-то так:
public override Control Settings
{
get {return LoadControl("~/bitrix/customtypes/file/settings.ascx");}
}
public override Control Edit
{
get {return LoadControl("~/bitrix/customtypes/file/edit.ascx");
}
|
Выполнен второй пункт плана. Наш тип полностью повторяет поведение штатного типа File. Теперь приступим к модификации поведения типа.
Если реализовывать "по-уму", то наш кастомный тип должен распознать тип файла и в зависимости от типа файла отобразить либо картинку, либо флеш-проигрыватель, либо просто ссылку на файл (а картинку следует еще и смаштабировать). Но, чтобы не отклоняться сильно от основной цели повествования, будем считать, что к нам загружают только изображения, так что добавим только код для показа изображения в админ-зоне.
Отображение содержимого свойства в админ-зоне выполняет элемент управления, возвращаемый свойством Edit кастомного типа. В случае типа File - это edit.ascx. ASP.NET не предоставляет средств по наследованию визуальной части UserControl'ов, поэтому для расширения функциональности этого UserControl'а пойдем по пути Copy-Paste и скопируем в папку собственного кастомного типа UserControl edit.ascx (далее будем называть его редактор).
Для начала нам надо найти, как сформировать ссылку на файл. Задача не совсем очевидная, так как сам файл хранится на диске папке, задаваемой в настройках сайта, а в значении соответствующего свойства хранится только идентификатор файла. Но "все уже украдено до нас" и в своем методе Initialize редактор выполняет data-binding к значению свойства. В самом конце метода находим строчки кода:
BXFile f = null;
if (currentValue != null && (currentValue.Value != null && (int)currentValue.Value != 0) && (f = BXFile.GetById((int)currentValue.Value, BXTextEncoder.EmptyTextEncoder)) != null)
{
StoredId.Value = "Y";
DisplayName.Value = f.FileNameOriginal;
DisplaySize.Value = BXStringUtility.BytesToString(f.FileSize);
if (addDescription)
Description.Text = f.Description;
}
|
Здесь редактор формирует по идентификатору файла объект типа BXFile, свойства которого предоставят нам необходимую информацию. Чтобы воспользоваться этой информацией позже во время rendering'а страницы, определим свойство для хранения объекта BXFile и добавим сохранение объекта в это свойство в метод Initialize (так как мы не наследуемся от штатного редактора сайта, а copy-paste'им его, то вносим изменения прямо в код метода).
Свойство ImageFile:
public BXFile ImageFile
{
get;
private set;
}
|
И добавка в код:
BXFile f = null;
if (currentValue != null && (currentValue.Value != null && (int)currentValue.Value != 0) && (f = BXFile.GetById((int)currentValue.Value, BXTextEncoder.EmptyTextEncoder)) != null)
{
StoredId.Value = "Y";
DisplayName.Value = f.FileNameOriginal;
DisplaySize.Value = BXStringUtility.BytesToString(f.FileSize);
if (addDescription)
Description.Text = f.Description;
ImageFile = f;
}
|
А теперь выведем нашу картинку. Для этого пойдем в ascx файл и добавим соответствующую разметку.
<% if(ImageFile != null)
{ %>
<img src="<%= ImageFile.FilePath %>" width="100" /><br />
<% } %>
|
Вот и все. Загруженное изображение отображается в админ-зоне (еще и масштабируется, правда только по одному измерению).
Упс, а при нажатии на кнопку "Очистить" изображение остается, хотя все параметры файла исчезают. Надо и изображение тоже скрывать. Смотрим реализацию этой функции.
На кнопке "Очистить" висит JavaScript обработчик события onclick - CustomTypeFile_UploadFile. Эта JavaScript функция определена в файле edit.aspx, располагающемся в папке (находим это выполняя поиск по всем файлам сайта). Мы уже избрали метод copy-paste, так что копируем себе и этот файл из штатного кастомного типа.
Функция CustomTypeFile_UploadFile принимает на вход идентификатор редактора кастомного типа (ClientID) и на его основании формирует идентификаторы вложенных DOM-элементов, получает эти элементы и сбрасывает их содержимое. Дополняем наш img элемент идентификатором и добавляем в код функции скрытие элемента.
Элемент img с идентификатором:
<img id="<% =ClientID + "_Image" %>" src="<%= ImageFile.FilePath %>" width="100" />
|
Добавка в код функции:
var imageId = id + '_Image';
if (c = document.getElementById(imageId))
{
c.style.display = 'none';
c.style.visibility = 'hidden';
c.src = '';
}
|
На этом выполнен и третий пункт нашего плана. Наш кастомный тип теперь отображает в админ-зоне изображение, содержащееся в свойстве объекта инфо-блока. Конечно, текущая реализация - это только технологический прототип, и ее следует расширить механизмами контроля типа файла, масштабирования изображения, скачивания файла на локальный компьютер и т.п. Но это уже стандартные задачи web-разработки и вы с ними справитесь самостоятельно.