Всем привет!
В ходе работы над одним дополнением, которым поделюсь позже, вспомнилась идея: "а не плохо было бы перед публикацией записи посмотреть что получится". Многие ресурсы имеют такой функционал в своем комплекте, а чем инстант хуже?))Сначала двигался по пути реализации дополнительного функционала указанного выше дополнения, затем посмотрел в сторону отдельного компонента и, наконец, остановился на хаке системного компонента content. При удачном стечении обстоятельств, надеюсь опубликовать pull request. Ну а что это за обстоятельства — читаем ниже...

Этот пост скорее представляет собой развернутую тему для обсуждения сообществом "как лучше сделать" некоторые моменты в функционале предпросмотра создаваемых или редактируемых записей.
Ближе к телу:
1. Создаем файл ..\system\controllers\content\actions\item_preview.php и кидаем в него следующий код
<?php class actionContentItemPreview extends cmsAction { public function run($data){ $item['ctype_name'] = $ctype['name']; 'id' => $this->cms_user->id, 'nickname' => $this->cms_user->nickname ); if ($ctype['is_cats'] && $item['category_id'] > 1){ $item['category'] = $this->model->getCategory($ctype['name'], $item['category_id']); } // Получаем поля для данного типа контента $fields = $this->model->getContentFields($ctype['name']); // Парсим значения полей foreach($fields as $name=>$field){ $fields[ $name ]['html'] = $field['handler']->setItem($item)->parse( $item[$name] ); // <- предложение внести флаг is_preview (полезно для нестандартных полей) } // Получаем поля-свойства if ($ctype['is_cats'] && $item['category_id'] > 1){ $props = $this->model->getContentProps($ctype['name'], $item['category_id']); $props_values = $this->model->getPropsValues($ctype['name'], $item['id']); } // Рейтинг if ($ctype['is_rating'] && $this->isControllerEnabled('rating')){ $item['rating_widget'] = cmsCore::getController('rating')->getWidget(1, false, false); } // Формируем теги if ($ctype['is_tags']){ } 'item' => $item, 'ctype' => $ctype, 'fields' => $fields, 'errors' => false, 'html' => $html )); } }
для первого добавляем перед $item = $this->model->addContentItem($ctype, $item, $fields); (205 строка), для второго $item = $this->model->updateContentItem($ctype, $id, $item, $fields); (182 строка)
// Блок предпросмотра if ($this->request->has('is_preview')){ }
// Запрос предпросмотра с ошибками заполнения полей if ($errors && $this->request->has('is_preview')){ 'errors' => $errors, 'message' => LANG_FORM_ERRORS )); }
'action' => '', 'method' => 'post', 'toolbar' => false, 'event' => "content_{$ctype['name']}_form_html", 'do' => $do, 'id' => $do=='edit' ? $item['id'] : null ) ), 'append_html' => html_button('Предпросмотр', 'preview', "icms.content.getPreview();", array('class'=>'button-preview', 'data-description' => 'Предварительный просмотр')), // <- Вот эта строка !!! ), $errors);
data-description — необходим чтобы передать текст в js-скрипт на фронтенде. Есть и другие способы, но я выбрал этот.
Добавляем немного стилей в конец файла ..\templates\default\css\theme-content.css
#preview { border: 5px solid #d6d6d6; padding: 10px; margin: 7px 0 20px; } .preview-title { margin-bottom: 10px; font-size: 12px; color: #aaa; } input.button-preview { float: right; background: #dedede; color: #333; } input.button-preview:hover { background: #e7e7e7; }
this.getPreview = function() { var preview_title = $('.button-preview').data('description'); // объект формы контента var $form = $('.button-preview').closest('form'); var url = $form.attr('action'); // получаем данные из формы var form_data = icms.forms.toJSON($form); // добавляем данные из нестандартных полей // $('[data-preview]', $form).each(function(i, element){ // var key = $(element).closest('.field').attr('id').substr(2); // form_data[key] = $(element).data('preview'); // }); form_data.submit = true; // флаг события submit form_data.is_preview = true; // устанавливаем флаг предпросмотра $.post(url, form_data, function(result) { $('#preview').remove(); // Очищаем старые сообщения об ошибках $('.sess_messages').remove(); $('.error_text', $form).remove(); $('.field_error', $form).removeClass('field_error'); var errors = result.errors || false; // если есть ошибки заполнения полей... if (errors instanceof Object) { // ...выводим их for (var key in errors) { if (key === 'length' || !errors.hasOwnProperty(key)) continue; $('#f_'+key) .addClass('field_error') .prepend('<div class="error_text">'+errors[key]+'</div>'); } // добавляем сообщение о наличии ошибок на форме $('#body').prepend('<div class="sess_messages"><div class="message_error">'+result.message+'</div></div>'); return; } // удаляем скрипты кроме подключаемых по ссылке var html = $(result.html.bold()); html.find('script:not([src])').remove(); // выводим html страницы $form.before('<div id="preview"><div class="preview-title">'+preview_title+'</div>'+html.html()+'</div>'); }, 'json'); }
Вопросы и предложения
Как я написал в начале поста есть ряд моментов которые требуют обсуждения:1. Сторонние разработчики могут делать и делают специфические поля, которые рендеряться по своему внутреннему механизму вывода контента (например в моем компоненте Опросы). Для этих целей предлагаю ввести флаг is_preview в функцию ..\system\core\formfield.php parse($value) -> parse($value, $is_preview=false). Соответственно по этому флагу можно особым образом выводить html-содержимое "нестандартных" полей.
2. Для аналогичных целей с п.1. можно добавить обработчик получения данных поля из атрибута data-preview. В js-скрипте выше этот участок кода закомментирован. Вопрос для разработчиков — нужно ли оно? Или достаточно флага is_preview, или всё же добавить еще возможность формирования данных на фронтэнеде.
3. В представленном решении есть одна большая проблема — подключение стилей CSS! Если поле выводит данные, которые в своем составе содержат стили подключаемые, например, через addCSS(...), то они в текущем виде никак не подгружаются, и соответственно такие данные криво отображаются в области предпросмотра. Тоже самое происходит и с подключаемыми скриптами, функции которых могут запрашиваться при рендере контента, поэтому внутренние скрипты я вычищаю из результатов предпросмотра.
4. Очень проблематично получить ID формы добавления/редактирования контента! Эту проблему я обошел указанием вместо id непосредственно тега form, но механизм получения id был бы не лишним))
5. И в заключении интересует вопрос куда лучше выводить области предпросмотра?

Сейчас, по примеру хабра, я показываю превьюшку вверху формы (позиция 1). Может стоит задействовать позицию 2 или 3? Или выводить в модальном окне, или еще какие варианты?
Демо здесь
demo@demo.ru / 123456
UPD 3 вопрос можно решить заменой подключения стилей с addCSS(...) на addCSSFromContext(...). Также и для js — addJSFromContext($file, $comment='')
Я думаю позиция 2 будет самое то - посмотрел - сохранил.
Поменял вывод предпросмотра на демо во вторую позицию.
Согласен, здесь более логично увидеть превьюшку.
При размещении и в позиции 1, и в 2, всё равно приходится прокручивать и область предпросмотра, и форму до кнопки "Сохранить". Также нужно учитывать, что обычно предпросмотр используется там, где текст по высоте больше, чем форма. Поэтому на мой взгляд, правильнее оставить предпросмотр в позиции 1. Посмотрел весь текст сверху вниз, что-то решил изменить и форма уже сразу перед глазами - удобно и без лишних движений. Если всё подходит, то кнопка "Сохранить" тут же под формой - прокручивать если и нужно, то невысокую форму и вниз, а не большой текст и в обратном направлении. А если делать в п.2, то для изменения нужно прокручивать весь текст обратно до верха, потом прокрутить форму и там менять в форме - это нелогично, это лишние манипуляции и неудобно.
Да и привычнее видеть кнопку "Сохранить" под формой ввода, а не под текстом. Ведь сохраняем мы не просмотренный текст, а то, что набрано в форме. Если предпросмотр будет в п.2, то при изменении текста в форме и неизменности текста предпросмотра могут быть непонятки: что мы сохраняем, просмотренный текст, раз кнопка "Сохранить" под ним, или набранное в форме? Так что и с этой точки зрения удобнее делать предпросмотр сверху в позиции 1, как Val сразу интуитивно и сделал.
WebMan, а как вы смотрите на модальное окно предпросмотра?
Какая разница пользователю как будут выглядеть теги, или заголовок. Или прикрепленный к материалу файл. Или карта из поля карты.
Избавьте разработчиков и себя от ненужной возни. Сделайте предпросмотр для поля html.
Примеры таких нестандартных полей, как уже упоминал в посте, мои опросы - в форме редактирования пользователь видит менеджер опросов, т.е. список их заголовков, а в записи - уже выводятся полноценные вопросы с вариантами ответов. Или недавно опубликованное поле Loadыря "Список количества", и множество других подобных полей. Это примеры полей которые интересно посмотреть в "препродакшене", т.е. перед публикацией записи.
Можно сделать предпросмотр и сейчас, не ставя публиковать в статьях (типа черновик). Но если решение будет универсальным, то бует удобно. даже пускай если будет открываться в отдельном окне.
Вы за вариант модального окна?
Отсюда вопрос: куда девать эту пимпу?
Также есть предложение при включенной опции премодерации, переименовывать кнопку "Сохранить" в "Отправить на модерацию", тогда и надпись о публикации после проверки можно убрать совсем, или выводить ее в виде хинта под кнопкой. Получается более наглядный UX))
Вот такой вариант мне кажется неуместным:
А как поместить "Предпросмотр" справа от "Сохранить" (не выравнивание по правому краю, а следом за сохранить) без костылей я пока не знаю.
Зайдите на демо-сайт и потестируйте =)
Есть только пункт 1 описания со Спойлером и все!
В отдельный компонент когда завернете?
Хорошее дело. А вот Хаки только головняк добавляют.
Перенес настройки из Дефолтного шаблона в свой кнопка появилась, но не работает!
Возможно неправильно прописал код js/ Куда лучше всего вносить код из 4-го пункта и в какой шаблон?
Зайдите на демосайт и посмотрите как там реализовано.
Поэтому будет проще, если как выше напишите что и куда вставить.
Надеюсь когда все получится, сделать патч к Компоненту Мои патчи, чтобы другие могли воспользоваться Хаком без головной боли.
Думаю именно по этой причине не появилось много заинтересованных. Сразу не пошло...
Попробуйте еще раз на дефолтном шаблоне чистого движка. Если получится, то нужно искать проблему в стороннем шаблоне (возможно что-то не вписали или не туда вписали и, например, просто не выводится кнопка, а функционал присутствует) ?
Про демосайт, я имел ввиду, посмотреть через devtools что и как, например, в скриптах и html-коде страниц. Но там все ровно так как и описано в тексте поста.
Обращайтесь в личку по возникшим трудностям, победим их вместе
Сам предосмотр нужен! Конечно можно без него обойтись, да и без вилок обходятся..., но макароны удобнее вилкой...
Вот этот предосмотр, когда он есть, будет использоваться некоторыми на постоянной основе! Остальные ихредка по ходу жизни!
Так что в коробочку его и никаких хаков! Будет приятное расширение функционала с нуля.
p.s. а на счёт "коробки"... в последнее время какая-то тенденция нехорошая любое дополнение тянуть в коробку. Вот мне оно нужно, и вам и еще 5-10 пользователям, но абсолютному большинству нет! Так что до коробки надо еще дорасти =)
Но почва подготовлена, и семена посажены...
1 Использую редактор markitup, и в предпросмотре нет абзацев http://joxi.ru/p27R9Dgig6pDm7 Как это можно исправить?
2 Возможно ли, чтобыпосле того, как первый раз нажата кнопка Предпросмотр, вместе с формой (и после нее) добавилась еще одна кнопка Сохранить?
2. Если я правильно вас понял, можно добавить в конец функции getPreview() следующий код:
На 2.15 актуально?
тоже интересно