Хабрахабр

Как использовать soy, requirejs, backbone js в плагинах для Atlassian Jira

Мы будем использовать библиотеки soy, requirejs, backbone js для отображения пользовательского интерфейса. В этой статье разработаем плагин, который будет сохранять настройки плагина в Jira. Soy, requirejs, backbone js это встроенные в Jira библиотеки.
Цель статьи состоит в том, чтобы показать как можно использовать встроенные средства Jira для разработки пользовательского интерфейса.

Параметры будут вводиться на двух экранах (по два параметра на каждом экране). Разработанный плагин будет содержать модуль webwork для сохранения параметров плагина в Jira. Исходный код плагина можно посмотреть вот тут. Далее параметры будут упаковываться в json, который и будет сохраняться в Jira.

Создадим скелет плагина

Откроем терминал и выполним команду ниже:
atlas-create-jira-plugin

Ответим на вопросы в терминале вот так:

Define value for groupId: : ru.matveev.alexey.jira.tutorial.webworkui
Define value for artifactId: : webwork-soy-require-backbone
Define value for version: 1.0.0-SNAPSHOT: : Define value for package: ru.matveev.alexey.jira.tutorial.webworkui: : Y: : Y

Внесем изменения в pom.xml

После создания скелета плагина необходимо внести изменения для корректной работы atlassian-spring-scanner 2.

0. Установим версию atlassian-spring-scanner в 2. 0:

<atlassian.spring.scanner.version>2.0.0</atlassian.spring.scanner.version>

Изменим scope зависимости atlassian-spring-scanner-annotation с compile на provided:

<dependency> <groupId>com.atlassian.plugin</groupId> <artifactId>atlassian-spring-scanner-annotation</artifactId> <version>$</version> <scope>provided</scope>
</dependency>

Удалим зависимость atlassian-spring-scanner-runtime.

Создадим сервис для получения и сохранения настроек плагина

Сначала создадим интерфейс для управления настройками плагина.

src/main/java/ru/matveev/alexey/jira/tutorial/webworkui/api/PluginSettingService.java

package ru.matveev.alexey.jira.tutorial.webworkui.api; public interface PluginSettingService { String getConfigJson(); void setConfigJson(String json);
}

Теперь сделаем реализацию интерфейса.

src/main/java/ru/matveev/alexey/jira/tutorial/webworkui/impl/PluginSettingServiceImpl.java

package ru.matveev.alexey.jira.tutorial.webworkui.impl; import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;
import com.atlassian.sal.api.pluginsettings.PluginSettings;
import com.atlassian.sal.api.pluginsettings.PluginSettingsFactory;
import ru.matveev.alexey.jira.tutorial.webworkui.api.PluginSettingService; import javax.inject.Inject;
import javax.inject.Named; @Named
public class PluginSettingServiceImpl implements PluginSettingService { public final PluginSettings pluginSettings; private static final String PLUGIN_STORAGE_KEY = "ru.matveev.alexey.jira.tutorial.webworkui."; private static final String CONFIG_JSON = "configjson"; @Inject public PluginSettingServiceImpl(@ComponentImport PluginSettingsFactory pluginSettingsFactory) { this.pluginSettings = pluginSettingsFactory.createGlobalSettings(); } private void setSettingValue(String settingKey, String settingValue) { this.pluginSettings.put(PLUGIN_STORAGE_KEY + settingKey, settingValue != null?settingValue:""); } private String getSettingValue(String settingKey) { return pluginSettings.get(PLUGIN_STORAGE_KEY + settingKey) != null?pluginSettings.get(PLUGIN_STORAGE_KEY + settingKey).toString():""; } @Override public String getConfigJson() { return getSettingValue(CONFIG_JSON); } @Override public void setConfigJson(String json) { setSettingValue(CONFIG_JSON, json); }
}

Методы getConfigJson и setConfigJson отвечают за получение и сохранение параметра в формате json.

Создадим webwork для управления настройками плагина

Откроем терминал в папке плагина и выполним команду ниже:
create-atlas-jira-plugin-module

На вопросы в терминале отвечаем следующим образом:

Choose a number (1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/33/34): 31
Enter Plugin Module Name My Webwork Module: : Config
Show Advanced Setup? (Y/y/N/n) N: : Y
Module Key config: : webwork-config
Module Description The Config Plugin: : i18n Name Key config.name: : i18n Description Key config.description: : Enter Action Classname MyActionClass: : ConfigWebwork
Enter Package Name ru.matveev.alexey.jira.tutorial.webworkui.jira.webwork: :Enter Alias ConfigWebwork: : Enter View Name success: : success.soy
Enter Template Path /templates/webwork-config/configwebwork/success.soy.vm: : /templates/webwork-config/configwebwork/success.soy
Add Another View? (Y/y/N/n) N: : N
Add Another Action? (Y/y/N/n) N: : N
Add Another Plugin Module? (Y/y/N/n) N: : N

В результате будет создан файл src/main/java/ru/matveev/alexey/jira/tutorial/webworkui/jira/webwork/ConfigWebwork.java. Этот файл нужно изменить вот так:

src/main/java/ru/matveev/alexey/jira/tutorial/webworkui/jira/webwork/ConfigWebwork.java

package ru.matveev.alexey.jira.tutorial.webworkui.jira.webwork; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.atlassian.jira.web.action.JiraWebActionSupport;
import ru.matveev.alexey.jira.tutorial.webworkui.api.PluginSettingService; import javax.inject.Inject; public class ConfigWebwork extends JiraWebActionSupport
{ private static final Logger log = LoggerFactory.getLogger(ConfigWebwork.class); private final PluginSettingService pluginSettingService; private String configJson; @Inject public ConfigWebwork(PluginSettingService pluginSettingService) { this.pluginSettingService = pluginSettingService; } @Override public String execute() throws Exception { super.execute(); return SUCCESS; } public void doSave() { pluginSettingService.setConfigJson(configJson); } @ActionViewData public String getConfigJson() { return pluginSettingService.getConfigJson().isEmpty()?"{}":pluginSettingService.getConfigJson(); } public void setConfigJson(String json) { this.configJson = json; } }

Аннотация @ActionViewData необходима для того, чтобы параметр configJson был доступен в soy шаблоне.

Создадим web section и web item

Мы добавили webwork. Теперь добавим пункт меню, из которого будет запускаться webwork.
Открываем терминал и выполняем следующую команду:
create-atlas-jira-plugin-module

Отвечаем на вопросы следующим образом:

Choose a number (1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/33/34): 30
Enter Plugin Module Name My Web Section: : Webwork Config Section
Enter Location (e.g. system.admin/mynewsection): admin_plugins_menu
Show Advanced Setup? (Y/y/N/n) N: : N
Add Another Plugin Module? (Y/y/N/n) N: : Y
Choose a number (1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/33/34): 25
Enter Plugin Module Name My Web Item: : Webwork Config Item
Enter Section (e.g. system.admin/globalsettings): admin_plugins_menu/webwork-config-section Enter Link URL (e.g. /secure/CreateIssue!default.jspa): /secure/ConfigWebwork.jspa? Show Advanced Setup? (Y/y/N/n) N: : N
Add Another Plugin Module? (Y/y/N/n) N: : N

В результате мы создали пункт меню на странице Add-ons.

Создаем soy шаблон

Подробно про soy шаблоны можно почитать тут.

Мы создадим файл
src/main/resources/templates/webwork-config/configwebwork/success.soy.

src/main/resources/templates/webwork-config/configwebwork/success.soy

{namespace webwork.config}
/** * This template is needed for drawing the formview. */
{template .formview} {@param configJson: string} {webResourceManager_requireResource('ru.matveev.alexey.jira.tutorial.webworkui.webwork-soy-require-backbone:webwork-soy-require-backbone-resources')} <html> <head> <meta charset="utf-8"/> <meta name="decorator" content="atl.admin"> <meta name="admin.active.section" content="admin_plugins_menu/telegram-config-section"> <meta name="admin.active.tab" content="telegram-general-config-item"> <title>my page page</title> </head> <body> <div id="container"> <form class="aui" action="ConfigWebwork!save.jspa" method="POST"> <div class="field-group"> <label for="configJson">Json</label> <input class="text long-field" type="text" id="configJson" name="configJson" placeholder="Json String" value="{$configJson}"> <div class="description">the configJson Parameter</div> </div> <div class="buttons-container"> <div class="buttons"> <input class="button submit" type="submit" value="Save" id="config-save-button"> <a class="cancel" href="#">Cancel</a> </div> </div> </form> </div> </body> </html>
{/template}

В файле atlassian-plugin.xml в тег web-resource нужно добавить ссылку на созданный soy шаблон:

<resource type="soy" name="webwork-config" location="/templates/webwork-config/configwebwork/success.soy"/>

Теперь внесем изменения в atlassian-plugin.xml для того, чтобы при обращении к webwork был бы отображен созданный soy шаблон:

<view name="success" type="soy">:webwork-soy-require-backbone-resources/webwork.config.formview</view>

webwork-soy-require-backbone-resources — это атрибут name в теге web-resource, куда мы добавили ссылку на наш soy шаблон.

webwork.config.formview — namespace и название шаблона из soy файла.

Протестируем плагин

Откроем терминал в папке плагина и выполним следующую команду:
atlas-run

После того как Jira запустится нужно перейти в браузере по следующей ссылке:

localhost:2990/jira/secure/ConfigWebwork.jspa

Экран будет выглядеть вот так:

Webwork работает.
Теперь нам нужно сделать так, чтобы было два экрана для заполнения параметров, и на последнем экране кнопка Save должна приводить все параметры в json формат и сохранять в настройках плагина. Можно попробовать ввести данные в поле Json и сохранить.

Про backbone js можно почитать тут. Для управления логикой перемещения по экранам и приведением параметров в json формат мы будем использовать backbone js.

Создадим backbone модель

src/main/resources/js/webwork-config-model.js

Model.extend({
defaults: {
parameter1: '',
parameter2: '',
parameter3: '',
parameter4: ''
}
});
return {
Model: WebConfigModel
};
})
define('webwork/config/model', [
'jquery',
'backbone',
'underscore'
], function($, Backbone, _) {
var WebConfigModel = Backbone.

Для того, чтобы модель была доступна при загрузке soy шаблона, файл с моделью необходимо добавить в atlassian-plugin.xml в тег web-resource:

<resource type="download" name="webwork-config-model.js" location="/js/webwork-config-model.js"/>

Создадим backbone вью

Я написал в коде комментарии для важных моментов.

src/main/resources/js/webwork-config-view.js

//define это директива requirejs и определяет модель как модуль webwork/config/view. Это позволяет нам определять зависимости в других файлах от модели.
define('webwork/config/view', [
'jquery',
'backbone',
'underscore'
], function($, Backbone, _) {
«use strict»;
var AppView = Backbone.View.extend({
events: {
«click #config-save-button»: «saveConfig»,
«click #next-button»: «nextButton»,
«click #back-button»: «prevButton»

Сохраняет параметры с экрана в модель и преобразует параметры в json формат
saveConfig: function(){
this.model.set(«parameter3», $("#parameter3").val());
this.model.set(«parameter4», $("#parameter4").val());
$("#configJson").val(JSON.stringify(this.model));
},
// функция, которая работает по кнопке Next на первом экране. },
// функция, которая работает по кнопке Save. Сохраняет параметры со второго экрана в модель и рисует первый экран
prevButton: function(){
this.model.set(«parameter3», $("#parameter3").val());
this.model.set(«parameter4», $("#parameter4").val());
var template = webwork.config.page1({configJson:$("#configJson").val(), parameter1:this.model.get('parameter1'), parameter2:this.model.get('parameter2')});
$("#container").replaceWith(template);
$("#configJson").val(JSON.stringify(this.model));
},
initialize: function(){
this.render();
},
render: function(){
var template = webwork.config.page1({configJson:$("#configJson").val(), parameter1:this.model.get('parameter1'), parameter2:this.model.get('parameter2')});
$("#container").replaceWith(template);
},
// это ссылка на главный контейнер. Сохраняет параметры с первого экрана в модель и рисует второй экран
nextButton: function(){
this.model.set(«parameter1», $("#parameter1").val());
this.model.set(«parameter2», $("#parameter2").val());
var template = webwork.config.page2({configJson:$("#configJson").val(), parameter3:this.model.get('parameter3'), parameter4:this.model.get('parameter4')});
$("#container").replaceWith(template);
$("#configJson").val(JSON.stringify(this.model));
},
// функция, которая работает по кнопке Back на втором экране. Вью будет ловить все события от элементов ниже этого элемента
el: '#maincontainer'
});
return {
View: AppView
};
})

Для того, чтобы вью была доступна при загрузке soy шаблона, файл с вью необходимо добавить в atlassian-plugin.xml в тег web-resource:

<resource type="download" name="webwork-config-view.js" location="/js/webwork-config-view.js"/>

Создадим js file, чтобы настроить backbone модель и вью

src/main/resources/js/webwork-soy-require-backbone.js

require([
'webwork/config/view',
'webwork/config/model',
'jquery',
'backbone',
'underscore'
], function(webworkConfigView, webworkConfigModel, $, Backbone, _) {
var webworkConfigModel = new webworkConfigModel.Model(JSON.parse($("#configJson").val()));
var actionsView = new webworkConfigView.View({model: webworkConfigModel});

})

Наш js файл использует requirejs. Директива require позволяет добиться того, что файл будет загружен только после того, как все зависимости загружены. Мы определили следующие зависимости для нашего файла: webwork/config/view, webwork/config/model, query, backbone, underscore.

Добавим параметры необходимые для работы soy шаблонов

В тег web-resource в файле atlassian-plugin.xml нужно добавить:

<transformation extension="soy"> <transformer key="soyTransformer"/>
</transformation>
<resource name="success-soy.js" type="download" location="/templates/webwork-config/configwebwork/success.soy"/>

Эти параметры позволяют обращаться к soy шаблону в js файлах.

Внесем изменения в success.soy

Я добавил комментарии к важным моментам

src/main/resources/templates/webwork-config/configwebwork/success.soy

{namespace webwork.config}
/** * Этот шаблон запускается сразу из webwork. Здесь выводится json параметр. Далее этот шаблон сразу перерисовывается шаблоном page1. Шаблон нужен для того, чтобы получить json параметр и заполнить backbone model. */
{template .formview} {@param configJson: string} {webResourceManager_requireResource('ru.matveev.alexey.jira.tutorial.webworkui.webwork-soy-require-backbone:webwork-soy-require-backbone-resources')} <html> <head> <meta charset="utf-8"/> <meta name="decorator" content="atl.admin"> <meta name="admin.active.section" content="admin_plugins_menu/telegram-config-section"> <meta name="admin.active.tab" content="telegram-general-config-item"> <title>my page page</title> </head> <body> <div id="maincontainer"> <div id="container"> <input class="text long-field hidden" type="text" id="configJson" name="configJson" placeholder="Json String" value="{$configJson}"> </div> </div> </body> </html>
{/template}
/** * Это шаблон первого экрана. Он содержит parameter1 и parameter2. */
{template .page1} {@param configJson: string} {@param parameter1: string} {@param parameter2: string} <div id="container"> <form class="aui"> <div class="field-group"> <label for="parameter1">Parameter 1</label> <input class="text long-field" type="text" id="parameter1" name="parameter1" placeholder="Parameter1 value" value="{$parameter1}"> <div class="description">Value of Parameter 1</div> </div> <div class="field-group"> <label for="parameter2">Parameter 2</label> <input class="text long-field" type="text" id="parameter2" name="parameter2" placeholder="Parameter2 value" value="{$parameter2}"> <div class="description">Value of Parameter 2</div> </div> <div class="field-group"> <input class="text long-field hidden" type="text" id="configJson" name="configJson" placeholder="Json String" value="{$configJson}"> </div> <div class="buttons-container"> <div class="buttons"> <a class="cancel" href="#">Cancel</a> <input class="button submit" type="submit" value="Next" id="next-button"> </div> </div> </form> </div>
{/template}
/** * Это шаблон второго экрана. Он содержит parameter3 и parameter4. */
{template .page2} {@param configJson: string} {@param parameter3: string} {@param parameter4: string} <div id="container"> <form class="aui" action="ConfigWebwork!save.jspa" method="POST"> <div class="field-group"> <label for="parameter1">Parameter 3</label> <input class="text long-field" type="text" id="parameter3" name="parameter3" placeholder="Parameter3 value" value="{$parameter3}"> <div class="description">Value of Parameter 3</div> </div> <div class="field-group"> <label for="parameter4">Parameter 4</label> <input class="text long-field" type="text" id="parameter4" name="parameter4" placeholder="Parameter4 value" value="{$parameter4}"> <div class="description">Value of Parameter 4</div> </div> <div class="field-group"> <input class="text long-field hidden" type="text" id="configJson" name="configJson" placeholder="Json String" value="{$configJson}"> </div> <div class="buttons-container"> <div class="buttons"> <input class="button submit" type="submit" value="Back" id="back-button"> <input class="button submit" type="submit" value="Save" id="config-save-button"> </div> </div> </form> </div> {/template}

Протестируем приложение

Открываем терминал в папке плагина и запускаем:

atlas-run

После того как Jira запуститься, откройте браузер по ссылке:

http://localhost:2990/jira/secure/ConfigWebwork.jspa

Вы увидите следующий экран:

Появится следующий экран: Заполните параметры и нажмите на кнопку Next.

Параметры будут сохранены в формате Json. Заполните параметры 3 и 4 и нажмите на кнопку Save. Можно нажать на кнопку Back и вы перейдете к первому экрану.

Наш плагин работает.

Показать больше

Похожие публикации

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

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

Кнопка «Наверх»