В Atlassian Confluence есть замечательный функционал сравнения версий страницы. Им удобно пользоваться, но выйти за пределы истории изменений не удастся. Как же быть? Поиск готового плагина в Marketplace результатов не дал и было принято решение написать свой.
Нам понадобятся:
Установим SDK и выполним скрипт
Вводим:
Для сборки можно воспользоваться maven-ом и зависимостями из SDK, но проще подключить дополнительные репозитории в своем settings.xml.
Откроем получившийся проект в IDE, запустим Confluence в режиме отладки

и проверим работу локальной установки по адресу http://localhost:1990/confluence/. Логин admin, пароль admin. В базе уже создано демонстрационное пространство и несколько статей.
Начнем с меню. Добавим в дескриптор плагина atlassian-plugin.xml модуль web-item.
Где:
Добавляем в diff-page.properties строку:
Если нужна поддержка переводов — создадим дополнительные property, например, для русской локализации файл будет называться diff-page_ru_RU.properties.
Выполним

Для выбора страницы удобно использовать модальное окно. Продолжим править atlassian-plugin.xml
Добавим немного ресурсов:
Где:
Определим обработчики наших url-ов:
Где:
Создадим новый пакет com.kshch.confluence.plugins.diff.page.action и добавим в него класс DiffPagePopupAction, расширяющий ConfluenceActionSupport с 2-мя полями.
Сгенерируем к ним геттеры и сеттеры. В поле spaceKey будет содержаться ключ текущего пространства, а в sourcePageId — id текущей страницы.
Займемся фронтом. Для создания всплывающего окна воспользуемся элементом dialog2 из фреймворка AUI.
Добавим в диалог форму, кнопку submit и input-ы с css классами autocomplete-space и autocomplete-page, что позволит без дополнительных усилий организовать выбор пространства и страницы. Сохраним полученный результат в /templates/diff-page-popup.vm.
Для отображения окна создадим в ресурсах /js/diff-page-popup.js со следующим содержимым:
Его задача переопределить стандартное событие click и показать нам всплывающее окно, а не осуществить переход по ссылке.
Выполним

Приступим к реализации основного функционала. Добавим в пакет com.kshch.confluence.plugins.diff.page.action еще один класс DiffPageAction, расширяющий ConfluenceActionSupport и реализующий интерфейс PageAware со следующими полями.
Где:
Сгенерируем геттеры и сеттеры, реализуем все методы, кроме getPage, как предлагает IDE по умолчанию. В полях sourcePageId, spaceKey и destinationPageName будут содержаться данные, которые пришли из формы внутри dialog2.
Переопределим метод execute своим. Именно в нем и заключена основная логика работы плагина.
Остается реализовать frontend сравнения 2-х страниц. Он гораздо проще всплывающего окна.
Создадим в ресурсах /templates/diff-page.vm. В нем, кроме верстки, реализуем простейшую проверку на ошибки.
Написание плагина закончено. Еще раз выполним

Полный код проекта доступен на GitHub.
Инструменты
Нам понадобятся:
- Oracle JDK или AdoptOpenJDK 1.8, работа с OpenJDK официально не поддерживается;
- Atlassian SDK — можно скачать с официального сайта для Windows и Linux/Mac без СМС и регистрации;
- Apache Maven;
- Любимая среда разработки, у меня IntelliJ IDEA.
Создадим проект
Установим SDK и выполним скрипт
atlas-create-confluence-plugin.Вводим:
- group-id: com.kshch.confluence.plugins
- artifact-id: diff-page
- version: 1.0.0-SNAPSHOT
- package: com.kshch.confluence.plugins.diff.page
Для сборки можно воспользоваться maven-ом и зависимостями из SDK, но проще подключить дополнительные репозитории в своем settings.xml.
<repository> <id>atlassian-public</id> <url>https://packages.atlassian.com/maven/repository/public</url> <snapshots> <enabled>true</enabled> <updatePolicy>never</updatePolicy> <checksumPolicy>warn</checksumPolicy> </snapshots> <releases> <enabled>true</enabled> <checksumPolicy>warn</checksumPolicy> </releases> </repository> <pluginRepository> <id>atlassian-public</id> <url>https://maven.atlassian.com/repository/public</url> <releases> <enabled>true</enabled> <checksumPolicy>warn</checksumPolicy> </releases> <snapshots> <updatePolicy>never</updatePolicy> <checksumPolicy>warn</checksumPolicy> </snapshots> </pluginRepository>
Откроем получившийся проект в IDE, запустим Confluence в режиме отладки
confluence:debug
и проверим работу локальной установки по адресу http://localhost:1990/confluence/. Логин admin, пароль admin. В базе уже создано демонстрационное пространство и несколько статей.
Немного функционала
Начнем с меню. Добавим в дескриптор плагина atlassian-plugin.xml модуль web-item.
<web-item key="diff-page-menu" name="Diff Page" section="system.content.action/secondary" weight="300"> <description>Add diff item to drop-down menu</description> <label key="diff.page.menu.name"/> <link linkId="diff-page">/plugins/diffPage/diffPagePopup.action?spaceKey=${space.key}&sourcePageId=${page.id}</link> <condition class="com.atlassian.confluence.plugin.descriptor.web.conditions.HasPageCondition"/> </web-item>
Где:
- атрибут section — определяет место появления пункта меню;
- атрибут weight — отвечает за порядок сортировки;
- атрибут key тега label — ключ, по которому в файлах ресурсов находится название отображаемого пункта меню;
- тег link — содержит ссылку, а атрибут linkId ее id;
- condition — определяет условие появления, в нашем случае только на страницах.
Добавляем в diff-page.properties строку:
diff.page.menu.name=Diff pageЕсли нужна поддержка переводов — создадим дополнительные property, например, для русской локализации файл будет называться diff-page_ru_RU.properties.
Выполним
maven package. Плагин будет автоматически перезагружен и появится наш пункт меню.
Модальное окно
Для выбора страницы удобно использовать модальное окно. Продолжим править atlassian-plugin.xml
Добавим немного ресурсов:
<web-resource key="diff-page-popup-resources" name="diff-page Popup Web Resources"> <dependency>com.atlassian.auiplugin:ajs</dependency> <dependency>com.atlassian.auiplugin:dialog2</dependency> <resource type="download" name="diff-page-popup.js" location="/js/diff-page-popup.js"/> <context>page</context> </web-resource>
Где:
- тег dependency — зависимость;
- в атрибутах resource указаны тип ресурса, имя и расположение на файловой системе;
- тег context — определяет видимость в зависимости от контекста.
Определим обработчики наших url-ов:
<xwork name="Diff Page Action" key="diff-page-action"> <description>Diff page action</description> <package name="diff-page-package" extends="default" namespace="/plugins/diffPage"> <default-interceptor-ref name="defaultStack"/> <action name="diffPage" class="com.kshch.confluence.plugins.diff.page.action.DiffPageAction"> <result name="success" type="velocity">/templates/diff-page.vm</result> </action> <action name="diffPagePopup" class="com.kshch.confluence.plugins.diff.page.action.DiffPagePopupAction"> <result name="success" type="velocity">/templates/diff-page-popup.vm</result> </action> </package> </xwork>
Где:
- атрибут name тега action — определяет название ссылки, а class — java class для ее обработки;
- тег result — содержит ссылку на шаблон, в нашем случае velocity.
Создадим новый пакет com.kshch.confluence.plugins.diff.page.action и добавим в него класс DiffPagePopupAction, расширяющий ConfluenceActionSupport с 2-мя полями.
private String spaceKey; private Long sourcePageId;
Сгенерируем к ним геттеры и сеттеры. В поле spaceKey будет содержаться ключ текущего пространства, а в sourcePageId — id текущей страницы.
Займемся фронтом. Для создания всплывающего окна воспользуемся элементом dialog2 из фреймворка AUI.
Добавим в диалог форму, кнопку submit и input-ы с css классами autocomplete-space и autocomplete-page, что позволит без дополнительных усилий организовать выбор пространства и страницы. Сохраним полученный результат в /templates/diff-page-popup.vm.
<section role="dialog" id="diff-page-popup" class="aui-layer aui-dialog2 aui-dialog2-medium" aria-hidden="true" data-aui-remove-on-hide="true"> <form action="$action.getBootstrapManager().getWebAppContextPath()/plugins/diffPage/diffPage.action" method="get" class="aui"> ... <input type="hidden" name="sourcePageId" value="$action.getSourcePageId()"> <input class="text autocomplete-space" type="text" id="diff-page-space" name="spaceKey" data-max="10" data-none-message="$action.getText("diff.page.popup.no.result")" placeholder="$action.getText("diff.page.popup.select.space")" value="$action.getSpaceKey()" data-template="{key}"> <input type="text" class="text autocomplete-page" name="destinationPageName" data-max="10" placeholder="$action.getText("diff.page.popup.select.page")" data-none-message="$action.getText("diff.page.popup.no.result")"> <button id="dialog-diff-button" class="aui-button aui-button-primary">$action.getText("diff.page.popup.diff")</button> ... </form> </section>
Для отображения окна создадим в ресурсах /js/diff-page-popup.js со следующим содержимым:
(function ($) { $(function () { AJS.$('#diff-page').unbind('click'); AJS.$('#diff-page').bind("click", function (e) { e.preventDefault(); var link = AJS.$(this); AJS.$.get(link.attr('href'), function (response) { AJS.$('.aui-page-panel').after(response); AJS.dialog2("#diff-page-popup").show(); Confluence.Binder.autocompletePage(AJS.$("#diff-page-popup-binder")); }); return false; }); }); })(AJS.$);
Его задача переопределить стандартное событие click и показать нам всплывающее окно, а не осуществить переход по ссылке.
Выполним
maven package и попробуем выбрать страницу и пространство. Подсказчик активируется после набора 2-х символов.
Основной функционал
Приступим к реализации основного функционала. Добавим в пакет com.kshch.confluence.plugins.diff.page.action еще один класс DiffPageAction, расширяющий ConfluenceActionSupport и реализующий интерфейс PageAware со следующими полями.
private Long sourcePageId; private Long destinationPageId; private String spaceKey; private String destinationPageName; private String sourcePageTitle; private String destinationPageTitle; private String diff; private Page sourcePage; private Differ differ; private final PageManager pageManager;
Где:
- sourcePageId — id исходной страницы;
- destinationPageId — id сравниваемой страницы;
- spaceKey — ключ пространства;
- destinationPageName — имя сравниваемой страницы;
- sourcePageTitle — заголовок исходной страницы;
- destinationPageTitle — заголовок сравниваемой страницы;
- diff — результат сравнения.
Сгенерируем геттеры и сеттеры, реализуем все методы, кроме getPage, как предлагает IDE по умолчанию. В полях sourcePageId, spaceKey и destinationPageName будут содержаться данные, которые пришли из формы внутри dialog2.
@Override public AbstractPage getPage() { return this.sourcePage; }
Переопределим метод execute своим. Именно в нем и заключена основная логика работы плагина.
@Override public String execute() throws Exception { if (this.sourcePageId != null && this.spaceKey != null && this.destinationPageName != null) { this.sourcePage = this.pageManager.getPage(this.sourcePageId); Page destinationPage = this.pageManager.getPage(this.spaceKey, this.destinationPageName); if (this.sourcePage != null && destinationPage != null) { this.destinationPageId = destinationPage.getId(); this.sourcePageTitle = this.sourcePage.getTitle(); this.destinationPageTitle = destinationPage.getTitle(); this.diff = this.differ.diff(this.sourcePage, destinationPage); } } return super.execute(); }
Остается реализовать frontend сравнения 2-х страниц. Он гораздо проще всплывающего окна.
Создадим в ресурсах /templates/diff-page.vm. В нем, кроме верстки, реализуем простейшую проверку на ошибки.
#if ($action.getDiff()) #diffBody() #else <div class="aui-message aui-message-error"> <p class="title"> <strong>$action.getText("diff.page.error")</strong> </p> <p>$action.getText("diff.page.error.message")</p> </div> #end
Написание плагина закончено. Еще раз выполним
maven package, выберем страницу и насладимся полученным результатом.
Полный код проекта доступен на GitHub.
