В интернет-магазинах/сайтах/веб-приложениях/B2B отображение характеристик товаров, картинок и текстовых описаний обычно оформляется в отдельных блоках . Однако, в текстовых описаниях иногда требуется использование HTML тегов, таких как: выделить жирным, подчеркнуть, заголовки, размер шрифта, вставить поясняющие картинки внутри описания и оформить список. Для решения этой задачи вполне достаточно функций стандартного HTML редактора платформы 1С, который просто внедряется в любую форму.
В нашем конструкторе веб-приложений EDIbot (подсистема для 1С для создания Dashboard, B2B) тоже возникла необходимость использования в ячейках внутреннего HTML оформления шаблона, покажем на примере:
Или вот как эта карточка выглядит в вебе - https://jsonwebapp.com/regexp/json, ссылка на JSON для песочницы.
Итак, нам понятно, что мы хотим получить на выходе нашей публикации, давайте приступим к реализации.
Функция ПолучитьHTML() возвращает нам HTML страницу со ссылками внутри неё на ключи картинок ("image001") и заполняет структуру вложений.
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
<meta name="format-detection" content="telephone=no" />
<style type="text/css">
body{margin:0;padding:8px;}
p{line-height:1.15;margin:0;white-space:pre-wrap;}
ol,ul{margin-top:0;margin-bottom:0;}
img{border:none;}
li>p{display:inline;}
</style>
</head>
<body>
<p style="text-align: left;"><span style="font-size: 26pt;font-weight: bold;"><body[^>]*>((.|\n)*)<\/body></span></p>
<p style="text-align: left;"><br></p>
<p style="text-align: left;"><img height="146" src="image001" style="border:none;" width="449"/></p>
</body>
</html>
Как выяснилось, нам это неудобно, и требуется:
1.Получить значение тега <body> из HTML (без самого тега body), который нам возвращает ПолучитьHTML.
2.Преобразовать получаемые картинки в base64 и заменить на него ключи в теле HTML.
Давайте реализуем функцию, которая получает значение произвольного тега HTML без самого тега - ПолучитьЗначениеТегаHTML(HTML, "body").
Проще всего для этого использовать регулярные выражения, в данном случае, нам подойдёт - <body[^>]*>((.|\n)*)<\/body>.
Давайте разберем его:
- <body[^>]*> - означает, что нам нужно найти в тексте <body>, причем после слова body может идти любой символ, кроме >. Используем жадный алгоритм (без ?), квантификатор *, т.е. нам подходит ноль или более букв после слова body. В итоге алгоритм найдет и <body>, и <body style="max-width: 1920px;background-color: rgb(166,166,166);">
- ((.|\n)*) - означает, что нам подходит любой символ или перевод строки после тега <body>, используем также жадный алгоритм. Скобочки нужны, чтобы выделить значение в отдельную группу (без слова body), которую мы легко сможем получить используя SubMatches. Кстати, если надо найти все теги <p>, то нужно из жадного сделать ленивый, добавив знак вопроса - ((.|\n)*?).
- <\/body> - означает, что шаблон завершается тегом </body>, символ \ экранирует символ /.
Попробовать его работу можно на сайте - https://regex101.com/r/MCgzCo/1/
Итак, наша функция будет выглядеть следующим образом:
Функция ПолучитьЗначениеТегаHTML(HTML, Тег) экспорт
Результат = HTML;
РегулярноеВыражение = _ОбщегоНазначенияКлиентСервер.НовоеРегулярноеВыражение("<"+Тег+"[^>]*>((.|\n)*)<\/"+Тег+">", Истина, Истина, Истина);
Выражения = РегулярноеВыражение.Execute(HTML);
Если Выражения.Count()>0 И Выражения.Item(0).SubMatches.Count()>0 Тогда
Результат = СокрЛП(Выражения.Item(0).SubMatches.Item(0));
КонецЕсли;
Возврат Результат;
КонецФункции
В результате обработки этой функцией нашего HTML мы получаем текст:
<p style="text-align: left;"><span style="font-size: 26pt;font-weight: bold;"><body[^>]*>((.|\n)*)<\/body></span></p>
<p style="text-align: left;"><br></p>
<p style="text-align: left;"><img height="146" src="image001" style="border:none;" width="449"/></p>
Здесь вставленная картинка описывается ключом ("image001") и отправка такой информации на сайт/интернет-магазин/веб-приложение приведёт к отображению страницы без картинки. Чтобы исправить такое положение дел, мы воспользуемся заменой ключей картинок на их значение в формате base64, собственно вот реализация такой процедуры:
Процедура ЗаменитьКартинкиНаBase64(HTML, ВложенияHTML, ОбратноеДействие=Ложь) экспорт
Для Каждого Вложение Из ВложенияHTML Цикл
Картинка = Вложение.Значение; ИмяКартинки = Вложение.Ключ;
Попытка
ПрефиксBase64 = ПолучитьПрефиксBase64(Картинка.Формат());
Исключение
Продолжить;
КонецПопытки;
Если ПустаяСтрока(ПрефиксBase64) Тогда
Продолжить;
КонецЕсли;
ДвоичныеДанныеФайла = Картинка.ПолучитьДвоичныеДанные();
Если ОбратноеДействие Тогда
HTML = СтрЗаменить(HTML, "src="""+ПрефиксBase64+Base64Строка(ДвоичныеДанныеФайла)+"""", "src="""+ИмяКартинки+"");
Иначе
HTML = СтрЗаменить(HTML, "src="""+ИмяКартинки+"", "src="""+ПрефиксBase64+Base64Строка(ДвоичныеДанныеФайла)+"""");
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Теперь мы можем построить нашу итоговую универсальную функцию по сохранению HTML, которая на вход получает форму, объект, вложения HTML и реквизиты формы (типа структура), используемые для HTML редактирования.
Процедура СохранитьHTMLПоля(Форма, Объект, СтруктураВложенийHTML, Реквизиты)экспорт
Если СтруктураВложенийHTML=Неопределено Тогда
СтруктураВложенийHTML = Новый Структура();
КонецЕсли;
Для Каждого Реквизит Из Реквизиты Цикл
ВложенияHTMLРеквизита = Новый Структура();
Форма[Реквизит.Значение].ПолучитьHTML(Объект[Реквизит.Ключ], ВложенияHTMLРеквизита);
СтруктураВложенийHTML.Вставить(Реквизит.Ключ, ВложенияHTMLРеквизита);
//сохраним только тело
Объект[Реквизит.Ключ] = ПолучитьЗначениеТегаHTML(Объект[Реквизит.Ключ], "body");
ЗаменитьКартинкиНаBase64(Объект[Реквизит.Ключ], ВложенияHTMLРеквизита);
КонецЦикла;
КонецПроцедуры
Пример вызова функции, например, из ПередЗаписьюНаСервере: _ВебАппHTML.СохранитьHTMLПоля(ЭтаФорма, ТекущийОбъект, ВложенияHTML, Новый Структура("_ОписаниеHTML", "_ОписаниеHTMLФорма"));
_ОписаниеHTML - это реквизит метаданных типа неограниченная строка
_ОписаниеHTMLФорма - это реквизит формы типа ФорматированныйДокумент.
Отлично, мы сохранили с Вами данные, а теперь при открытии в следующий раз их нужно восстановить, т.е. выполнить обратное действие по замене картинок на их ключи. Зачем? А чтобы мы могли потом редактировать теги HTML без пролистывания большого значения base64.
Для этого реализуем универсальную функцию - ВосстановитьHTMLПоля(Форма, Объект, СтруктураВложенийHTML, Реквизиты).
Процедура ВосстановитьHTMLПоля(Форма, Объект, СтруктураВложенийHTML, Реквизиты) экспорт
Если НЕ ТипЗнч(СтруктураВложенийHTML)=Тип("Структура") Тогда
СтруктураВложенийHTML = Новый Структура();
КонецЕсли;
ВложенияHTMLРеквизита = Неопределено;
Для Каждого Реквизит Из Реквизиты Цикл
СтруктураВложенийHTML.Свойство(Реквизит.Ключ, ВложенияHTMLРеквизита);
Если НЕ ТипЗнч(ВложенияHTMLРеквизита)=Тип("Структура") Тогда
ВложенияHTMLРеквизита = Новый Структура();
КонецЕсли;
HTML = Объект[Реквизит.Ключ];
ЗаменитьКартинкиНаBase64(HTML, ВложенияHTMLРеквизита, Истина);
Форма[Реквизит.Значение].УстановитьHTML(HTML, ВложенияHTMLРеквизита);
КонецЦикла;
КонецПроцедуры
Бонусом, как и обещал, мы теперь с Вами можем переходить вот в такой режим редактирования:
Все это легко сделать с помощью разработанных ранее универсальных функций:
&НаСервере
Процедура УправлениеРедактированиемHTML()
Если Объект._РежимРедактированияТекста Тогда
ВложенияHTMLРеквизита = Новый Структура();
_ОписаниеHTMLФорма.ПолучитьHTML(Объект._ОписаниеHTML, ВложенияHTMLРеквизита);
Объект._ОписаниеHTML = _ВебАппHTML.ПолучитьЗначениеТегаHTML(Объект._ОписаниеHTML, "body");
Если НЕ ТипЗнч(ВложенияHTML)=Тип("Структура") Тогда
ВложенияHTML = Новый Структура();
КонецЕсли;
ВложенияHTML.Вставить("_ОписаниеHTML", ВложенияHTMLРеквизита);
Иначе
_ОписаниеHTMLФорма.УстановитьHTML(Объект._ОписаниеHTML, ВложенияHTML._ОписаниеHTML);
КонецЕсли;
КонецПроцедуры
UPD.
Функция НовоеРегулярноеВыражение(Паттерн = "", ИгнорироватьРегистр = Истина, МногострочныйРежим = Ложь, ВсеВхождения = Истина) экспорт
РегулярноеВыражение = Новый COMОбъект("VBScript.RegExp");
РегулярноеВыражение.Pattern = Паттерн;
РегулярноеВыражение.IgnoreCase = ИгнорироватьРегистр;
РегулярноеВыражение.MultiLine = МногострочныйРежим;
РегулярноеВыражение.Global = ВсеВхождения;
Возврат РегулярноеВыражение;
КонецФункции
Надеюсь, моя публикация была Вам полезна и сэкономит Ваше время, ссылка на все публикации SizovE.
Подписывайтесь на мой канал (наверху), будет много интересного бесплатного контента :)