Как стать автором
Обновить

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

Время на прочтение 8 мин
Количество просмотров 12K

В Альфастраховании мы активно пользуемся "Вики", движком которого выступает 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 — что, на мой взгляд, можно с их помощью действительно улучшить.


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

Теги:
Хабы:
+11
Комментарии 3
Комментарии Комментарии 3

Публикации

Информация

Сайт
alfastrah.ru
Дата регистрации
Дата основания
Численность
5 001–10 000 человек
Местоположение
Россия