null

Полноэкранный режим в CKEditor

Сегодня расскажу вам чудесную историю про то, как добавить в редактор CKEditor полноэкранный режим.

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

 

 

Вот только в нашем случае это не поможет. Дело в том, что редактор находится внутри портлета, который размещен внутри страницы в определнном блоке, который определяется шаблоном страницы. У нас используется трёхколоночный макет. Из-за этого нет возможности растягивать редактор по ширине.

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

Узнав у коллег, что действительно в портелете блогов у нас используется CKEditor, я отправился на оффициальный сайт этого компонента. Стоило мне открыть демо на сайте. И я сразу же увидел заветную кнопку. Вот она. Манящая и чарующая. Фича о которой я мечтаю.

 

 

 Казалось, что дело в шляпе. Осталось просто показать её в интерфейсе или прикрутить штатными мехназимами CKEditor'а. Минутка поиска в Google и я узнаю, что эта кнопка добавляется с помощью плагина на CKEditor. Плагин называется maximize. Скачать его и почитать о нём можно тут. Мало того, что есть штатные механизмы добавления в редактор полноэкранного режима, так ещё и оказалось, что в блоге моего коллеги уже есть статья, описывающая процесс установки плагина для CKEditor в 7 Liferay. Советую ознакомиться с ней и поблагодарить автора за её создание. 

 

Если коротко, то в нашу задачу входит написание двух OSGI фрагментов. C помощью первого мы подсунем в Лайфрейный CKEditor наш плагин (статические файлы), а вторым мы поправим настройки редактора, указав ему использовать наш новый плагин, а также настроим toolbar редактора, чтобы в нём появилась долгожданная кнопка.

Настоятельно рекоммендую прочитать статью Ивана, ссылку на которую  я дал выше, ибо ниже я буду приводить лишь выжимки кода без объяснений.

Проект из архетипа, с важным указанием как потом это связать с ckeditor'ом.

mvn archetype:generate \
	-DarchetypeGroupId=com.liferay \
	-DarchetypeArtifactId=com.liferay.project.templates.fragment \
	-DliferayVersion=7.0 \
	-DartifactId=maximaze-plugin \
	-Dpackage=com.test \
	-DgroupId=com.test \
	-Dversion=1.0 \
	-DhostBundleSymbolicName=com.liferay.frontend.editor.ckeditor.web \
	-DhostBundleVersion=1.0.43

Копируем код скачанного плагина сюды

src/
└── main
    └── resources
        └── META-INF
            └── resources
                └── ckeditor
                    └── plugins
                        └── КОПИРОВАТЬ СЮДА!

 

Во втором модуле создаём(без ключей Dhost* компонент наследник BaseEditorConfigContributor и указываем плагин maximize. Плюс необходимо настроить toolbar. Код будет примерно следующий:

 

@Component(
        immediate = true,
        property = {
                "editor.name=ckeditor",
                "editor.config.key=contentEditor",
                "javax.portlet.name=com_liferay_blogs_web_portlet_BlogsPortlet",
                "javax.portlet.name=com_liferay_blogs_web_portlet_BlogsAdminPortlet",
                "service.ranking:Integer=1000000"
        },
        service = EditorConfigContributor.class
)
public class EditorConfig extends BaseEditorConfigContributor {

    @Override
    public void populateConfigJSONObject(JSONObject jsonObject, Map<String, Object> inputEditorTaglibAttributes, ThemeDisplay themeDisplay, RequestBackedPortletURLFactory requestBackedPortletURLFactory) {
         String extraPlugins = jsonObject.getString(ConfigConstants.EXTRA_PLUGINS_KEY);
        jsonObject.put(ConfigConstants.ALLOWED_CONTENT_KEY, true);
        if (!extraPlugins.isEmpty()) {
            extraPlugins += ",";
        }
        extraPlugins += "maximize";
        jsonObject.put("extraPlugins", extraPlugins);

        JSONArray toolbars = JSONFactoryUtil.createJSONArray();
        toolbars.put("Maximize"); //Добавили кнопку в toolbar
        
        jsonObject.put("toolbar_liferayArticle", toolbars);
        jsonObject.put("toolbar_liferay", toolbars);
        jsonObject.put("toolbar_simple", toolbars);
        jsonObject.put("toolbar_editInPlace", toolbars);
        jsonObject.put("toolbar_tablet", toolbars);
        jsonObject.put("toolbar_email", toolbars);
    }
}

 

После выполнения этих не хитрых операций мы получаем готовый код, который можно деплоить в портал (сначала фрагмент со статикой, а потом конфиг). Но в нашей бочке мёда оказалась ложка дёгтя. На нашем портале возник неприятный баг. Заключается он в следующем. Если зайти на портал в Firefox'е,  перейти в полноэкранный режим в CKEditor и выйти из этого режима назад, то окажется, что стиль курсора теперь text (выглядит примерно так 'ⵊ'). При этом при наведении на разные элементы поведение смены стиля курсора странное. Я полез смотреть css'ные стили и увидел, что есть выставленное браузером у body свойство cursor:text. При это правило пришло из какого-то странного файла conteneditable.css.  

 

 

 

Возможно у вас такой проблемы не возникнет, но если вы тоже по какой-то причине словили такой баг, то дальнейшее может быть для вас полезно. А для везунчиков, сразу проспойлерю информацию, которую я выяснил в ходе дебага проблем с курсором. Оказывается, что фрагмент с кодом библиотеки при деплое игнорировался. Это обусловлено тем, что у Liferay уже есть Maximize из коробки и достаточно добавить нужную кнопку в Toolbar.

В общем, мы выяснили, что проблемы есть в Firefox и это связано с каким-то contentEditable. Не долго думая я полез в код этого плагина. Он состоит из одного небольшого JS файла. И там как раз упоминается этот самый contentEditable. Вот в этой функции.

function refreshCursor( editor ) {
		if ( editor.editable().isInline() )
			return;
		// Refresh all editor instances on the page (https://dev.ckeditor.com/ticket/5724).
		var all = CKEDITOR.instances;
		for ( var i in all ) {
			var one = all[ i ];
			if ( one.mode == 'wysiwyg' && !one.readOnly ) {
				var body = one.document.getBody();
				// Refresh 'contentEditable' otherwise
				// DOM lifting breaks design mode. (https://dev.ckeditor.com/ticket/5560)
				body.setAttribute('contentEditable', false);
				body.setAttribute('contentEditable', true);
			}
		}
		if ( editor.editable().hasFocus ) {
			editor.toolbox.focus();
			editor.focus();
		}
	}

А вызывается это чудо в двух местах. И вызывается эта функция только для Firefox'а (проверка на gecko).

// Fixing positioning editor chrome in Firefox break design mode. (https://dev.ckeditor.com/ticket/5149)
CKEDITOR.env.gecko && refreshCursor( editor );

Этот прекрасный фикс нам всё портит. Попробуем его закомментировать. 

 

 

Через какое-то продолжительное время, я увидел в инструментах разработчика браузера, что там отображаются JS'ники всех плагинов для CKEditor'а кроме моего. 'Радости' моей не было предела. Оказывается мой фрагмент не применился. Я начал судоржно смотреть содержимое jar архива, проверять правильный ли файл я кидаю в каталог deploy. Всё было верно. Насколько я понял позже, дело в том, что в Liferay вроде бы есть из коробки этот плагин. Но он там лежит не как отдельный плагин со своей директорией, а собран в кучу со всем внутри какого-то JS-ника в один bundle. Разбираться в том как работают кишки Liferay и что они там придумали с OSGI сил уже не было и я пошёл на радикальные шаги. Я переименовал плагин maximize на maximize2, чтобы засунуть его в Liferay и подключить. Теперь мой отладочный вывод наконец-то появился в браузере.  

В конце концов я решил проблему чуть-чуть изменив код плагина. Изменения внёс в функцию refreshCursor. Изначальный вариант можете посмотреть чуть выше. А итоговый вот:

function refreshCursor( editor ) {
		if ( editor.editable().isInline() )
			return;

		// Refresh all editor instances on the page (https://dev.ckeditor.com/ticket/5724).
		var all = CKEDITOR.instances;
		for ( var i in all ) {
			var one = all[ i ];
			if ( one.mode == 'wysiwyg' && !one.readOnly ) {
				var body = one.document.getBody();
				// Refresh 'contentEditable' otherwise
				// DOM lifting breaks design mode. (https://dev.ckeditor.com/ticket/5560)
				if (this.state == CKEDITOR.TRISTATE_ON) {
					body.setAttribute('contentEditable', false);
					body.setAttribute('contentEditable', true);
				}
			}
		}

		if ( editor.editable().hasFocus ) {
			editor.toolbox.focus();
			editor.focus();
		}
	}

Как видите добавилась всего одна проверка. А теперь можно взглянуть на результат:

 

 

 

На этом, пожалуй, откланяюсь!