From 130dcac3520e82dcadc3e4f470e2ca935e387c75 Mon Sep 17 00:00:00 2001 From: Denis Shakhov Date: Wed, 11 Jun 2014 15:58:32 +0700 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=BA=D0=BE=D0=BC=D0=BC=D0=B5=D0=BD=D1=82=D0=B0?= =?UTF-8?q?=D1=80=D0=B8=D0=B5=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Удален экшн ActionComments (функционал будет перенесен в активность) * Добавлен компонент comment * Доработан js код комментариев + оформлен как jQuery Widget * Fixes #466 При ajax подгрузке комментов не работает голосование --- application/config/config.php | 1 + application/frontend/common/js/comments.js | 1339 +++++++++-------- application/frontend/i18n/ru.php | 9 +- .../developer/actions/ActionBlog/topic.tpl | 13 +- .../actions/ActionComments/index.tpl | 7 - .../skin/developer/assets/css/comments.css | 105 +- .../frontend/skin/developer/assets/js/init.js | 50 +- .../skin/developer/blocks/block.stream.tpl | 2 +- .../developer/blocks/block.stream_comment.tpl | 9 +- .../developer/blocks/block.stream_topic.tpl | 9 +- .../skin/developer/comments/comment.form.tpl | 35 - .../skin/developer/comments/comment.tpl | 130 -- .../skin/developer/comments/comment_list.tpl | 11 - .../developer/comments/comment_pagination.tpl | 86 -- .../skin/developer/comments/comment_tree.tpl | 104 -- .../developer/components/article/article.tpl | 5 +- .../components/comment/comment-form.tpl | 63 + .../components/comment/comment-list.tpl | 102 ++ .../components/comment/comment-tree.tpl | 62 + .../developer/components/comment/comment.tpl | 190 +++ .../comment/tests/visual/comment.tpl | 40 + .../skin/developer/layouts/layout.base.tpl | 2 +- 22 files changed, 1296 insertions(+), 1078 deletions(-) delete mode 100644 application/frontend/skin/developer/actions/ActionComments/index.tpl delete mode 100644 application/frontend/skin/developer/comments/comment.form.tpl delete mode 100644 application/frontend/skin/developer/comments/comment.tpl delete mode 100644 application/frontend/skin/developer/comments/comment_list.tpl delete mode 100644 application/frontend/skin/developer/comments/comment_pagination.tpl delete mode 100644 application/frontend/skin/developer/comments/comment_tree.tpl create mode 100644 application/frontend/skin/developer/components/comment/comment-form.tpl create mode 100644 application/frontend/skin/developer/components/comment/comment-list.tpl create mode 100644 application/frontend/skin/developer/components/comment/comment-tree.tpl create mode 100644 application/frontend/skin/developer/components/comment/comment.tpl create mode 100644 application/frontend/skin/developer/components/comment/tests/visual/comment.tpl diff --git a/application/config/config.php b/application/config/config.php index 8a7572a3..dbb6ddac 100644 --- a/application/config/config.php +++ b/application/config/config.php @@ -523,6 +523,7 @@ $config['head']['default']['js'] = array( "___path.application.web___/frontend/common/js/userfeed.js", "___path.application.web___/frontend/common/js/activity.js", "___path.application.web___/frontend/common/js/toolbar.js", + "___path.application.web___/frontend/common/js/toolbar.comments.js", "___path.application.web___/frontend/common/js/topic.js", "___path.application.web___/frontend/common/js/admin.js", "___path.application.web___/frontend/common/js/userfield.js", diff --git a/application/frontend/common/js/comments.js b/application/frontend/common/js/comments.js index f8a4b62d..07fea96d 100644 --- a/application/frontend/common/js/comments.js +++ b/application/frontend/common/js/comments.js @@ -8,687 +8,742 @@ * @author Denis Shakhov */ -var ls = ls || {}; - -ls.comments = (function ($) { +(function($) { "use strict"; - /** - * Дефолтные опции - * - * @private - */ - var _defaults = { - type: { - topic: { - url_add: aRouter.blog + 'ajaxaddcomment/', - url_response: aRouter.blog + 'ajaxresponsecomment/' - }, - talk: { - url_add: aRouter.talk + 'ajaxaddcomment/', - url_response: aRouter.talk + 'ajaxresponsecomment/' - } - }, - // Селекторы - selectors: { - container: '#comments', - preview: '.js-comment-preview', - wrapper: '.js-comment-wrapper', - fold_all: '.js-comments-fold-all', - unfold_all: '.js-comments-unfold-all', - title: '.js-comments-title', - reply_root: '.js-comment-reply-root', - comment: { - comment: '.js-comment', - reply: '.js-comment-reply', - fold: '.js-comment-fold', - remove: '.js-comment-remove', - update: '.js-comment-update', - update_timer: '.js-comment-update-timer', - scroll_to_child: '.js-comment-scroll-to-child', - scroll_to_parent: '.js-comment-scroll-to-parent' - }, - form: { - form: '.js-comment-form', - text: '#form_comment_text', - submit: '.js-comment-form-submit', - preview: '.js-comment-form-preview', - update_submit: '.js-comment-form-update-submit', - update_cancel: '.js-comment-form-update-cancel', - comment_id: '#form_comment_reply' - }, - toolbar: { - update: '#update-comments', - new_counter: '#new_comments_counter', - last_id: '#comment_last_id', - use_paging: '#comment_use_paging' - } - }, - classes: { - states: { - current: 'comment-current', - new: 'comment-new', - deleted: 'comment-deleted', - self: 'comment-self' - } - }, - wysiwyg: null, - folding: true, - show_form: false - }; - - this.aComments = $(); - this.aCommentsNew = $(); - this.iCurrentCommentId = null; - this.iFormTargetId = 0; - - /** - * Инициализация - * - * @param {Object} options Опции - */ - this.init = function(options) { - var _this = this, - oDocument = $(document); - - this.options = $.extend({}, _defaults, options); - - this.elements = { - container: $(this.options.selectors.container), - title: $(this.options.selectors.title), - reply_root: $(this.options.selectors.reply_root), - fold_all: $(this.options.selectors.fold_all), - unfold_all: $(this.options.selectors.unfold_all), - form: { - form: $(this.options.selectors.form.form), - text: $(this.options.selectors.form.text), - submit: $(this.options.selectors.form.submit), - preview: $(this.options.selectors.form.preview), - update_cancel: $(this.options.selectors.form.update_cancel), - update_submit: $(this.options.selectors.form.update_submit), - comment_id: $(this.options.selectors.form.comment_id) - }, - toolbar: { - update: $(this.options.selectors.toolbar.update), - new_counter: $(this.options.selectors.toolbar.new_counter), - last_id: $(this.options.selectors.toolbar.last_id), - use_paging: $(this.options.selectors.toolbar.use_paging) - } - } - - this.aComments = $(this.options.selectors.comment.comment); - - this.calcNewComments(); - this.checkFolding(); - ! this.options.show_form && this.formToggle(this.iFormTargetId); - - // Навигация по комментариям - oDocument.on('click', this.options.selectors.comment.scroll_to_parent, function (e) { - var oElement = $(this); - - _this.scrollToParentComment(oElement.data('id'), oElement.data('parent-id')); - }); - - // Показывает / скрывает форму комментирования - oDocument.on('click', this.options.selectors.comment.reply, function (e) { - _this.formToggle($(this).data('id')); - e.preventDefault(); - }); - - // Подгрузка новых комментариев - this.elements.toolbar.update.on('click', function (e) { - var oButton = $(this); - - _this.load(oButton.data('target-id'), oButton.data('target-type')); - e.preventDefault(); - }); - - // Переход к следующему новому комментарию - this.elements.toolbar.new_counter.on('click', function (e) { - _this.scrollToNextNewComment(); - e.preventDefault(); - }); - - // Превью текста - this.elements.form.preview.on('click', function (e) { - _this.previewShow(); - }); - - // Добавление - this.elements.form.form.on('submit', function (e) { - var oForm = $(this); - - _this.add(oForm, oForm.data('target-id'), oForm.data('target-type')); - e.preventDefault(); - }); - - this.elements.form.text.bind('keyup', function(e) { - if (e.ctrlKey && (e.keyCode || e.which) == 13) { - _this.elements.form.form.submit(); - } - }); - - // Удаление - this.elements.container.on('click', this.options.selectors.comment.remove, function(e) { - var oElement = $(this), - iCommentId = oElement.data('id'); - - _this.toggle(oElement, iCommentId); - - e.preventDefault(); - }); - - // Редактирование - this.elements.container.on('click', this.options.selectors.comment.update, function(e) { - var oElement = $(this), - iCommentId = oElement.data('id'); - - _this.formToggle(iCommentId,false,true); - e.preventDefault(); - }); - - // Отмена редактирования - this.elements.form.update_cancel.on('click', function (e) { - _this.formToggle(_this.iFormTargetId,false,true); - e.preventDefault(); - }); - - // Сохранение после редактирования - this.elements.form.update_submit.on('click', function (e) { - _this.submitCommentUpdate(_this.elements.form.form,_this.iFormTargetId); - e.preventDefault(); - }); - - // Сворачивание - if (this.options.folding) { - // Свернуть все - this.elements.fold_all.on('click', function (e) { - _this.foldAll(); - e.preventDefault(); - }); - - // Развернуть все - this.elements.unfold_all.on('click', function (e) { - _this.unfoldAll(); - e.preventDefault(); - }); - - // Свернуть/развернуть - this.elements.container.on('click', this.options.selectors.comment.fold, function(e) { - var oElement = $(this), - oComment = _this.getCommentById(oElement.data('id')); - - _this[ oElement.hasClass(ls.options.classes.states.open) ? 'fold' : 'unfold' ](oComment); - - e.preventDefault(); - }); - } - - this.initUpdateTimers(); - - ls.hook.run('ls_comments_init_after',[],this); - }; - - /** - * Добавляет комментарий - */ - this.add = function(oForm, iTargetId, sTargetType) { - var oData = oForm.serializeJSON(); - - ls.utils.formLock(oForm); - this.previewHide(); - - ls.ajax.load(this.options.type[sTargetType].url_add, oData, function(oResponse) { - if (oResponse.bStateError) { - ls.msg.error(null, oResponse.sMsg); - } else { - this.elements.form.text.val(''); - this.load(iTargetId, sTargetType, oResponse.sCommentId, true); - - ls.hook.run('ls_comments_add_after', [oForm, iTargetId, sTargetType, oResponse]); - } - - ls.utils.formUnlock(oForm); - }.bind(this)); - }; - - /** - * Удалить/восстановить комментарий - */ - this.toggle = function(oElement, iCommentId) { - var sUrl = aRouter['ajax'] + 'comment/delete/', - oParams = { idComment: iCommentId }; - - ls.hook.marker('toggleBefore'); - - ls.ajax.load(sUrl, oParams, function(oResponse) { - if (oResponse.bStateError) { - ls.msg.error(null, oResponse.sMsg); - } else { - ls.msg.notice(null, oResponse.sMsg); - - this.getCommentById(iCommentId).removeClass(this.options.classes.states.self + ' ' + this.options.classes.states.new + ' ' + this.options.classes.states.deleted + ' ' + this.options.classes.states.current); - - if (oResponse.bState) { - this.getCommentById(iCommentId).addClass(this.options.classes.states.deleted); - } - - oElement.text(oResponse.sTextToggle); - - ls.hook.run('ls_comments_toggle_after',[oElement, iCommentId, oResponse]); - } - }.bind(this)); - }; - - /** - * Подгружает новые комментарии - */ - this.load = function(iTargetId, sTargetType, iCommentSelfId, bNotFlushNew) { - var idCommentLast = this.elements.toolbar.last_id.val(), - oUpdate = this.elements.toolbar.update; - - oUpdate.addClass(ls.options.classes.states.active); - - var oParams = { - idCommentLast: idCommentLast, - idTarget: iTargetId, - typeTarget: sTargetType - }; - - oParams.selfIdComment = iCommentSelfId || undefined; - oParams.bUsePaging = this.elements.toolbar.use_paging.val() ? 1 : 0; - - ls.ajax.load(this.options.type[sTargetType].url_response, oParams, function(oResponse) { - oUpdate.removeClass(ls.options.classes.states.active); - - if (oResponse.bStateError) { - ls.msg.error(null, oResponse.sMsg); - } else { - var aCommentsLoaded = oResponse.aComments, - iCountOld = this.aCommentsNew.length; - - // Убираем подсветку новых и текущего комментариев - this.aComments.removeClass(this.options.classes.states.current); - - if ( ! bNotFlushNew ) { - this.aCommentsNew.removeClass(this.options.classes.states.new); - this.aCommentsNew = $(); - iCountOld = 0; - } - - // Если комментарии подгружаются после сабмита формы текущим пользователем - if (iCommentSelfId) { - this.formToggle(this.iFormTargetId, true); - iCountOld--; - } - - this.setCountNewComment(aCommentsLoaded.length + iCountOld); - - // Вставляем новые комментарии - $.each(aCommentsLoaded, function(iIndex, oItem) { - var oComment = $( $.trim(oItem.html) ); - - if ( ! (iCommentSelfId && iCommentSelfId == oItem.id) ) { - this.aCommentsNew = this.aCommentsNew.add(oComment); - } - - this.aComments = this.aComments.add(oComment); - - this.inject(oItem.idParent, oItem.id, oComment); - }.bind(this)); - - // Обновляем данные - if (aCommentsLoaded.length && oResponse.iMaxIdComment) { - this.elements.toolbar.last_id.val(oResponse.iMaxIdComment); - - // Обновляем счетчик комментариев - this.elements.title.text( ls.i18n.pluralize(this.aComments.length, 'comments.comments_declension') ); - - // Обновляем блок активности - // TODO: Fix - $('#js-stream-update').click(); - } - - // Проверяем сворачивание - if (this.options.folding) { - this.checkFolding(); - if ( ( ! iCommentSelfId && aCommentsLoaded.length ) || ( iCommentSelfId && aCommentsLoaded.length - 1 > 0 ) ) this.unfoldAll(); - } - - // Прокручиваем к комментарию который оставил текущий пользователь - if (iCommentSelfId) { - this.scrollToComment(this.getCommentById(iCommentSelfId)); - } - this.initUpdateTimers(); - - ls.hook.run('ls_comments_load_after', [iTargetId, sTargetType, iCommentSelfId, bNotFlushNew, oResponse]); - } - }.bind(this)); - }; - - /** - * Вставка комментария - */ - this.inject = function(iCommentParentId, iCommentId, oComment) { - var oCommentNew = $('
').append(oComment); - - if (iCommentParentId) { - var oWrapper = $(this.options.selectors.wrapper + '[data-id=' + iCommentParentId + ']'); - - if (oWrapper.parentsUntil(this.elements.container).length == ls.registry.get('comment_max_tree')) { - oWrapper = oWrapper.parent(this.options.selectors.wrapper); - } - - oWrapper.append(oCommentNew); - } else { - this.elements.container.append(oCommentNew); - } - - ls.hook.run('ls_comment_inject_after', arguments, oCommentNew); - }; - - /** - * Предпросмотр комментария - */ - this.previewShow = function() { - if ( ! this.elements.form.text.val() ) return; - - var oPreview = $('
'); - - this.previewHide(); - this.elements.form.form.before(oPreview); - - ls.utils.textPreview(this.elements.form.text, oPreview, false); - }; - - /** - * Предпросмотр комментария - */ - this.previewHide = function() { - this.elements.container.find(this.options.selectors.preview).remove(); - }; - - /** - * Показывает/скрывает форму комментирования - * - * @param {Number} iCommentId ID комментария - * @param {Boolean} bNoFocus Переводить фокус на инпут с текстом или нет - */ - this.formToggle = function(iCommentId, bNoFocus, bIsUpdate) { - this.previewHide(); - bIsUpdate=bIsUpdate || false; - - if (this.iFormTargetId == iCommentId && this.elements.form.form.is(':visible')) { - this.elements.form.form.hide(); - this.iFormTargetId = null; - return; - } - - this.elements.form.form.insertAfter(iCommentId ? this.getCommentById(iCommentId) : this.elements.reply_root).show(); - this.elements.form.text.val(''); - this.elements.form.comment_id.val(iCommentId); + $.widget( "livestreet.lsComments", { /** - * Показываем необходимые кнопки + * Дефолтные опции */ - if (bIsUpdate) { - this.elements.form.update_cancel.show(); - this.elements.form.update_submit.show(); - this.elements.form.submit.hide(); - /** - * Загружаем исходный текст комментария - */ - this.loadCommentUpdate(iCommentId); - } else { - this.elements.form.update_cancel.hide(); - this.elements.form.update_submit.hide(); - this.elements.form.submit.show(); - } + options: { + // Ссылки + urls: { + // Добавление комментария + add: null, + // Подгрузка новых комментариев + load: null, + // Показать/скрыть комментарий + hide: aRouter['ajax'] + 'comment/delete/', + // Обновление текста комментария + text: aRouter['ajax'] + 'comment/load/', + // Обновление комментария + update: aRouter['ajax'] + 'comment/update/' + }, - this.iFormTargetId = iCommentId; + // Селекторы + selectors: { + // Блок с превью текста + preview: '.js-comment-preview', + // Кнопка свернуть/развернуть все + fold_all_toggle: '.js-comments-fold-all-toggle', + // Заголовок + title: '.js-comments-title', + // Кнопка "Оставить комментарий" + reply_root: '.js-comment-reply-root', + // Блок с комментариями + comment_list: '.js-comment-list', + // Подписаться на новые комментарии + subscribe: '.js-comments-subscribe', - if ( ! bNoFocus ) this.elements.form.text.focus(); - }; + // Комментарий + comment: { + comment: '.js-comment', + wrapper: '.js-comment-wrapper', + reply: '.js-comment-reply', + fold: '.js-comment-fold', + remove: '.js-comment-remove', + update: '.js-comment-update', + update_timer: '.js-comment-update-timer', + scroll_to_child: '.js-comment-scroll-to-child', + scroll_to_parent: '.js-comment-scroll-to-parent' + }, - this.loadCommentUpdate = function(iCommentId) { - var sUrl = aRouter['ajax'] + 'comment/load/', - oParams = { idComment: iCommentId }; + // Форма добавления + form: { + form: '.js-comment-form', + text: '.js-comment-form-text', + submit: '.js-comment-form-submit', + preview: '.js-comment-form-preview', + update_submit: '.js-comment-form-update-submit', + update_cancel: '.js-comment-form-update-cancel', + comment_id: '#form_comment_reply' + } + }, - ls.utils.formLock(this.elements.form.form); - ls.hook.marker('loadBefore'); + // Классы + classes: { + states: { + current: 'comment--current', + new: 'comment--new', + deleted: 'comment--deleted', + self: 'comment--self' + } + }, - ls.ajax.load(sUrl, oParams, function(oResponse) { - if (oResponse.bStateError) { - ls.msg.error(null, oResponse.sMsg); - } else { - this.elements.form.text.val(oResponse.sText); - } - ls.hook.run('ls_comments_load_comment_update_after',[iCommentId, oResponse]); - ls.utils.formUnlock(this.elements.form.form); - }.bind(this)); - }; + // Использовать визуальный редактор или нет + wysiwyg: null, + // Включить/выключить функцию сворачивания + folding: true, + // Показать/скрыть форму по умолчанию + show_form: false + }, - this.submitCommentUpdate = function(oForm, iCommentId) { - var oData = oForm.serializeJSON(), - sUrl = aRouter['ajax'] + 'comment/update/'; + /** + * Конструктор + * + * @constructor + * @private + */ + _create: function () { + var _this = this, + oDocument = $(document); - ls.utils.formLock(oForm); - this.previewHide(); - oData.comment_id=iCommentId; - ls.ajax.load(sUrl, oData, function(oResponse) { - if (oResponse.bStateError) { - ls.msg.error(null, oResponse.sMsg); - } else { - var wrap=$('
').html(oResponse.sHtml); - var comment=this.getCommentById(iCommentId); - comment.find(this.options.selectors.comment.update_timer).stopTime(); - comment.replaceWith(wrap.children('.js-comment')); - this.aComments = $(this.options.selectors.comment.comment); // hack - this.formToggle(iCommentId, true); - this.initUpdateTimers(); - this.scrollToComment(this.getCommentById(iCommentId)); - } - ls.hook.run('ls_comments_submit_comment_update_after', [oForm, iCommentId, oResponse]); - ls.utils.formUnlock(oForm); - }.bind(this)); - }; + // Получаем элементы + this.form = this.element.find(this.options.selectors.form.form); - this.initUpdateTimers = function() { - var _this=this; - $(this.options.selectors.comment.update_timer).each(function(k,el){ - el=$(el); - if (!el.data('isInit')) { - el.data('isInit',true); - el.everyTime(1000,function(){ - var seconds=parseInt(el.data('seconds')); - seconds--; - el.data('seconds',seconds); - if (seconds>0) { - el.text(ls.utils.timeRemaining(seconds)); + this.elements = { + title: this.element.find(this.options.selectors.title), + reply_root: this.element.find(this.options.selectors.reply_root), + fold_all_toggle: this.element.find(this.options.selectors.fold_all_toggle), + comment_list: this.element.find(this.options.selectors.comment_list), + subscribe: this.element.find(this.options.selectors.subscribe), + form: { + text: this.form.find(this.options.selectors.form.text), + submit: this.form.find(this.options.selectors.form.submit), + preview: this.form.find(this.options.selectors.form.preview), + update_cancel: this.form.find(this.options.selectors.form.update_cancel), + update_submit: this.form.find(this.options.selectors.form.update_submit), + comment_id: this.form.find(this.options.selectors.form.comment_id) + } + }; + + // ID комментария помеченного как текущий + this.currentCommentId = null; + + // Список комментариев на странице + this.comments = $(this.options.selectors.comment.comment); + + // ID комментария после которого отображается форма + this.formTargetId = 0; + + // Получаем ID объекта к которому оставлен комментарий + this.targetId = this.element.data('target-id'); + + // Получаем тип объекта + this.targetType = this.element.data('target-type'); + + // ID последнего добавленного комментария + this.commentLastId = this.element.data('comment-last-id'); + + // Показываем кнопку свернуть у комментариев с дочерними комментариями + this.checkFolding(); + + // Обновление таймеров у кнопок редактирования + this.initUpdateTimers(); + + // Показываем/скрываем форму по умолчанию + ! this.options.show_form && this.formToggle(0); + + // + // События + // + + // Навигация по комментариям + oDocument.on('click', this.options.selectors.comment.scroll_to_parent, function (e) { + var element = $(this); + + _this.scrollToParentComment(element.data('id'), element.data('parent-id')); + }); + + // Показывает / скрывает форму комментирования + oDocument.on('click', this.options.selectors.comment.reply, function (e) { + _this.formToggle($(this).data('id'), true, false, false); + e.preventDefault(); + }); + + // Превью текста + this.elements.form.preview.on('click', this.previewShow.bind(this)); + + // Отправка формы + this.form.on('submit', function (e) { + this.add(this.form, this.form.data('target-id'), this.form.data('target-type')); + e.preventDefault(); + }.bind(this)); + + this.elements.form.text.bind('keyup', function(e) { + if ( e.ctrlKey && (e.keyCode || e.which) == 13 ) _this.form.submit(); + }); + + // Удаление + this.element.on('click', this.options.selectors.comment.remove, function(e) { + var element = $(this), + commentId = element.data('id'); + + _this.toggle(element, commentId); + + e.preventDefault(); + }); + + // Редактирование + this.element.on('click', this.options.selectors.comment.update, function(e) { + var element = $(this), + commentId = element.data('id'); + + _this.formToggle(commentId, false, true); + e.preventDefault(); + }); + + // Отмена редактирования + this.elements.form.update_cancel.on('click', function (e) { + this.formToggle(this.formTargetId, false, true); + e.preventDefault(); + }.bind(this)); + + // Сохранение после редактирования + this.elements.form.update_submit.on('click', function (e) { + this.submitCommentUpdate(this.formTargetId); + e.preventDefault(); + }.bind(this)); + + // Подписаться/отписаться от новых комментариев + this.elements.subscribe.on('click', function (e) { + var element = $(this), + isActive = element.hasClass('active'); + + ls.subscribe.toggle(element.data('type') + '_new_comment', element.data('target-id'), '', ! isActive); + + if ( isActive ) { + element.removeClass('active').text( ls.lang.get('comments.subscribe') ); + } else { + element.addClass('active').text( ls.lang.get('comments.unsubscribe') ); + } + + e.preventDefault(); + }); + + // Сворачивание + if ( this.options.folding ) { + // Свернуть/развернуть все + this.elements.fold_all_toggle.on('click', function (e) { + var element = $(this); + + if ( ! element.hasClass('active') ) { + _this.foldAll(); + element.addClass('active').text( ls.lang.get('comments.folding.unfold_all') ); } else { - el.parents(_this.options.selectors.comment.comment) - .find(_this.options.selectors.comment.update).hide(); - el.stopTime(); + _this.unfoldAll(); + element.removeClass('active').text( ls.lang.get('comments.folding.fold_all') ); } + + e.preventDefault(); + }); + + // Свернуть/развернуть + this.element.on('click', this.options.selectors.comment.fold, function(e) { + var element = $(this), + comment = _this.getCommentById(element.data('id')); + + _this[ element.hasClass(ls.options.classes.states.open) ? 'fold' : 'unfold' ](comment); + + e.preventDefault(); }); } - }); - }; - /** - * Скрывает кнопку сворачивания у комментариев без дочерних комментариев - */ - this.checkFolding = function() { - if ( ! this.options.folding ) return false; - this.getComments().each(function (iIndex, oComment) { - var oComment = $(oComment); + ls.hook.run('ls_comments_init_after',[],this); + }, - oComment.find(this.options.selectors.comment.fold)[ oComment.next(this.options.selectors.wrapper).length ? 'show' : 'hide' ](); - }.bind(this)); - }; + /** + * Добавляет комментарий + */ + add: function() { + // Получаем данные формы до ее блокировки + var data = this.form.serializeJSON(); - /** - * Сворачивает ветку комментариев - * - * @param {Object} oComment Комментарий - */ - this.fold = function(oComment) { - oComment.removeClass(ls.options.classes.states.open).nextAll(this.options.selectors.wrapper).hide(); - oComment.find(this.options.selectors.comment.fold).removeClass(ls.options.classes.states.open); + ls.utils.formLock(this.form); + this.previewHide(); - this.onFold(oComment); - }; + ls.ajax.load(this.options.urls.add, data, function(response) { + if (response.bStateError) { + ls.msg.error(null, response.sMsg); + } else { + this.elements.form.text.val(''); + this.load(response.sCommentId, true); - /** - * Разворачивает ветку комментариев - * - * @param {Object} oComment Комментарий - */ - this.unfold = function(oComment) { - oComment.addClass(ls.options.classes.states.open).nextAll(this.options.selectors.wrapper).show(); - oComment.find(this.options.selectors.comment.fold).addClass(ls.options.classes.states.open); + ls.hook.run('ls_comments_add_after', [this.form, this.targetId, this.targetType, response]); + } - this.onUnfold(oComment); - }; + ls.utils.formUnlock(this.form); + }.bind(this)); + }, - /** - * Сворачивает ветку комментариев - * - * @param {Object} oComment Комментарий - */ - this.onFold = function(oComment) { - oComment.find(this.options.selectors.comment.fold).find('a').text(ls.lang.get('comments.folding.unfold')); - }; + /** + * Скрыть/восстановить комментарий + * + * @param {jQuery} toggle Кнопка скрыть/восстановить комментарий + * @param {Number} commentId ID добавляемого комментария + */ + toggle: function(toggle, commentId) { + var url = this.options.urls.hide, + params = { idComment: commentId }; - /** - * Сворачивает ветку комментариев - * - * @param {Object} oComment Комментарий - */ - this.onUnfold = function(oComment) { - oComment.find(this.options.selectors.comment.fold).find('a').text(ls.lang.get('comments.folding.fold')); - }; + ls.hook.marker('toggleBefore'); - /** - * Сворачивает все ветки комментариев - */ - this.foldAll = function() { - this.getComments().filter('.' + ls.options.classes.states.open).each(function (iIndex, oComment) { - this.fold($(oComment)); - }.bind(this)); - }; + ls.ajax.load(url, params, function(response) { + if (response.bStateError) { + ls.msg.error(null, response.sMsg); + } else { + ls.msg.notice(null, response.sMsg); - /** - * Разворачивает все ветки комментариев - */ - this.unfoldAll = function() { - this.getComments().filter(':not(.' + ls.options.classes.states.open + ')').each(function (iIndex, oComment) { - this.unfold($(oComment)); - }.bind(this)); - }; + this.getCommentById(commentId).removeClass( + this.options.classes.states.self + ' ' + + this.options.classes.states.new + ' ' + + this.options.classes.states.deleted + ' ' + + this.options.classes.states.current + ); - /** - * Устанавливает число новых комментариев - * - * @param {Number} iCount Кол-во новых комментариев - */ - this.setCountNewComment = function(iCount) { - this.elements.toolbar.new_counter[ iCount > 0 ? 'show' : 'hide' ]().text(iCount); - }; + if (response.bState) { + this.getCommentById(commentId).addClass(this.options.classes.states.deleted); + } - /** - * Вычисляет кол-во новых комментариев - */ - this.calcNewComments = function() { - this.aCommentsNew = this.getCommentsNew(); - this.setCountNewComment(this.aCommentsNew.length); - }; + toggle.text(response.sTextToggle); - /** - * Прокрутка к следующему новому комментарию - */ - this.scrollToNextNewComment = function() { - if ( ! this.aCommentsNew.length ) return false; + ls.hook.run('ls_comments_toggle_after',[toggle, commentId, response]); + } + }.bind(this)); + }, - var oCommentNew = this.aCommentsNew.eq(0); + /** + * Подгружает новые комментарии + * + * @param {Number} сommentSelfId (undefined) ID добавляемого комментария + * @param {Boolean} flush (true) Удалять подсветку у текущих новых комментариев или нет + */ + load: function(сommentSelfId, flush, callbacks) { + flush = typeof flush === 'undefined' ? true : flush; - oCommentNew.removeClass(this.options.classes.states.new); - this.scrollToComment(oCommentNew); + var params = { + idCommentLast: this.commentLastId, + idTarget: this.targetId, + typeTarget: this.targetType, + // TODO: Fix + // bUsePaging: this.elements.toolbar.use_paging.val() ? 1 : 0 + }; - this.aCommentsNew = this.aCommentsNew.filter(':not(:eq(0))'); - this.setCountNewComment(this.aCommentsNew.length); - }; + params.selfIdComment = сommentSelfId || undefined; - /** - * Прокрутка к родительскому комментарию - * - * @param {Number} iCommentId ID комментария - * @param {Number} iCommentParentId ID родительского комментария - */ - this.scrollToParentComment = function(iCommentId, iCommentParentId) { - var _this = this, - oCommentChild = this.getCommentById(iCommentId), - oCommentParent = this.getCommentById(iCommentParentId), - oScrollToChild = oCommentParent.find(this.options.selectors.comment.scroll_to_child); + ls.ajax.load(this.options.urls.load, params, function(response) { + if (response.bStateError) { + ls.msg.error(null, response.sMsg); + } else { + var сommentsLoaded = response.aComments, + сountLoaded = сommentsLoaded.length; - this.getComments().find(this.options.selectors.comment.scroll_to_child).hide(); - this.scrollToComment(oCommentParent); + // Убираем подсветку у текущего комментария + this.getCommentCurrent().removeClass(this.options.classes.states.current); + this.currentCommentId = null; - // Прокрутка обратно к дочернему комментарию - oScrollToChild.show().off().one('click', function() { - oScrollToChild.hide(); - _this.scrollToComment(oCommentChild); - }); - }; + // Убираем подсветку у новых комментариев + if ( flush ) this.getCommentsNew().removeClass(this.options.classes.states.new); - /** - * Прокрутка к комментарию - * - * @param {Number} oComment Комментарий - */ - this.scrollToComment = function(oComment) { - this.getCommentCurrent().removeClass(this.options.classes.states.current); - oComment.addClass(this.options.classes.states.current); - this.iCurrentCommentId = oComment.data('id'); + // Если комментарии подгружаются после сабмита формы текущим пользователем + if ( сommentSelfId ) this.formToggle(this.formTargetId, true); - $.scrollTo(oComment, 1000, { offset: -250 }); - }; + // Вставляем новые комментарии + $.each(сommentsLoaded, function(index, item) { + var сomment = $( $.trim(item.html) ); - /** - * Получает комментарий по его ID - * - * @param {Number} iCommentId ID комментария - * @return {Object} jQuery объект комментария - */ - this.getCommentById = function(iCommentId) { - return this.getComments().filter('[data-id=' + iCommentId + ']'); - }; + this.comments = this.comments.add(сomment); + this.insert(сomment, item.id, item.idParent); + }.bind(this)); - /** - * Получает текущий комментарий - * - * @return {Object} Текущий комментарий - */ - this.getCommentCurrent = function() { - return this.getComments().filter('.' + this.options.classes.states.current); - }; + // Обновляем данные + if ( сountLoaded && response.iMaxIdComment ) { + this.commentLastId = response.iMaxIdComment; - /** - * Получает новые комментарии - * - * @return {Array} Массив с новыми комментариями - */ - this.getCommentsNew = function() { - return this.getComments().filter('.' + this.options.classes.states.new); - }; + // Обновляем кол-во комментариев в заголовке + this.elements.title.text( ls.i18n.pluralize(this.comments.length, 'comments.comments_declension') ); - /** - * Получает комментарии - * - * @return {Array} Массив с комментариями - */ - this.getComments = function() { - return this.aComments; - }; + // Обновляем блок активности + // TODO: Fix + $('#js-stream-update').click(); + } - return this; -}).call(ls.comments || {},jQuery); + // Проверяем сворачивание + if ( this.options.folding ) { + this.checkFolding(); + + // Разворачиваем все ветки если идет просто подгрузка комментариев + // или если при добавления комментария текущим пользователем + // помимо этого комментария подгружаются еще и ранее добавленные комментарии + if ( ( ! сommentSelfId && сountLoaded ) || ( сommentSelfId && сountLoaded - 1 > 0 ) ) this.unfoldAll(); + } + + // Прокручиваем к комментарию который оставил текущий пользователь + if ( сommentSelfId ) this.scrollToComment( this.getCommentById(сommentSelfId) ); + + // Обновляем таймеры + this.initUpdateTimers(); + + $.proxy(callbacks.success, this)(); + ls.hook.run('ls_comments_load_after', [ this.targetId, this.targetType, сommentSelfId, flush, response ]); + } + + $.proxy(callbacks.done, this)(); + }.bind(this)); + }, + + /** + * Вставка комментария + * + * @param {jQuery} сomment Комментарий + * @param {Number} commentId ID добавляемого комментария + * @param {Number} commentParentId (optional) ID родительского комментария + */ + insert: function(comment, commentId, commentParentId) { + var commentWrapper = $('
').append(comment); + + if (commentParentId) { + // Получаем обертку родительского комментария + var wrapper = $(this.options.selectors.comment.wrapper + '[data-id=' + commentParentId + ']'); + + // Проверяем чтобы уровень вложенности комментариев была не больше значения заданного в конфиге + if (wrapper.parentsUntil(this.elements.comment_list).length == ls.registry.get('comment_max_tree')) { + wrapper = wrapper.parent(this.options.selectors.comment.wrapper); + } + + wrapper.append(commentWrapper); + } else { + this.elements.comment_list.append(commentWrapper); + } + + ls.hook.run('ls_comment_insert_after', arguments, commentWrapper); + }, + + /** + * Предпросмотр комментария + */ + previewShow: function() { + if ( ! this.elements.form.text.val() ) return; + + var preview = $('
'); + + this.previewHide(); + this.form.before(preview); + + ls.utils.textPreview(this.elements.form.text, preview, false); + }, + + /** + * Предпросмотр комментария + */ + previewHide: function() { + this.element.find(this.options.selectors.preview).remove(); + }, + + /** + * Показывает/скрывает форму комментирования + * + * @param {Number} commentId ID комментария после которого нужно показать форму + * @param {Boolean} focus Переводить фокус на инпут с текстом или нет + */ + formToggle: function(commentId, focus, update, reset) { + update = update || false, + focus = typeof focus === 'undefined' ? true : focus; + reset = typeof reset === 'undefined' ? true : reset; + + this.previewHide(); + + if ( ( ! update && this.form.data('update') ) || reset ) this.elements.form.text.val(''); + + this.form.data('update', false); + + if ( this.formTargetId == commentId && this.form.is(':visible') ) { + this.form.hide(); + this.formTargetId = 0; + return; + } + + this.form.insertAfter(commentId ? this.getCommentById(commentId) : this.elements.reply_root).show(); + this.elements.form.comment_id.val(commentId); + + // Показываем необходимые кнопки + if (update) { + this.form.data('update', true); + this.elements.form.update_cancel.show(); + this.elements.form.update_submit.show(); + this.elements.form.submit.hide(); + + // Загружаем исходный текст комментария + this.loadCommentUpdate(commentId); + } else { + this.elements.form.update_cancel.hide(); + this.elements.form.update_submit.hide(); + this.elements.form.submit.show(); + } + + this.formTargetId = commentId; + + if ( focus ) this.elements.form.text.focus(); + }, + + /** + * Обновление текста комментария + * + * @param {Number} commentId ID комментария + */ + loadCommentUpdate: function(commentId) { + var url = this.options.urls.text, + params = { idComment: commentId }; + + ls.utils.formLock(this.form); + ls.hook.marker('loadBefore'); + + ls.ajax.load(url, params, function(response) { + if (response.bStateError) { + ls.msg.error(null, response.sMsg); + } else { + this.elements.form.text.val(response.sText); + } + + ls.hook.run('ls_comments_load_comment_update_after', [commentId, response]); + + ls.utils.formUnlock(this.form); + }.bind(this)); + }, + + /** + * Редактирование комментария + * + * @param {Number} commentId ID комментария + */ + submitCommentUpdate: function(commentId) { + var data = this.form.serializeJSON(), + url = this.options.urls.update; + + ls.utils.formLock(this.form); + this.previewHide(); + + data.comment_id = commentId; + + ls.ajax.load(url, data, function(response) { + if (response.bStateError) { + ls.msg.error(null, response.sMsg); + } else { + var comment = this.getCommentById(commentId), + commentNew = $( $.trim(response.sHtml) ); + + this.removeCommentById(commentId); + this.comments = this.comments.add(commentNew); + + comment.replaceWith( commentNew ); + comment.find(this.options.selectors.comment.update_timer).stopTime(); + + this.formToggle(commentId, true); + this.initUpdateTimers(); + this.checkFolding(commentNew); + this.scrollToComment(commentNew); + } + + ls.hook.run('ls_comments_submit_comment_update_after', [this.form, commentId, response]); + + ls.utils.formUnlock(this.form); + }.bind(this)); + }, + + /** + * Иниц-ия таймеров + * TODO: Fix + */ + initUpdateTimers: function() { + var _this=this; + $(this.options.selectors.comment.update_timer).each(function(k,el){ + el=$(el); + if (!el.data('isInit')) { + el.data('isInit',true); + el.everyTime(1000,function(){ + var seconds=parseInt(el.data('seconds')); + seconds--; + el.data('seconds',seconds); + if (seconds>0) { + el.text(ls.utils.timeRemaining(seconds)); + } else { + el.parents(_this.options.selectors.comment.comment) + .find(_this.options.selectors.comment.update).hide(); + el.stopTime(); + } + }); + } + }); + }, + + /** + * Показывает кнопку сворачивания у комментариев с дочерними комментариями + * и скрывает у комментариев без них + * + * @param {jQuery} сomment (optional) Комментарий у которого нужно проверить наличие дочерних комментарев, если не указан то проверяется у всех + */ + checkFolding: function(comment) { + if ( ! this.options.folding ) return; + + var comments = comment ? comment : this.getComments(); + + comments.each(function (index, сomment) { + var сomment = $(сomment); + + сomment.find(this.options.selectors.comment.fold)[ сomment.nextAll(this.options.selectors.comment.wrapper).length ? 'show' : 'hide' ](); + }.bind(this)); + }, + + /** + * Сворачивает ветку комментариев + * + * @param {jQuery} сomment Комментарий у которого нужно скрыть дочернии комментарии + */ + fold: function(сomment) { + сomment.removeClass(ls.options.classes.states.open).nextAll(this.options.selectors.comment.wrapper).hide(); + сomment.find(this.options.selectors.comment.fold).removeClass(ls.options.classes.states.open); + + this.onFold(сomment); + }, + + /** + * Разворачивает ветку комментариев + * + * @param {jQuery} comment Комментарий у которого нужно показать дочернии комментарии + */ + unfold: function(comment) { + comment.addClass(ls.options.classes.states.open).nextAll(this.options.selectors.comment.wrapper).show(); + comment.find(this.options.selectors.comment.fold).addClass(ls.options.classes.states.open); + + this.onUnfold(comment); + }, + + /** + * Коллбэк вызываемый после сворачивания ветки комментариев + * + * @param {jQuery} comment Комментарий + */ + onFold: function(comment) { + comment.find(this.options.selectors.comment.fold).find('a').text(ls.lang.get('comments.folding.unfold')); + }, + + /** + * Коллбэк вызываемый после разворачивания ветки комментариев + * + * @param {jQuery} comment Комментарий + */ + onUnfold: function(comment) { + comment.find(this.options.selectors.comment.fold).find('a').text(ls.lang.get('comments.folding.fold')); + }, + + /** + * Сворачивает все ветки комментариев + */ + foldAll: function() { + this.getComments().filter('.' + ls.options.classes.states.open).each(function (index, comment) { + this.fold( $(comment) ); + }.bind(this)); + }, + + /** + * Разворачивает все ветки комментариев + */ + unfoldAll: function() { + this.getComments().not('.' + ls.options.classes.states.open).each(function (index, comment) { + this.unfold( $(comment) ); + }.bind(this)); + }, + + /** + * Прокрутка к комментарию + * + * @param {jQuery} comment Комментарий + */ + scrollToComment: function(comment) { + this.setCommentCurrent(comment); + + $.scrollTo(comment, 1000, { offset: -250 }); + }, + + /** + * Прокрутка к родительскому комментарию + * + * @param {Number} commentId ID комментария + * @param {Number} commentParentId ID родительского комментария + */ + scrollToParentComment: function(commentId, commentParentId) { + var child = this.getCommentById(commentId), + parent = this.getCommentById(commentParentId), + scroll = parent.find(this.options.selectors.comment.scroll_to_child); + + // Скрываем кнопку прокрутки к дочернему комментарию у текущего комментария + this.getCommentCurrent().find(this.options.selectors.comment.scroll_to_child).off().hide(); + + // Прокрутка к родительскому комментарию + this.scrollToComment(parent); + + // Прокрутка обратно к дочернему комментарию + scroll.show().off().one('click', function() { + scroll.hide(); + this.scrollToComment(child); + }.bind(this)); + }, + + /** + * Получает комментарий по его ID + * + * @param {Number} commentId ID комментария + * @return {jQuery} Комментарий + */ + getCommentById: function(commentId) { + return this.getComments().filter('[data-id=' + commentId + ']'); + }, + + /** + * Удаляет комментарий по его ID + * + * @param {Number} commentId ID комментария + */ + removeCommentById: function(commentId) { + this.comments = this.getComments().not('[data-id=' + commentId + ']'); + }, + + /** + * Устанавливает текущий комментарий + * + * @param {Object} comment + */ + setCommentCurrent: function(comment) { + this.getCommentCurrent().removeClass(this.options.classes.states.current); + comment.addClass(this.options.classes.states.current); + this.currentCommentId = comment.data('id'); + }, + + /** + * Получает текущий комментарий + * + * @return {jQuery} Текущий комментарий + */ + getCommentCurrent: function() { + return this.getCommentById(this.currentCommentId); + }, + + /** + * Получает новые комментарии + * + * @return {Array} Массив с новыми комментариями + */ + getCommentsNew: function() { + return this.getComments().filter('.' + this.options.classes.states.new); + }, + + /** + * Получает комментарии + * + * @return {Array} Массив с комментариями + */ + getComments: function() { + return this.comments; + } + }); +})(jQuery); \ No newline at end of file diff --git a/application/frontend/i18n/ru.php b/application/frontend/i18n/ru.php index 7eefe1b8..353278ca 100644 --- a/application/frontend/i18n/ru.php +++ b/application/frontend/i18n/ru.php @@ -466,10 +466,11 @@ return array( * Комментарии */ 'comments' => array( - 'comments_declension' => 'комментарий;комментария;комментариев', - 'count_new' => 'Число новых комментариев', - 'title' => 'Комментарии', - 'subscribe' => 'Подписаться на новые комментарии', + 'comments_declension' => '%%count%% комментарий;%%count%% комментария;%%count%% комментариев', + 'count_new' => 'Число новых комментариев', + 'title' => 'Комментарии', + 'subscribe' => 'Подписаться', + 'unsubscribe' => 'Отписаться', // Комментарий 'comment' => array( diff --git a/application/frontend/skin/developer/actions/ActionBlog/topic.tpl b/application/frontend/skin/developer/actions/ActionBlog/topic.tpl index e41d456d..498cd042 100644 --- a/application/frontend/skin/developer/actions/ActionBlog/topic.tpl +++ b/application/frontend/skin/developer/actions/ActionBlog/topic.tpl @@ -5,18 +5,25 @@ {extends 'layouts/layout.base.tpl'} {block 'layout_content'} + {* Топик *} {include 'topics/topic.tpl'} - {include 'comments/comment_tree.tpl' + + {* Комментарии *} + {include 'components/comment/comment-list.tpl' + sClasses = 'js-comments-topic' iTargetId = $oTopic->getId() iAuthorId = $oTopic->getUserId() + aComments = $aComments sAuthorNotice = $aLang.topic_author sTargetType = 'topic' iCountComment = $oTopic->getCountComment() sDateReadLast = $oTopic->getDateRead() - bForbidNewComment = $oTopic->getForbidComment() + bForbidAdd = $oTopic->getForbidComment() sNoticeNotAllow = $aLang.topic_comment_notallow sNoticeCommentAdd = $aLang.topic_comment_add bAllowSubscribe = true oSubscribeComment = $oTopic->getSubscribeNewComment() - aPagingCmt = $aPagingCmt} + aPagingCmt = $aPagingCmt + bShowVote = true + bShowFavourite = true} {/block} \ No newline at end of file diff --git a/application/frontend/skin/developer/actions/ActionComments/index.tpl b/application/frontend/skin/developer/actions/ActionComments/index.tpl deleted file mode 100644 index eec2d0ee..00000000 --- a/application/frontend/skin/developer/actions/ActionComments/index.tpl +++ /dev/null @@ -1,7 +0,0 @@ -{extends file='layouts/layout.base.tpl'} - -{block name='layout_page_title'}{$aLang.comments_all}{/block} - -{block name='layout_content'} - {include file='comments/comment_list.tpl'} -{/block} \ No newline at end of file diff --git a/application/frontend/skin/developer/assets/css/comments.css b/application/frontend/skin/developer/assets/css/comments.css index df5c136c..06c1431e 100644 --- a/application/frontend/skin/developer/assets/css/comments.css +++ b/application/frontend/skin/developer/assets/css/comments.css @@ -13,11 +13,14 @@ /** * Блок со списком комментариев */ -.comments { margin-bottom: 30px; } +.comments { } .comments-header { margin-bottom: 20px; } .comments-title { font-size: 24px; margin-bottom: 5px; } +.comments-actions { padding: 0; background-color: transparent; } + +.comment-list { margin-bottom: 30px; } /** * Вспомогательный блок-обертка @@ -28,7 +31,7 @@ /** * Предпросмотр текста комментария */ -.comment-preview { padding: 15px; margin: 0 0 10px 0; border: 1px solid #eee; } +.comment-preview { padding: 15px; margin: 10px 0 10px 0; border: 1px solid #eee; } .comment-wrapper .comment-preview { margin-left: 25px; } @@ -45,66 +48,108 @@ */ .comment { min-height: 48px; - padding: 10px 10px 10px 68px; + padding: 15px 15px 15px 80px; + margin-bottom: 2px; position: relative; - border-top: 1px solid #eee; - background: #fff; + background: #fafafa; } -.comment.comment-self { background: #c5f7ea; } +.comment--self { background: #c5f7ea; } -.comment.comment-new { background: #fbfba8; } +.comment--new { background: #fbfba8; } -.comment.comment-current { background: #a5e7fa; } +.comment--current { background: #a5e7fa; } -.comment.comment-bad { opacity: 0.3; filter: alpha(opacity=30); } -.comment.comment-bad:hover { opacity: 1; filter: alpha(opacity=100); } +.comment--bad { opacity: 0.3; filter: alpha(opacity=30); } +.comment--bad:hover { opacity: 1; filter: alpha(opacity=100); } -.comment.comment-deleted { background: #efd5d5; } -.ls-user-role-not-admin .comment.comment-deleted { padding: 10px 15px; min-height: 0; background: #f7f7f7; color: #888; } +.comment.comment--deleted { background: #efd5d5; } +.ls-user-role-not-admin .comment.comment--deleted { + padding: 10px 15px; + min-height: 0; + background: #f7f7f7; + color: #888; +} .comment.comment-list-item { margin-bottom: 20px; } .comment.comment-list-item .vote .vote-up, .comment.comment-list-item .vote .vote-down { display: none; } /* Аватар */ -.comment-avatar { position: absolute; top: 10px; left: 10px; } +.comment-avatar { + position: absolute; + top: 15px; + left: 15px; + width: 50px; + height: 50px; + border-radius: 50%; + overflow: hidden; +} +.comment-avatar img { width: 50px; height: 50px; } /* Информация */ -.comment-info { padding: 0 70px 0 0; margin-bottom: 10px; line-height: 1.3em; position: relative; overflow: hidden; } -.comment-info li { float: left; margin-right: 10px; } -.comment-info a { text-decoration: none; } -.comment-date a { color: #999; border-color: #999; } -.comment-info .vote { position: absolute; top: 0; right: 0; margin: 0; } -.comment-scroll-to-child { display: none; } -.comment-username { font-weight: bold; } -.comment-username-author { background: #2891D3; padding: 0 3px; } -.comment-username-author a { color: #fff; } +.comment-info { padding: 0 70px 0 0; margin-bottom: 15px; line-height: 1.3em; position: relative; } +.comment-info li { float: left; margin-right: 10px; } +.comment-info a { text-decoration: none; } -/* Содержимое комментария */ -.comment-content.text { font-size: 13px; } +/* Логин */ +.comment-info .comment-username { float: none; font: 20px/1.3em "Open Sans", sans-serif; margin-bottom: 5px; } + +/* Избранное */ +.comment-favourite { + display: none; + position: absolute; + width: 50px; + top: 65px; + left: -65px; + text-align: center; +} + +.comment-favourite.favourite--added, +.comment-favourite.favourite--has-counter, +.comment:hover .comment-favourite { display: block; } + +/* Дата */ +.comment-date a { color: #999; } +.comment-date a:hover { color: #777; } + +/* Голосование */ +.comment-vote { position: absolute; top: 0; right: 0; margin: 0; } + +.comment-vote.vote--not-voted.vote--count-zero { display: none; } +.comment:hover .comment-vote { display: block; } + +/* Прокрутка к дочернему комментарию */ +.comment-scroll-to { cursor: pointer; } +.comment-scroll-to-child { display: none; } + +/* Текст комментария */ +.comment-content.text { font-size: 13px; line-height: 1.7em; } .comment-content.text blockquote { background: #fff; border-color: #ccc; padding: 5px 10px; margin-bottom: 5px; } -/* Кнопки */ -.comment-actions { margin-top: 10px; } -.comment-actions li { display: inline; margin-right: 10px; } +/* Действия */ +.comment-actions li { float: left; margin: 10px 10px 0 0; } /* Сворачивание */ -.comment-fold { background: #CBCBF3; } +.comment-fold { background: #CBCBF3; display: none; } .comment-fold.open { background: transparent; } +/* Информация о редактировании */ +.comment-edit-info { margin-top: 10px; font-size: 11px; opacity: .5; } + /** * Форма комментирования * * @template comments/comment.form.tpl */ -.comment-form { padding: 15px; background: #fafafa; } +.comment-form { padding: 15px; margin-bottom: 2px; background: #fafafa; } .comment-form textarea { height: 150px; } .comment-wrapper .comment-form { margin-left: 25px; } -.comment-reply-root { font-size: 20px; margin-bottom: 15px; } +.comment-reply-root { font-size: 20px; margin-bottom: 0; } +.comment-reply-root + .comment-form { margin-top: 15px; } /** diff --git a/application/frontend/skin/developer/assets/js/init.js b/application/frontend/skin/developer/assets/js/init.js index 0a465ce8..3da6c610 100644 --- a/application/frontend/skin/developer/assets/js/init.js +++ b/application/frontend/skin/developer/assets/js/init.js @@ -98,18 +98,6 @@ jQuery(document).ready(function($){ $(window)._scrollable(); - /** - * Toolbar - */ - $('.js-toolbar').toolbar({ - target: '.grid-role-wrapper', - offsetX: 20 - }); - - ls.toolbar.topic.init(); // Тул-бар топиков - ls.toolbar.up.init(); // Кнопка "UP" - - /** * Code highlight */ @@ -137,7 +125,33 @@ jQuery(document).ready(function($){ /** * Comments */ - ls.comments.init(); + $('.js-comments-topic').lsComments({ + urls: { + add: aRouter['blog'] + 'ajaxaddcomment/', + load: aRouter['blog'] + 'ajaxresponsecomment/' + }, + }); + + $('.js-comments-talk').lsComments({ + urls: { + add: aRouter['talk'] + 'ajaxaddcomment/', + load: aRouter['talk'] + 'ajaxresponsecomment/' + }, + }); + + + /** + * Toolbar + */ + $('.js-toolbar').toolbar({ + target: '.grid-role-wrapper', + offsetX: 20 + }); + + ls.toolbar.topic.init(); // Тул-бар топиков + ls.toolbar.up.init(); // Кнопка "UP" + + $('.js-toolbar-comments').lsToolbarComments(); /** @@ -217,10 +231,12 @@ jQuery(document).ready(function($){ }); // Голосование за комментарий - $('.js-vote-comment').vote({ - urls: { - vote: aRouter['ajax'] + 'vote/comment/' - } + $('.js-vote-comment').livequery(function () { + $(this).vote({ + urls: { + vote: aRouter['ajax'] + 'vote/comment/' + } + }); }); diff --git a/application/frontend/skin/developer/blocks/block.stream.tpl b/application/frontend/skin/developer/blocks/block.stream.tpl index 4ee16d26..2fd64f5a 100644 --- a/application/frontend/skin/developer/blocks/block.stream.tpl +++ b/application/frontend/skin/developer/blocks/block.stream.tpl @@ -6,7 +6,7 @@ {extends file='blocks/block.aside.base.tpl'} -{block name='block_title'}{$aLang.block_stream}{/block} +{block name='block_title'}{$aLang.stream_menu}{/block} {block name='block_type'}stream{/block} {block name='block_class'}block-nopadding{/block} diff --git a/application/frontend/skin/developer/blocks/block.stream_comment.tpl b/application/frontend/skin/developer/blocks/block.stream_comment.tpl index a0a4529d..79347e0d 100644 --- a/application/frontend/skin/developer/blocks/block.stream_comment.tpl +++ b/application/frontend/skin/developer/blocks/block.stream_comment.tpl @@ -20,8 +20,11 @@ {$oTopic->getTitle()|escape:'html'}

- | - {$oTopic->getCountComment()} {$oTopic->getCountComment()|declension:$aLang.comments.comments_declension} + | + + {lang name='comments.comments_declension' count=$oTopic->getCountComment() plural=true}

{/foreach} @@ -29,5 +32,5 @@ \ No newline at end of file diff --git a/application/frontend/skin/developer/blocks/block.stream_topic.tpl b/application/frontend/skin/developer/blocks/block.stream_topic.tpl index 253c080f..c9c19957 100644 --- a/application/frontend/skin/developer/blocks/block.stream_topic.tpl +++ b/application/frontend/skin/developer/blocks/block.stream_topic.tpl @@ -19,8 +19,11 @@ {$oTopic->getTitle()|escape:'html'}

- | - {$oTopic->getCountComment()} {$oTopic->getCountComment()|declension:$aLang.comments.comments_declension} + | + + {lang name='comments.comments_declension' count=$oTopic->getCountComment() plural=true}

{/foreach} @@ -28,5 +31,5 @@ \ No newline at end of file diff --git a/application/frontend/skin/developer/comments/comment.form.tpl b/application/frontend/skin/developer/comments/comment.form.tpl deleted file mode 100644 index 91735e98..00000000 --- a/application/frontend/skin/developer/comments/comment.form.tpl +++ /dev/null @@ -1,35 +0,0 @@ -{** - * Форма комментирования - * - * @param integer $iTargetId - * @param string $sTargetType - * - * @styles css/comments.css - *} - -{* Подключение редактора *} - - -{* Форма *} -
- {hook run='form_add_comment_begin'} - - {* Текст комментария *} - {include 'components/editor/editor.tpl' sSet='light' sName='comment_text' sId='form_comment_text' bShowHelp=false sMediaTargetType='comment'} - - {hook run='form_add_comment_end'} - - {* Скрытые поля *} - {include 'components/field/field.hidden.tpl' sName='reply' sValue='0' sId='form_comment_reply'} - {include 'components/field/field.hidden.tpl' sName='cmt_target_id' sValue=$iTargetId} - - {* Кнопки создания *} - {include 'components/button/button.tpl' sName='submit_comment' sText=$aLang.common.add sMods='primary' sClasses='js-comment-form-submit'} - - {* Кнопки редактирования *} - {include 'components/button/button.tpl' sName='submit_comment' sType='button' sText=$aLang.common.save sMods='primary' sClasses='js-comment-form-update-submit hide'} - {include 'components/button/button.tpl' sName='submit_comment' sType='button' sText=$aLang.common.cancel sClasses='js-comment-form-update-cancel fl-r hide'} - - {* Общие кнопки *} - {include 'components/button/button.tpl' sText=$aLang.common.preview_text sType='button' sClasses='js-comment-form-preview'} -
\ No newline at end of file diff --git a/application/frontend/skin/developer/comments/comment.tpl b/application/frontend/skin/developer/comments/comment.tpl deleted file mode 100644 index f7bfd57f..00000000 --- a/application/frontend/skin/developer/comments/comment.tpl +++ /dev/null @@ -1,130 +0,0 @@ -{** - * Комментарий - * - * @param boolean bAllowNewComment true если разрешно добавлять новые комментарии - * @param boolean bOneComment - * @param boolean bNoCommentFavourites true если не нужно выводить кнопку добавления в избранное - * @param integer iAuthorId ID автора топика - * @param boolean bList true если комментарий выводится в списках (например на странице Избранные комментарии) - * - * @styles css/comments.css - *} - -{$oUser = $oComment->getUser()} - - -{* Выводим ссылки на блог и топик в котором находится комментарий (только в списках) *} -{if $bList} - {$oTopic = $oComment->getTarget()} - {$oBlog = $oTopic->getBlog()} - - -{/if} - - -{* Комментарий *} -
- {if ! $oComment->getDelete() or ($oUserCurrent and $oUserCurrent->isAdministrator())} - {* Аватар пользователя *} - - {$oUser->getDisplayName()} - - - {* Информация *} -
    - {* Автор комментария *} -
  • - {$oUser->getDisplayName()} -
  • - - {* Дата *} -
  • - - - -
  • - - {* Прокрутка к родительскии/дочернии комментариям *} - {if ! $bList and $oComment->getPid()} -
  • - {/if} - -
  • - - {* Голосование *} - {if $oComment->getTargetType() != 'talk'} -
  • {include 'components/vote/vote.tpl' sClasses='js-vote-comment' oObject=$oComment bIsLocked=($oUserCurrent && $oUserCurrent->getId() == $oUser->getId())}
  • - {/if} - - {* Избранное *} - {if $oUserCurrent and ! $bNoCommentFavourites} -
  • {include 'components/favourite/favourite.tpl' sClasses='js-favourite-comment' oObject=$oComment}
  • - {/if} -
- - - {* Текст комментария *} -
- {$oComment->getText()} -
- - {* Информация о редактировании *} - {if $oComment->getDateEdit()} -
- {$aLang.comments.comment.edit_info}: {date_format date=$oComment->getDateEdit() hours_back="12" minutes_back="60" now="60" day="day H:i" format="j F Y, H:i"} - {if $oComment->getCountEdit()>1} - ({$oComment->getCountEdit()} {$oComment->getCountEdit()|declension:$aLang.common.times_declension}) - {/if} -
- {/if} - - {* Кнопки ответа, удаления и т.д. *} - {if $oUserCurrent} - - {/if} - {else} - {$aLang.comments.comment.deleted} - {/if} -
\ No newline at end of file diff --git a/application/frontend/skin/developer/comments/comment_list.tpl b/application/frontend/skin/developer/comments/comment_list.tpl deleted file mode 100644 index b282d53d..00000000 --- a/application/frontend/skin/developer/comments/comment_list.tpl +++ /dev/null @@ -1,11 +0,0 @@ -{** - * Список комментариев - * - * @styles css/comments.css - *} - -{foreach $aComments as $oComment} - {include file='comments/comment.tpl' bList=true} -{/foreach} - -{include 'components/pagination/pagination.tpl' aPaging=$aPaging} \ No newline at end of file diff --git a/application/frontend/skin/developer/comments/comment_pagination.tpl b/application/frontend/skin/developer/comments/comment_pagination.tpl deleted file mode 100644 index fbadf50e..00000000 --- a/application/frontend/skin/developer/comments/comment_pagination.tpl +++ /dev/null @@ -1,86 +0,0 @@ -{** - * Пагинация комментариев - * - * @styles assets/css/common.css - *} - -{if $aPagingCmt and $aPagingCmt.iCountPage>1} - {if $aPagingCmt.sGetParams} - {$sGetSep = '&'} - {else} - {$sGetSep = '?'} - {/if} - - -{/if} \ No newline at end of file diff --git a/application/frontend/skin/developer/comments/comment_tree.tpl b/application/frontend/skin/developer/comments/comment_tree.tpl deleted file mode 100644 index cc490f80..00000000 --- a/application/frontend/skin/developer/comments/comment_tree.tpl +++ /dev/null @@ -1,104 +0,0 @@ -{** - * Комментарии - * - * @styles css/comments.css - *} - -{* Добавляем в тулбар кнопку обновления комментариев *} -{add_block group='toolbar' name='toolbar/toolbar.comment.tpl' - aPagingCmt = $aPagingCmt - iTargetId = $iTargetId - sTargetType = $sTargetType - iMaxIdComment = $iMaxIdComment} - - -{hook run='comment_tree_begin' iTargetId=$iTargetId sTargetType=$sTargetType} - - -{** - * Комментарии - *} -
- {** - * Хидер - *} - {if ! $bForbidNewComment || ( $bForbidNewComment && $iCountComment )} -
-

{$iCountComment} {$iCountComment|declension:$aLang.comments.comments_declension}

- - {* Подписка на комментарии *} - {if $bAllowSubscribe and $oUserCurrent} -


- {/if} - - {* Свернуть/развернуть все *} - {$aLang.comments.folding.fold_all} | - {$aLang.comments.folding.unfold_all} -
- {/if} - - {** - * Комментарии - *} - {$iCurrentLevel = -1} - {$iMaxLevel = Config::Get('module.comment.max_tree')} - - {foreach $aComments as $oComment} - {$iCommentLevel = $oComment->getLevel()} - - {if $iCommentLevel > $iMaxLevel} - {$iCommentLevel = $iMaxLevel} - {/if} - - {if $iCurrentLevel > $iCommentLevel} - {section name=closelist1 loop=$iCurrentLevel - $iCommentLevel + 1}
{/section} - {elseif $iCurrentLevel == $iCommentLevel && ! $oComment@first} - - {/if} - -
- - {include './comment.tpl'} - - {$iCurrentLevel = $iCommentLevel} - - {if $oComment@last} - {section name=closelist2 loop=$iCurrentLevel + 1}
{/section} - {/if} - {/foreach} - - - -{** - * Пагинация - *} -{include './comment_pagination.tpl' aPagingCmt=$aPagingCmt} - -{hook run='comment_tree_end' iTargetId=$iTargetId sTargetType=$sTargetType} - - -{** - * Форма добавления комментария - *} -{if $bForbidNewComment} - {include 'components/alert/alert.tpl' sMods='info' mAlerts=$sNoticeNotAllow} -{else} - {if $oUserCurrent} - {* Ссылка открывающая форму *} -

- {$sNoticeCommentAdd} -

- - {include './comment.form.tpl'} - {else} - {include 'components/alert/alert.tpl' sMods='info' mAlerts=$aLang.comments.alerts.unregistered} - {/if} -{/if} \ No newline at end of file diff --git a/application/frontend/skin/developer/components/article/article.tpl b/application/frontend/skin/developer/components/article/article.tpl index e819daeb..5ac21c89 100644 --- a/application/frontend/skin/developer/components/article/article.tpl +++ b/application/frontend/skin/developer/components/article/article.tpl @@ -105,7 +105,10 @@ {* Не показываем если комментирование запрещено и кол-во комментариев равно нулю *} {if $bTopicList && ( ! $oTopic->getForbidComment() || ( $oTopic->getForbidComment() && $oEntry->getCountComment() ) )}
  • - {$oEntry->getCountComment()} {$oEntry->getCountComment()|declension:$aLang.comments.comments_declension} + + {lang name='comments.comments_declension' count=$oEntry->getCountComment() plural=true} + + {if $oEntry->getCountCommentNew()}+{$oEntry->getCountCommentNew()}{/if}
  • {/if} diff --git a/application/frontend/skin/developer/components/comment/comment-form.tpl b/application/frontend/skin/developer/components/comment/comment-form.tpl new file mode 100644 index 00000000..1405ad69 --- /dev/null +++ b/application/frontend/skin/developer/components/comment/comment-form.tpl @@ -0,0 +1,63 @@ +{** + * Форма комментирования + * + * @param integer $iTargetId + * @param string $sTargetType + * @param string $sClasses Дополнительные классы + * @param string $sAttributes Атрибуты + * @param string $sMods Модификаторы + * @param string $sEditorSet (light) Стиль редактора + * + * @styles css/comments.css + *} + +{* Название компонента *} +{$sComponent = 'comment-form'} + +{* Переменные *} +{$iTargetId = $smarty.local.iTargetId} +{$sTargetType = $smarty.local.sTargetType} + + +{* Форма *} +
    + + {block 'comment-form'} + {hook run='comment-form-begin'} + + {block 'comment-form-fields'} + {* Скрытые поля *} + {include 'components/field/field.hidden.tpl' sName='reply' sValue='0' sId='form_comment_reply'} + {include 'components/field/field.hidden.tpl' sName='cmt_target_id' sValue=$iTargetId} + + {* Текст комментария *} + {include 'components/editor/editor.tpl' + sSet = $smarty.local.sEditorSet|default:'light' + sName = 'comment_text' + sInputClasses = 'js-comment-form-text' + bShowHelp = false + sMediaTargetType = 'comment'} + {/block} + + {hook run='comment-form-end'} + + {** + * Кнопки + *} + + {* Кнопка добавления *} + {include 'components/button/button.tpl' sName='submit_comment' sText=$aLang.common.add sMods='primary' sClasses='js-comment-form-submit'} + + {* Кнопки редактирования *} + {include 'components/button/button.tpl' sName='submit_comment' sType='button' sText=$aLang.common.save sMods='primary' sClasses='js-comment-form-update-submit hide'} + {include 'components/button/button.tpl' sName='submit_comment' sType='button' sText=$aLang.common.cancel sClasses='js-comment-form-update-cancel fl-r hide'} + + {* Кнопка превью текста *} + {include 'components/button/button.tpl' sText=$aLang.common.preview_text sType='button' sClasses='js-comment-form-preview'} + {/block} +
    \ No newline at end of file diff --git a/application/frontend/skin/developer/components/comment/comment-list.tpl b/application/frontend/skin/developer/components/comment/comment-list.tpl new file mode 100644 index 00000000..d775e850 --- /dev/null +++ b/application/frontend/skin/developer/components/comment/comment-list.tpl @@ -0,0 +1,102 @@ +{** + * Комментарии + * + * @param string $sTargetType + * @param integer $iTargetId + * @param array $aComments + * @param boolean $bForbidAdd + * @param string $sHeading + * @param integer $iCountComment + * @param boolean $bAllowSubscribe + * + * @styles css/comments.css + *} + +{block 'comment-list-options'} + {$iTargetId = $smarty.local.iTargetId} + {$sTargetType = $smarty.local.sTargetType} + {$iCountComment = $smarty.local.iCountComment} +{/block} + +{add_block group='toolbar' name='toolbar/toolbar.comment.tpl' target='.js-comment'} + +
    + {** + * Заголовок + *} +
    +

    + {lang name='comments.comments_declension' count=$iCountComment plural=true} +

    +
    + + + {** + * Экшнбар + *} + + {* Свернуть/развернуть все комментарии *} + {$aItems = [ [ 'classes' => 'js-comments-fold-all-toggle', 'text' => $aLang.comments.folding.fold_all ] ]} + + {* Подписка на комментарии *} + {if $bAllowSubscribe and $oUserCurrent} + {* Подписан пользователь на комментарии или нет *} + {$bIsSubscribed = $oSubscribeComment && $oSubscribeComment->getStatus()} + + {$aItems[] = [ + 'classes' => "comments-subscribe js-comments-subscribe {if $bIsSubscribed}active{/if}", + 'attributes' => "data-type=\"{$sTargetType}\" data-target-id=\"{$iTargetId}\"", + 'text' => ( $bIsSubscribed ) ? $aLang.comments.unsubscribe : $aLang.comments.subscribe + ]} + {/if} + + {* TODO: Добавить хук *} + + {include 'components/actionbar/actionbar.tpl' aItems=$aItems sClasses='comments-actions'} + + + {** + * Комментарии + *} +
    + {include './comment-tree.tpl' + aComments = $smarty.local.aComments + bForbidAdd = $bForbidAdd + bShowFavourite = $smarty.local.bShowFavourite + bShowVote = $smarty.local.bShowVote} +
    + + + {** + * TODO: Пагинация + *} + {*include 'comments/comment_pagination.tpl' aPagingCmt=$aPagingCmt*} + + + {** + * Форма добавления комментария + *} + + {* Проверяем запрещено комментирование или нет *} + {if $bForbidAdd} + {include 'components/alert/alert.tpl' sMods='info' mAlerts=$sNoticeNotAllow} + + {* Если разрешено то показываем форму добавления комментария *} + {else} + {if $oUserCurrent} + {* Кнопка открывающая форму *} +

    + {$sNoticeCommentAdd} +

    + + {* Форма добавления комментария *} + {include './comment-form.tpl' sTargetType=$sTargetType iTargetId=$iTargetId} + {else} + {include 'components/alert/alert.tpl' sMods='info' mAlerts=$aLang.comments.alerts.unregistered} + {/if} + {/if} +
    \ No newline at end of file diff --git a/application/frontend/skin/developer/components/comment/comment-tree.tpl b/application/frontend/skin/developer/components/comment/comment-tree.tpl new file mode 100644 index 00000000..dfe1f9ea --- /dev/null +++ b/application/frontend/skin/developer/components/comment/comment-tree.tpl @@ -0,0 +1,62 @@ +{** + * Дерево комментариев + * + * @component comment + * @styles css/comments.css + * @scripts js/comments.js + * + * @param array $aComments Комментарии + * @param string $sClasses Дополнительные классы + * @param string $sAttributes Атрибуты + * @param string $sMods + * @param boolean $bShowVote (true) Показывать или нет голосование + * @param boolean $bShowReply (true) Показывать или нет кнопку Ответить + * @param integer $iAuthorId + * @param string $sDateReadLast + *} + +{* Текущая вложенность *} +{$iCurrentLevel = -1} + +{* Максимальная вложенность *} +{$iMaxLevel = $smarty.local.iMaxLevel|default:Config::Get('module.comment.max_tree')} + +{* Добавляем возможность переопределить стандартный шаблон комментария *} +{$sTemplate = $smarty.local.template|default:'./comment.tpl'} + +{* Построение дерева комментариев *} +{foreach $smarty.local.aComments as $oComment} + {* Ограничиваем вложенность комментария максимальным значением *} + {$iCommentLevel = ( $oComment->getLevel() > $iMaxLevel ) ? $iMaxLevel : $oComment->getLevel()} + + {* Закрываем блоки-обертки *} + {if $iCurrentLevel > $iCommentLevel} + {section closewrappers1 loop=$iCurrentLevel - $iCommentLevel + 1}{/section} + {elseif $iCurrentLevel == $iCommentLevel && ! $oComment@first} + + {/if} + + {* Устанавливаем текущий уровень вложенности *} + {$iCurrentLevel = $iCommentLevel} + + {* Вспомогательный блок-обертка *} +
    + + {* Комментарий *} + {include "$sTemplate" + oComment = $oComment + bShowVote = $smarty.local.bShowVote + bShowReply = ! $smarty.local.bForbidAdd + bShowFavourite = $smarty.local.bShowFavourite + sDateReadLast = $sDateReadLast + bIsHidden = $oComment->getDelete() + bShowScroll = true + bShowEdit = true} + + {* Закрываем блоки-обертки после последнего комментария *} + {if $oComment@last} + {section closewrappers2 loop=$iCurrentLevel + 1}
    {/section} + {/if} +{foreachelse} + {include 'components/alert/alert.tpl' sMods='empty' mAlerts=$aLang.common.empty} +{/foreach} \ No newline at end of file diff --git a/application/frontend/skin/developer/components/comment/comment.tpl b/application/frontend/skin/developer/components/comment/comment.tpl new file mode 100644 index 00000000..7229052b --- /dev/null +++ b/application/frontend/skin/developer/components/comment/comment.tpl @@ -0,0 +1,190 @@ +{** + * Комментарий + * + * @component comment + * @styles css/comments.css + * @scripts js/comments.js + * + * @param object $oComment Комментарий + * @param string $sClasses Дополнительные классы + * @param string $sAttributes Атрибуты + * @param string $sMods Модификаторы + * @param boolean $bShowVote (true) Показывать или нет голосование + * @param boolean $bShowReply (true) Показывать или нет кнопку Ответить + * @param integer $iAuthorId + * @param string $sDateReadLast + *} + +{* Название компонента *} +{$sComponent = 'comment'} + +{* Переменные *} +{$oComment = $smarty.local.oComment} +{$sMods = $smarty.local.sMods} +{$bShowEdit = $smarty.local.bShowEdit|default:true} +{$bIsHidden = $smarty.local.bIsHidden} +{$oUser = $oComment->getUser()} +{$iCommentId = $oComment->getId()} + +{* Получаем ссылку на комментарий *} +{* TODO: Вынести в бэкенд *} +{$sPermalink = ( Config::Get('module.comment.use_nested') ) ? "{router page='comments'}{$iCommentId}" : "#comment{$iCommentId}"} + +{** + * Добавляем модификаторы + *} + +{* Комментарий с отрицательным рейтингом *} +{if $smarty.local.bShowVote && $oComment->isBad()} + {$sMods = "$sMods bad"} +{/if} + +{* Автор комментария является автором объекта к которому оставлен комментарий *} +{if $smarty.local.iAuthorId == $oUser->getId()} + {$sMods = "$sMods author"} +{/if} + +{* Комментарий удален *} +{if $bIsHidden} + {$sMods = "$sMods deleted"} + +{* Комментарий текущего залогиненого пользователя *} +{elseif $oUserCurrent && $oComment->getUserId() == $oUserCurrent->getId()} + {$sMods = "$sMods self"} + +{* Непрочитанный комментарий *} +{elseif $smarty.local.sDateReadLast && strtotime($smarty.local.sDateReadLast) <= strtotime($oComment->getDate())} + {$sMods = "$sMods new"} +{/if} + + +{** + * Комментарий + * Атрибут id используется для ссылки на комментарий через хэш в урл #comment123 + *} +
    + + {* Показываем удаленные комментарии только администраторам *} + {if ! $bIsHidden || ( $oUserCurrent && $oUserCurrent->isAdministrator() )} + {* Аватар пользователя *} + + {$oUser->getDisplayName()} + + + {* Информация *} +
      + {* Автор комментария *} +
    • + + {$oUser->getDisplayName()} + +
    • + + {* Дата добавления комментария *} + {* Так же является ссылкой на комментарий *} +
    • + + + +
    • + + {* Прокрутка к родительскому комментарию *} + {if $smarty.local.bShowScroll} + {if $oComment->getPid()} +
    • + {/if} + + {* Прокрутка к дочернему комментарию *} +
    • + {/if} + + {* Голосование *} + {if $smarty.local.bShowVote} +
    • + {* Блокируем голосование для гостей или если залогиненый пользователь является автором комментария*} + {include 'components/vote/vote.tpl' + sClasses = "{$sComponent}-vote js-vote-{$sComponent}" + oObject = $oComment + bIsLocked = ($oUserCurrent && $oUserCurrent->getId() == $oUser->getId())} +
    • + {/if} + + {* Избранное *} + {if $oUserCurrent && $smarty.local.bShowFavourite} +
    • + {include 'components/favourite/favourite.tpl' sClasses='comment-favourite js-favourite-comment' oObject=$oComment} +
    • + {/if} +
    + + {* Текст комментария *} +
    + {$oComment->getText()} +
    + + {* Информация о редактировании *} + {if $oComment->getDateEdit()} +
    + {$aLang.comments.comment.edit_info}: + + + {date_format date=$oComment->getDateEdit() hours_back="12" minutes_back="60" now="60" day="day H:i" format="j F Y, H:i"} + + + {if $oComment->getCountEdit() > 1} + ({$oComment->getCountEdit()} {$oComment->getCountEdit()|declension:$aLang.common.times_declension}) + {/if} +
    + {/if} + + {* Действия *} + + {else} + {$aLang.comments.comment.deleted} + {/if} +
    \ No newline at end of file diff --git a/application/frontend/skin/developer/components/comment/tests/visual/comment.tpl b/application/frontend/skin/developer/components/comment/tests/visual/comment.tpl new file mode 100644 index 00000000..a82bbaf2 --- /dev/null +++ b/application/frontend/skin/developer/components/comment/tests/visual/comment.tpl @@ -0,0 +1,40 @@ +{** + * Тестирование компонента comment + *} + +{extends 'layouts/layout.base.tpl'} + +{block 'layout_options'} + {$bNoSidebar = true} +{/block} + +{block 'layout_page_title'} + Component comment +{/block} + +{block 'layout_content'} + {function test_heading} +

    {$text}

    + {/function} + + {* Полная версия *} + {test_heading text='Default'} + +
    +
    + {include 'components/comment/comment.tpl' + oComment = $comment1 + bShowVote = true + sDateLastRead = '2014-01-01 00:00:00'} +
    + +
    + {include 'components/comment/comment.tpl' + comment = $comment2 + bShowVote = true + sDateLastRead = '2014-01-01 00:00:00'} +
    + + {include 'comments/comment.form.tpl'} +
    +{/block} \ No newline at end of file diff --git a/application/frontend/skin/developer/layouts/layout.base.tpl b/application/frontend/skin/developer/layouts/layout.base.tpl index 1a60e267..9af2c029 100644 --- a/application/frontend/skin/developer/layouts/layout.base.tpl +++ b/application/frontend/skin/developer/layouts/layout.base.tpl @@ -11,7 +11,7 @@ {block 'layout_head' append}