Pull to refresh

Учим MS SQL Server Reporting Services 2008 R2 показывать HTML в отчетах

Reading time 4 min
Views 30K


Проблема


Недавно столкнулся с необходимостью показать в отчете SSRS 2008 R2 HTML-таблицы, хранящиеся в базе данных.
И здесь на сцену статисты выносят заботливо подготовленные «грабли». Все дело в том, что Report Manager в SSRS 2008 R2 поддерживает только ограниченное количество тегов HTML, и табличные в их число не входят.
Согласно официальной документации, поддерживаются только следующие теги:
  • Ссылки: A HREF
  • Шрифт: FONT
  • Заголовки, блочные элементы: H{n}, DIV, SPAN,P, LI
  • Форматирование текста: B, I, U, S
  • Списки: OL, UL, LI

Список атрибутов CSS также сильно урезан.

После продолжительного поиска в интернете, стало понятно, что ни один из вариантов, предлагаемых сообществом разработчиков, меня не устроит.

Исследование


Варианты предлагались следующие:
  • Рендерить HTML в картинку на стороне сервера с помощью специально разработанного компонента (Custom Report Items)
  • Заменить теги на поддерживаемые с симуляцией таблиц (нужно писать скрипт в самом отчете)

В одном из обсуждений натолкнулся на подсказку — Javascript на стороне клиента. Готового решения не было.
Начал с попыток проанализировать какой код выполняется на стороне клиента при отображении отчета. Подсмотрел в код отчета в браузере и нашел внедренный скрипт ReportingServices.js:
<script language="JScript" src="/Reports_SQLEXPRESS/js/ReportingServices.js" type="text/Javascript"></script>

Сам скрипт у меня на сервере лежал здесь:
c:\Program Files\Microsoft SQL Server\MSRS10_50.SQLEXPRESS\Reporting Services\ReportManager\js\ReportingServices.js

Нашел пару статей на тему Javascript-инъекции с помощью данного скрипта, но основная проблема была в том, что мои модификации выполнялись только один раз при отображении первой страницы отчета, и при переходе на следующие страницы интерфейсные элементы уже не перерисовывались (AJAX).
Попытки глубже разобраться в принципах работы SSRS продолжились изучением фрагментов кода основных DLL-ей сервера с помощью DevExtras CodeReflect и препарированием отчетов в IE9 developer tool/Opera Dragonfly.
В результате нашел следующее:
  • Для WebForms которые используют Ajax, есть ряд событий, определяющих жизненный цикл страницы
  • Одно из таких событий — Load, вызывается каждый раз, при загрузке страницы и всех вызовов Ajax
  • Подробности в статье Ajax Client Life-Cycle Events: http://msdn.microsoft.com/en-us/library/bb386417.aspx
Для того чтобы добавить свой обработчик — нужно создать JS-функцию pageLoad, которая автоматически будет выполняться при возникновении события Load:
function pageLoad(sender, args) {
	//здесь наш код, например alert("Работает!");
}
Вставляю данную конструкцию в самое начало скрипта ReportingServices.js. Работает!

Решение


Собственно, дальше уже головоломка собралась и получилось следующее решение:
  1. Добавляем в начало скрипта %SSRS Install Dir%\Reporting Services\ReportManager\js\ReportingServices.js следующую функцию:
    function pageLoad() {
       var toDecode = document.getElementsByTagName("div");    // получаем все DIVы
       for (var i = 0; i < toDecode.length; ++i){       // перебираем
          if(toDecode[i].innerHTML.indexOf("HTMLInject:") == 0){   // DIV содержит префикс?
             Decoded = toDecode[i].innerText || toDecode[i].textContent; // поддержка IE, Opera, FF
             toDecode[i].innerHTML = Decoded.slice(11);     //  вырезаем префикс & TXT --> HTML
          }
       }
    }
    

  2. Теперь, чтобы вывести поле как HTML, в свойствах placeholder'а в поле Value надо написать следующее:
    ="HTMLInject:"+Fields!FieldName.Value
    
    , где FieldName — имя поля датасета, содержащего HTML.


    При этом свойство «General\Markup type» самого поля нужно поставить в значение "None — Plain text only".

  3. При необходимости изменения стилей HTML — можно внести правки в этот CSS: %SSRS Install Dir%\ReportingServices\ReportManager\Styles\ReportingServices.css Однако, мне повезло и мои HTML таблицы были отформатированы классами, которые я и добавил в CSS-файл.

А теперь примеры


Так по-умолчанию показывается поле («None — Plain text only»):


Так показывает таблицы, если тип разметки поставить в «HTML — Interpret HTML tags as styles»:

Мой вариант:


Про/контра такого решения


Достоинства:
  • Работает
  • Легко и быстро сделать

Недостатки:
  • Работает только при отображении отчетов в веб-интерфейсе. В других форматах отчетов (Excel, PDF, Word...) отображается весь HTML с тегами. Как обходное решение можно использовать несколько столбцов для одного поля и признак видимости "Show or hide based on an expression" с формулой "=Globals!RenderFormat.Name"(то есть показывать только один столбец, в зависимости от формата отчета). Далее, в отчетах для других форматов уже можно разработать функцию для плейсхолдера, чтобы как-то причесать HTML (например заменой на поддерживаемые теги, или отображением как картинки).
  • Слетает навигация по меню отчета (DocumentMap) — то есть якоря в документе расставляются некорректно и для отрендеренных в HTML элементов может происходить смещение на величину, составляющую разницу между тем, как собирался показать SSRS наше поле, и как в итоге показал.
  • Быстродействие на сложных отчетах и медленных клиентах
  • XSS

Выводы


Вот так, с помощью нехитрых приспособлений, можно научить SSRS показывать HTML… Но ЗАЧЕМ?!
Сотни постов, от растерянных до гневных, переполняют интернеты из-за такой мелочи.
Даже BIRT это уже давно умеет, а новейшая версия SSRS2012 еще нет (пруфлинк: http://technet.microsoft.com/en-us/library/ff519562.aspx).
Есть мысли, что MS реализовала показ HTML в таком ограниченном виде чтобы не пострадала безопасность/стабильность системы отчетности, но похоже, что все альтернативные решения напоминают пресловутый «троллейбус».

UPD:
Про рендеринг HTML в картинку — вот на основе этого можно попробовать разработать Custom report Item.
Tags:
Hubs:
+6
Comments 7
Comments Comments 7

Articles