Уровень опыта |
продвинутый |
Временная оценка |
0:45 |
Применимость Atlassian |
JIRA 7.3+ |
Обзор функций
Если вы закончили учебник Написание плагина для редактора Rich Text в JIRA, вы узнаете, как добавить новые функции в редактор Rich Text. После прочтения этого руководства вы узнаете, как добавить макрос, который использует новый пользовательский элемент. Предположим, мы хотим создать простой макрос {info}. Конечный результат:
РИСУНОК
Давайте погрузимся в детали.
Представленный пример имеет следующую структуру кода:
РИСУНОК
Создание макроса информации
Основными компонентами нашего макроса являются:
- InfoMacro - класс рендеринга,
- шаблоны soy- разметка HTML и Wiki,
- CSS - внешний вид,
- тесты - чтобы убедиться, что все работает так, как вы ожидаете,
- i18n - копия, текст которой отображается пользователю.
Эти части описаны в учебнике - Написание плагина для Rich Text Editor в JIRA.
Добавление класса рендера InfoMacro -
Листинг InfoMacro.java
package com.atlassian.jira.plugin.editor.ref;
import com.atlassian.jira.template.soy.SoyTemplateRendererProvider;
import com.atlassian.renderer.RenderContext;
import com.atlassian.renderer.v2.RenderMode;
import com.atlassian.renderer.v2.macro.BaseMacro;
import com.atlassian.renderer.v2.macro.MacroException;
import com.atlassian.soy.renderer.SoyException;
import com.atlassian.soy.renderer.SoyTemplateRenderer;
import com.google.common.collect.ImmutableMap;
import java.util.Map;
public class InfoMacro extends BaseMacro {
private static final String FALLBACK_RENDER_OUTPUT = "{info}%s{info}";
private final SoyTemplateRenderer soyTemplateRenderer;
public InfoMacro(final SoyTemplateRendererProvider soyTemplateRendererProvider) {
super();
this.soyTemplateRenderer = soyTemplateRendererProvider.getRenderer();
}
@Override
public boolean hasBody() {
return true;
}
@Override
public RenderMode getBodyRenderMode() {
return RenderMode.allow(RenderMode.F_ALL);
}
@Override
public String execute(Map parameters, String body, RenderContext renderContext) throws MacroException {
ImmutableMap.Builder templateParams = ImmutableMap.builder();
templateParams.put("content", body);
try {
return this.soyTemplateRenderer.render(
"com.atlassian.jira.plugins.jira-editor-ref-plugin:handler",
"RefPlugin.Macros.Info.html",
templateParams.build());
} catch (SoyException e) {
return String.format(FALLBACK_RENDER_OUTPUT, body);
}
}
}
,> ,>
Добавление шаблонов soy - разметка HTML и Wiki
Листинг info-macro.soy
{namespace RefPlugin.Macros.Info}
/**
* @param content
*/
{template .html}
<info-macro>{$content|noAutoescape}</info-macro>
{/template}
/**
* @param content
*/
{template .wiki}
{lb}info{rb}{$content}{lb}info{rb}
{/template}
/**
* @param innerMarkup
*/
{template .convert}
{lb}info{rb}{$innerMarkup}{lb}info{rb}
{/template}
Обратите внимание, что внутри html-шаблона используется модификатор noAutoescape, потому что мы хотим облегчить установку истинного HTML, например тегов p или strong.
Добавление CSS - внешний вид
Листинг info-macro.less
info-macro {
@text-padding: 18px;
@icon-indent: -(@text-padding - 3px);
margin: @text-padding/4 0;
padding: 10px 4px 4px 4px;
display: block;
background: #3572b0;
> * {
padding: 0 @text-padding @text-padding/4 @text-padding !important;
background: #fff !important;
margin: 0 !important;
}
> *:first-child:before {
content: "\2139";
font-size: 22px;
text-indent: 0;
margin-left: @icon-indent;
color: inherit;
font-weight: 400;
-webkit-font-smoothing: antialiased;
font-style: normal;
speak: none;
}
}
Добавление тестов
Этот раздел содержит только пример нескольких простых модульных тестов.
Для получения дополнительной информации см. Раздел * Запуск и тестирование * раздела * Учебник - Написание плагина для Rich Text Editor в JIRA.
Листинг info-macro-init.js
AJS.test.require(['com.atlassian.jira.plugins.jira-editor-ref-plugin:handler'], function () {
var htmlConverter = require('jira/editor/converter');
module('InfoMacro handler');
test('Should convert HTML to wiki markup for info macro properly', function () {
assertConversion('<info-macro><p>WIP</p></info-macro>', '{info}WIP{info}');
assertConversion('<info-macro><p>WIP<br>new line</p></info-macro>', '{info}WIP\nnew line{info}');
assertConversion('<info-macro><p>one two</p><ul><li>a</li><li>b</li></ul></info-macro>', '{info}one two\n * a\n * b{info}');
});
var assertConversion = function (html, markup, testName) {
htmlConverter.convert(html).then(function (result) {
equal(result, markup, testName);
}).fail(function (e) {
throw e;
});
};
});
Добавление i18n - копия
Листинг ref-i18n.properties
refplugin.toolbar.info=Info
refplugin.macro.info.placeholder=Info...
Разрешение использования пользовательского элемента внутри редактора
Мы используем новый пользовательский элемент info-macro, и TinyMCE ничего не знает об этом теге, поэтому нам нужно сказать TinyMCE, как поддерживать тег.Для этого нам нужен JS-файл, который будет обрабатывать инициализацию * info * macro. Назовем его info-macro-init.js.
Листинг info-macro-init.js
require([
"jira/editor/customizer"
], function (
Customizer
) {
Customizer.customizeSettings(function (tinymceSettings, tinymce, SchemaBuilder) {
SchemaBuilder.withCustomElement('info-macro', ['p', 'ul', 'ol']);
});
});
- Мы вызываем require, чтобы иметь возможность использовать Customizer(Настройщик), поскольку он позволяет нам добавлять
- Данная функция обратного вызова function (tinymceSettings, tinymce, SchemaBuilder) {...} вызывается до инициализации экземпляра редактора TinyMCE.
Есть три параметра, которые мы можем использовать:
- объект tinymceSettings, который используется для инициализации экземпляра редактора TinyMCE: tinymce.init (tinymceSettings);(подробнее см. документацию TinyMCE)tinymce
- Основной объект tinymce TinyMCE, который мы можем использовать, например, для добавления нового плагина TinyMCE(некоторые примеры приведены в этой части документации TinyMCE)
- Редактор Rich Text Editor SchemaBuilder контролирует параметры схемы, связанные с TinyMCE, такие как schema, valid_elements, valid_children или custom_elements, потому что только подмножество HTML поддерживается форматом Wiki Markup, который используется в качестве формата хранения в JIRA.
- SchemaBuilder используется для добавления пользовательского элемента info-macro вместе с разрешенными дочерними элементами: p, ul, ol.
tinymceSettings позволяет изменять все настройки TinyMCE, но schema, valid_elements, extended_valid_elements, valid_children и custom_elements. Вы можете использовать SchemaBuilder для изменения этих свойств. В настоящее время открыт только метод withCustomElement, который позволяет добавлять пользовательский элемент вместе с разрешенными дочерними элементами и атрибутами.
/**
* Registers a custom element along with allowed children.
* The text node is added by default.
*
* @example
* withCustomElement('x-task', ['p']);
* withCustomElement('x-task', false);
* withCustomElement('x-task');
* withCustomElement('x-task', ['p'], ['content', 'done']);
*
* @param {string} name custom element name
* @param {array.string|false=} children
* when array provided: tag names of allowed children;
* when false: no children allowed;
* when undefined / unspecified: allow p and #comment nodes
* @param {array.string=} attributes list of allowed attributes
* @returns {SchemaBuilder}
*/
SchemaBuilder.prototype.withCustomElement = function (name, children, attributes);
Подсказка
Существуют некоторые проблемы при работе с текстовыми узлами непосредственно под пользовательским элементом. Для лучшего удобства пользователя содержимое вашего макроса должно быть завернуто в тег p.
Прикрепление макроса информации к панели инструментов
Нам нужно добавить кнопку на панель инструментов, а также выполнить правильное действие, когда пользователь нажимает на нее. Эта часть описана в учебнике - Написание плагина для редактора Rich Text в JIRA.
Ниже вы можете найти расширенный файл info-macro-init.js.
Листинг info-macro-init.js
require([
"jquery",
"jira/util/formatter",
"jira/editor/registry",
"jira/editor/customizer"
], function (
$,
formatter,
editorRegistry,
Customizer
) {
var RefPlugin = window.RefPlugin;
Customizer.customizeSettings(function (tinymceSettings, tinymce, SchemaBuilder) {
SchemaBuilder.withCustomElement('info-macro', ['p', 'ul', 'ol']);
});
var INFO = formatter.I18n.getText('refplugin.toolbar.info');
var INFO_PLACEHOLDER = formatter.I18n.getText('refplugin.macro.info.placeholder');
var DROPDOWN_ITEM_HTML = '<li><a href="#" data-operation="info">' + INFO + '</a></li>';
editorRegistry.on('register', function (entry) {
var $otherDropdown = $(entry.toolbar).find('.wiki-edit-other-picker-trigger');
$otherDropdown.one('click', function (dropdownClickEvent) {
var dropdownContentId = dropdownClickEvent.currentTarget.getAttribute('aria-owns');
var dropdownContent = document.getElementById(dropdownContentId);
var speechItem = dropdownContent.querySelector('.wiki-edit-speech-item');
var infoItem = $(DROPDOWN_ITEM_HTML).insertAfter(speechItem).on('click', function () {
entry.applyIfTextMode(addWikiMarkup).applyIfVisualMode(addRenderedContent);
});
entry.onUnregister(function cleanup() {
infoItem.remove();
});
});
});
function addWikiMarkup(entry) {
var wikiEditor = $(entry.textArea).data('wikiEditor');
var content = wikiEditor.manipulationEngine.getSelection().text || INFO_PLACEHOLDER;
wikiEditor.manipulationEngine.replaceSelectionWith(RefPlugin.Macros.Info.wiki({content: content}));
}
function addRenderedContent(entry) {
entry.rteInstance.then(function (rteInstance) {
var tinyMCE = rteInstance.editor;
if (tinyMCE && !tinyMCE.isHidden()) {
var content = tinyMCE.selection.getContent() || INFO_PLACEHOLDER;
tinyMCE.selection.setContent(RefPlugin.Macros.Info.html({ content: '<p>' + content + '</p>' }));
}
});
};
});
Загрузка ресурсов в контекст редактора
Все ресурсы должны быть отражены в файле atlassian-plugin.xml. В этом разделе мы увидим пошаговое руководство по загрузке * info * макроресурсов. Порядок тэгов xml произволен.
Вы можете найти объяснение конкретных тегов xml, используемых в этом разделе, в Написание плагина для редактора Rich Text в JIRA.
Помещение * info * макроса на место
Листинг. Кусок atlassian-plugin.xml
<macro key='info' name='{info} formatting macro'
class='com.atlassian.jira.plugin.editor.ref.InfoMacro'>
<description>Allows you to insert a information banner.</description>
<param name="convert-selector">info-macro</param>
<param name="convert-function">RefPlugin.Macros.Info.convert</param>
</macro>
Добавление info-macro-init.js
Листинг. Кусок atlassian-plugin.xml
<macro key='info' name='{info} formatting macro'
class='com.atlassian.jira.plugin.editor.ref.InfoMacro'>
<description>Allows you to insert a information banner.</description>
<param name="convert-selector">info-macro</param>
<param name="convert-function">RefPlugin.Macros.Info.convert</param>
</macro>
Добавление информации о разметке HTML и Wiki info-macro.soy.js
Листинг. Кусок atlassian-plugin.xml
<web-resource key="handler" name="JIRA Editor Reference Plugin Context Init">
<context>jira.rich.editor</context>
<dependency>com.atlassian.jira.plugins.jira-editor-plugin:converter</dependency>
<resource name="soy/info-macro.soy.js" type="download" location="soy/info-macro.soy" />
<transformation extension="soy">
<transformer key="soyTransformer"/>
</transformation>
</web-resource>
Добавление модульных тестов info-macro-tests.js
Листинг. Кусок atlassian-plugin.xml
<resource type="qunit" name="js/info-macro-tests.js" location="/js/info-macro-tests.js" />
Добавление CSS info-macro.less
Листинг. Кусок atlassian-plugin.xml
<web-resource key="css" name="JIRA Editor Reference Plugin CSS Resources">
<context>jira.view.issue</context>
<context>gh-rapid</context>
<context>jira.rich.editor.content</context>
<transformation extension="less">
<transformer key="lessTransformer"/>
</transformation>
<resource type="download" name="less/info-macro.css" location="less/info-macro.less"/>
</web-resource>
Добавление i18n
Листинг. Кусок atlassian-plugin.xml
<resource type="i18n" name="i18n" location="ref-i18n"/>
Ссылки
- Исходный код плагина: bitbucket.org/atlassian/jira-editor-ref-plugin/overview
- Документация TinyMCE: tinymce.com/docs/
- Спецификация пользовательских элементов: www.w3.org/TR/custom-elements/
По материалам Atlassian JIRA Server Developer Customizing Rich Text Editor in JIRA