Atlassian Confluence: расширяем на python

от автора

В Альфастраховании мы активно пользуемся «Вики», движком которого выступает Atlassian Confluence. Когда я первый раз с ним всерьез столкнулся (в попытке создать в нем контент), мне в нем не хватило «динамичности» — хотелось иметь возможность программно формировать части страниц, взаимодействовать с другими системами и т.п.

Некоторое время бился головой в разные стены, но потом увидел, что «в доме не было одной стены». Хочу поделиться опытом — как можно добавить динамики в Confluence. Надеюсь, это будет полезно тем, кто им пользуется. И, как обычно, всем любознательным.

Динамичный вики

Изначально Confluence предполагает один универсальный способ расширения своей фукнциональности — плагины. Наверное, он хорош, есть один лишь недостаток — высоченный порог вхождения (нужно много чему научиться).

После непродолжительного (по космическим меркам) размышления нашлось еще два достаточно простых способа расширения его функциональности: стандартные макро «HTML» и «HTML Include», в этой статье я более подробно остановлюсь на последнем.

Принцип действия у этих способов один — в страницу Confluence встраивается HTML код, он может быть статическим (что тоже может быть интересным, например, для нестандартного форматирования страниц), может быть динамическим (включая серверные компоненты).

Пример: запрос на согласование

Давайте рассмотрим предложенный способ расширения возможностей Confluence на простом примере — списке согласования документа.

Что мы хотим сделать: добавить на страницу список согласования с тем, чтобы всем было видно — кто должен согласовать документ, был ли он согласован и, если был, то когда.

Для удобства сделаем элемент списка кнопкой и напишем в ней фамилию согласующего. Кнопка будет активной в случае, если страницу просматривает согласующий, и не активной во всех остальных случаях.

После согласования кнопка «перестанет быть кнопкой» — вместо кнопки после согласования отрисуем на странице факт согласования в виде текста (фамилия согласующего и дата согласования). В самом черновом варианте (без оформления) может выглядеть как-то так:

image

Схема взаимодействия

image

Комментарии — как это работает:

  • страница в Confluence содержит несколько макро «html include» — фактически HTTP GET запросов с параметрами (рассмотрим подробнее ниже)
  • при отрисовке страницы эти запросы (python скрипты) исполняются на сервере приложений, результат (сгенерированный HTML) отрисовывается на странице
  • в процессе своей работы скрипт, например, обращается в базу данных с историей подписания страниц, если страница еще не была «подписана» подписантом — HTML будет содержать кнопку (все, как описано выше)
  • при нажатии на кнопку происходит submit — вызов другого python скрипта (скрипта обработки факта согласования)
  • этот скрипт сохраняет информацию о факте подписания в базу данных и перенаправляет пользователя обратно на исходную страницу (при отрисовки которой уже будет учтен факт подписания — нажатия на кнопку «подписать»)

Немного сложно словами, давайте попробуем кодом прояснить.

Итак нам нужно создать два скрипта

  • скрипт формирования кнопки «подписать» (буду называть его скрипт «кнопка»)
  • скрипт отработки факта подписания (реакция на нажатие кнопки — буду называть его скрипт «обработчик»)

Давайте создадим их, начнем с кнопки.

Скрипт «кнопка»

Схематично, как работает скрипт «кнопка»:

date = getSignDate() if date:     # отрисвываем текст с датой согласования и пр. else:     if user==signee:         # отрисовываем активную кнопку     else:         # отрисовываем inactive кнопку

Скрипт должен представлять из себя валидный HTML документ, тело которого в самом минимальном варианте представляет из себя форму

  • action формы содержит URL скрипта «обработчик»
  • активная кнопка — это submit
  • hidden поля формы содержат дополнительные параметры для обработчика (в нашем случае — имя страницы и логин подписанта)

Примерный вид скрипта (для краткости выкинул все лишнее — см. полный работающий код на github)

form = cgi.FieldStorage() signee = form["signee"].value # логин согласующего (кто должен согласовать) actual = form["actual"].value # логин текущего пользователя id = form["id"].value # имя страницы, которую необходимо согласовать  resHtml = """ <!DOCTYPE HTML> <html> <head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /></head> <body> <form name="input" action="http://172.16.108.216/misc/sign_proc.py" method="get"> """  server, token = wikiLogin()           # логинимся в Confluence userName = getUserName(token, signee) # получаем имя текущего пользователя по его логину  res = getSignDate(id, signee)         # получаем дату согласования страницы if res: # согласование было получено     resHtml += "<p>Согласовано ({0}, {1})</p>".format(userName, res) # отрисовываем текст else:   # согласования не было     if signee.lower() == actual.lower(): # текущий пользователь совпадает с согласующим - отрисовываем кнопку         resHtml += '<input type="hidden" name="id" value="{0}">'.format(id)         resHtml += '<input type="hidden" name="signee" value="{0}">'.format(signee)         resHtml += "Требуется Ваше согласование <input type=\"submit\" value=\"{0}\">".format(userName)     else: # текущий пользователь не есть согласующий - отрисовываем неактивную кнопку         resHtml += "Требуется согласование пользователем <input disabled type=\"submit\" value=\"{0}\">".format(userName)  resHtml += "</form></body></html>"  # выводим собранный HTML sys.stdout.buffer.write(b'Content-Type: text/html;charset=utf-8\n\n') sys.stdout.buffer.write(resHtml.encode("utf-8"))

Скрипт «обработчик»

Задача обработчика очень проста — зафиксировать факт нажатия кнопки «согласовать». После чего перенаправить обратно на страницу, откуда пришел запрос — при отрисовке страницы уже будет учтен факт согласования (см. описание выше).

Пример (сокращенный) скрипта «обработчика» (полный вариант — см. github)

form = cgi.FieldStorage() signee = form["signee"].value # логин согласующего id = form["id"].value         # имя страницы, которую необходимо согласовать  addSignDate(id, signee)       # фиксируем факт согласования  resHtml = """ <html> <head> <meta http-equiv="refresh" content="5;url=http://wiki.alfastrah.ru/display/DIT/{0}"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Согласование отработано</title> </head> <body> <p>Запрос на согласование страницы <b>{0}</b> пользователем <b>{1}</b> обработан. <br>Сейчас Вы будете перенаправлены обратно на страницу...</p> </body> </html> """.format(id, signee)  sys.stdout.buffer.write(b'Content-Type: text/html;charset=utf-8\n\n') sys.stdout.buffer.write(resHtml.encode("utf-8"))

Страница в Confluence

Страница в Confluence содержит три макро HTML include с разными параметрами (см. код ниже) и в самом простом варианте выглядит так:

image

Код страницы в формате wiki markup — здесь можно видеть параметры

<h1>Важный документ</h1> <p>Важный текст, требующий согласования</p> <h1>Список согласования</h1> <ul>   <li>     <ac:structured-macro ac:macro-id="..." ac:name="html-include" ac:schema-version="1">       <ac:parameter ac:name="url">         <ri:url ri:value="http://z14-0510-wiksap.vesta.ru/misc/sign_btn.py?signee=boss&amp;actual=korolevmv&amp;id=wikitools_sign"/>       </ac:parameter>     </ac:structured-macro>   </li>   <li>     <ac:structured-macro ac:macro-id="..." ac:name="html-include" ac:schema-version="1">       <ac:parameter ac:name="url">         <ri:url ri:value="http://z14-0510-wiksap.vesta.ru/misc/sign_btn.py?signee=korolevmv&amp;actual=korolevmv&amp;id=wikitools_sign"/>       </ac:parameter>     </ac:structured-macro>   </li>   <li>     <ac:structured-macro ac:macro-id="..." ac:name="html-include" ac:schema-version="1">       <ac:parameter ac:name="url">         <ri:url ri:value="http://z14-0510-wiksap.vesta.ru/misc/sign_btn.py?signee=maxvar&amp;actual=korolevmv&amp;id=wikitools_sign"/>       </ac:parameter>     </ac:structured-macro>   </li> </ul>

Немного о настройке

Для того, чтобы описанная схема заработала необходимо учесть следующее

Наличие макроса HTML Include

Иногда администраторы его «прячут» — уж очень много дополнительных хлопот он доставляет (обратная сторона возможностей).

Этот макрос является бесплатным и входит в Confluence — если у Вас его нет, обратитесь к администраторам, пусть поищут…

Белые списки Confluence

Confluence не будет выполнять скрипты, размещенные на неизвестных источниках, хост, на котором размещен скрипт должен быть в так называемых «белых списках» (обратитесь к админам). Это относится только к скрипту «кнопки» — скрипт обработчика вызывается уже кнопкой, на нее ограничения Confluence не действуют.

Выполнимость скриптов

Скрипты (кнопки и обработчика) должны быть выполнимыми — скопируйте урл в браузер, он должен отработать и сформировать HTML, если этого не произошло — отлаживайтесь.

Ошибки в скриптах

Если любой из скриптов «падает» с ошибкой — Вы не увидите ничего хорошего, отладьтесь как следует перед публикацией в Confluence.

Параметры скриптов

Пытливый ум мог обратить внимание на параметры скриптов — с одной стороны, они избыточны (например, имя страницы, на которой размещена кнопка согласования — она известна, зачем ее заполнять?). С другой стороны — небезопасны («злоумышленник» в нашем примере может изменить имя согласующего на свое или удалить кнопку согласования вовсе).

Избыточность можно «побороть» пользовательскими макросами (я долгое время удивлялся — чем они могут быть практически полезны? Напишу кратко в отдельной статье). Безопасность в Confluence обеспечивается историей страницы — все «ходы» записаны, можно что угодно менять, в Confluence остается замечательный «аудит след», позволяющий детально разобраться — кто что и когда менял.

Взаимодействие со страницей

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

Еще примеры

Для чего еще мы использовали эти макросы (как идеи — вдруг что-то окажется полезным), очень кратко

Оформление страницы: если хочется оформить страницу нестандартно — размечаем с помощью CSS, который содержится в HTML блоке

Календарь отпусков: используем какой-нибудь простой JS календарь, наполняем его данными из JSON, который редактируем прямо в коде страницы, получаем красивую табличку с отпусками в разбивке (год, месяц, неделя).

Печать карточек задач для скрам-доски: задаем в форме прямо на вики странице (в HTML блоке) параметры (номера задач в Jira и дополнительная атрибутика), серверная часть формирует карточки в виде, пригодном для отправки на принтер.

Управление портфелем проектов: была у нас и такая затея. Прямо в вики вели скор карты (скор карта — это специальным образом размеченная страница), по этим скор картам собирали крупноблочный план, который красиво отрисовывался в виде гант-диаграммы.

Глоссарий: возможность визуализировать термины — пояснения к отдельным словам страницы — из общего словаря и выдавать словарные статьи в виде «попапов». Сам словарь автоматически собирается в нижней части страницы.

Графики: если нужно отрисовать какой-то несложный график, то очень просто это сделать, встроив HTML блок с каким-либо из стандартных пакетов (Google Charts, HighCharts, etc)

Таблицы: поверх любой таблицы в Confluence можно «повесить» Datatable — получаем фильтруемую таблицу с «пагинацией», очень красиво и удобно.

Список можно продолжать достаточно долго, в процессе эксплуатации была замечено одно существенное неудобство: ошибки в серверном кода достаточно плохо ловятся — 500 ошибка плохо визуализируется и практически не содержит информации. В-остальном, экспериментируйте — это достаточно безопасно.

Чуть подытожим

Описанный выше простой способ повышеия динамичности Confluence позволяет решить много разных задач. Для его использования не требуется особых инструментов и навыков. Использование достаточно безопасно. Рекомендую.

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

В программировании можно все — вопрос только времени и мотивации.


ссылка на оригинал статьи https://habr.com/ru/company/alfastrah/blog/460929/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *