1
0
Fork 0
mirror of https://github.com/Oreolek/ifhub.club.git synced 2024-06-16 23:00:51 +03:00

Компонент wall

This commit is contained in:
Denis Shakhov 2014-07-07 17:50:14 +07:00
parent 622d6a84e0
commit 1666bd6048
22 changed files with 890 additions and 510 deletions

View file

@ -69,10 +69,6 @@ class ActionProfile extends Action {
$this->AddEventPreg('/^.+$/i','/^(whois)?$/i','EventWhois');
$this->AddEventPreg('/^.+$/i','/^wall$/i','/^$/i','EventWall');
$this->AddEventPreg('/^.+$/i','/^wall$/i','/^add$/i','EventWallAdd');
$this->AddEventPreg('/^.+$/i','/^wall$/i','/^load$/i','EventWallLoad');
$this->AddEventPreg('/^.+$/i','/^wall$/i','/^load-reply$/i','EventWallLoadReply');
$this->AddEventPreg('/^.+$/i','/^wall$/i','/^remove$/i','EventWallRemove');
$this->AddEventPreg('/^.+$/i','/^favourites$/i','/^comments$/i','/^(page([1-9]\d{0,5}))?$/i','EventFavouriteComments');
$this->AddEventPreg('/^.+$/i','/^favourites$/i','/^(page([1-9]\d{0,5}))?$/i','EventFavourite');
@ -498,180 +494,6 @@ class ActionProfile extends Action {
*/
$this->SetTemplateAction('wall');
}
/**
* Добавление записи на стену
*/
public function EventWallAdd() {
/**
* Устанавливаем формат Ajax ответа
*/
$this->Viewer_SetResponseAjax('json');
/**
* Пользователь авторизован?
*/
if (!$this->oUserCurrent) {
return $this->EventErrorDebug();
}
if (!$this->CheckUserProfile()) {
return $this->EventErrorDebug();
}
/**
* Создаем запись
*/
$oWall=Engine::GetEntity('Wall');
$oWall->_setValidateScenario('add');
$oWall->setWallUserId($this->oUserProfile->getId());
$oWall->setUserId($this->oUserCurrent->getId());
$oWall->setText(getRequestStr('sText'));
$oWall->setPid(getRequestStr('iPid'));
$this->Hook_Run('wall_add_validate_before', array('oWall'=>$oWall));
if ($oWall->_Validate()) {
/**
* Экранируем текст и добавляем запись в БД
*/
$oWall->setText($this->Text_Parser($oWall->getText()));
$this->Hook_Run('wall_add_before', array('oWall'=>$oWall));
if ($this->Wall_AddWall($oWall)) {
$this->Hook_Run('wall_add_after', array('oWall'=>$oWall));
/**
* Отправляем уведомления
*/
if ($oWall->getWallUserId()!=$oWall->getUserId()) {
$this->Notify_SendWallNew($oWall,$this->oUserCurrent);
}
if ($oWallParent=$oWall->GetPidWall() and $oWallParent->getUserId()!=$oWall->getUserId()) {
$this->Notify_SendWallReply($oWallParent,$oWall,$this->oUserCurrent);
}
/**
* Добавляем событие в ленту
*/
$this->Stream_Write($oWall->getUserId(), 'add_wall', $oWall->getId());
} else {
$this->Message_AddError($this->Lang_Get('wall_add_error'),$this->Lang_Get('error'));
}
} else {
$this->Message_AddError($oWall->_getValidateError(),$this->Lang_Get('error'));
}
}
/**
* Удаление записи со стены
*/
public function EventWallRemove() {
/**
* Устанавливаем формат Ajax ответа
*/
$this->Viewer_SetResponseAjax('json');
/**
* Пользователь авторизован?
*/
if (!$this->oUserCurrent) {
return $this->EventErrorDebug();
}
if (!$this->CheckUserProfile()) {
return $this->EventErrorDebug();
}
/**
* Получаем запись
*/
if (!($oWall=$this->Wall_GetWallById(getRequestStr('iId')))) {
return $this->EventErrorDebug();
}
/**
* Если разрешено удаление - удаляем
*/
if ($oWall->isAllowDelete()) {
$this->Wall_DeleteWall($oWall);
return;
}
return $this->EventErrorDebug();
}
/**
* Ajax подгрузка сообщений стены
*/
public function EventWallLoad() {
// Устанавливаем формат Ajax ответа
$this->Viewer_SetResponseAjax('json');
// Валидация
if ( ! $this->CheckUserProfile() ) {
return $this->EventErrorDebug();
}
// Формируем фильтр для запроса к БД
$aFilter = array(
'wall_user_id' => $this->oUserProfile->getId(),
'pid' => null
);
if ( is_numeric(getRequest('iLastId')) ) {
$aFilter['id_less'] = getRequest('iLastId');
} else if ( is_numeric(getRequest('iFirstId')) ) {
$aFilter['id_more'] = getRequest('iFirstId');
} else {
return $this->EventErrorDebug();
}
// Получаем сообщения и формируем ответ
$aWall = $this->Wall_GetWall($aFilter, array('id' => 'desc'), 1, Config::Get('module.wall.per_page'));
$this->Viewer_Assign('aWall', $aWall['collection']);
$this->Viewer_Assign('oUserCurrent', $this->oUserCurrent); // хак, т.к. к этому моменту текущий юзер не загружен в шаблон
$this->Viewer_AssignAjax('sHtml', $this->Viewer_Fetch('actions/ActionProfile/wall.posts.tpl'));
$this->Viewer_AssignAjax('iCountLoaded', count($aWall['collection']));
if (count($aWall['collection'])) {
$this->Viewer_AssignAjax('iLastId', end($aWall['collection'])->getId());
}
}
/**
* Подгрузка ответов на стене к сообщению
*/
public function EventWallLoadReply() {
// Устанавливаем формат Ajax ответа
$this->Viewer_SetResponseAjax('json');
// Валидация
if ( ! $this->CheckUserProfile() ) {
return $this->EventErrorDebug();
}
if ( ! ($oWall = $this->Wall_GetWallById(getRequestStr('iTargetId'))) or $oWall->getPid() ) {
return $this->EventErrorDebug();
}
// Формируем фильтр для запроса к БД
$aFilter = array(
'wall_user_id' => $this->oUserProfile->getId(),
'pid' => $oWall->getId()
);
if ( is_numeric(getRequest('iLastId')) ) {
$aFilter['id_less'] = getRequest('iLastId');
} else if ( is_numeric(getRequest('iFirstId')) ) {
$aFilter['id_more'] = getRequest('iFirstId');
} else {
return $this->EventErrorDebug();
}
// Получаем сообщения и формируем ответ
// Необходимо вернуть все ответы, но ставим "разумное" ограничение
$aWall = $this->Wall_GetWall($aFilter, array('id' => 'asc'), 1, 300);
// Передаем переменные
$this->Viewer_Assign('aReplyWall', $aWall['collection']);
$this->Viewer_AssignAjax('sHtml', $this->Viewer_Fetch('actions/ActionProfile/wall.comments.tpl'));
$this->Viewer_AssignAjax('iCountLoaded', count($aWall['collection']));
if (count($aWall['collection'])) {
$this->Viewer_AssignAjax('iLastId', end($aWall['collection'])->getId());
}
}
/**
* Сохраняет заметку о пользователе
*/

View file

@ -0,0 +1,220 @@
<?php
/*-------------------------------------------------------
*
* LiveStreet Engine Social Networking
* Copyright © 2008 Mzhelskiy Maxim
*
*--------------------------------------------------------
*
* Official site: www.livestreet.ru
* Contact e-mail: rus.engine@gmail.com
*
* GNU General Public License, version 2:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
---------------------------------------------------------
*/
/**
* Стена
*
* @package actions
* @since 1.0
*/
class ActionWall extends Action {
/**
* Инициализация
*/
public function Init() {
$this->oUserCurrent = $this->User_GetUserCurrent();
}
/**
* Регистрируем евенты
*/
protected function RegisterEvent() {
// Добавление поста/комментария
$this->AddEventPreg('/^add$/i', 'EventAdd');
// Удаление поста/комментария
$this->AddEventPreg('/^remove$/i', 'EventRemove');
// Подгрузка постов
$this->AddEventPreg('/^load$/i', 'EventLoad');
// Подгрузка комментариев
$this->AddEventPreg('/^load-comments$/i', 'EventLoadComments');
}
/**
* Проверка корректности профиля
*/
protected function CheckUserProfile() {
if ( ! ( $this->oUserProfile = $this->User_GetUserById( (int) getRequestStr('user_id') ) ) ) {
return false;
}
return true;
}
/**
* Добавление записи на стену
*/
public function EventAdd() {
$this->Viewer_SetResponseAjax('json');
if ( ! $this->oUserCurrent ) {
return $this->EventErrorDebug();
}
if ( ! $this->CheckUserProfile() ) {
return $this->EventErrorDebug();
}
// Создаем запись
$oWall = Engine::GetEntity('Wall');
$oWall->_setValidateScenario('add');
$oWall->setWallUserId($this->oUserProfile->getId());
$oWall->setUserId($this->oUserCurrent->getId());
$oWall->setText(getRequestStr('text'));
$oWall->setPid(getRequestStr('pid'));
$this->Hook_Run('wall_add_validate_before', array( 'oWall' => $oWall ));
if ($oWall->_Validate()) {
// Экранируем текст и добавляем запись в БД
$oWall->setText($this->Text_Parser($oWall->getText()));
$this->Hook_Run('wall_add_before', array('oWall'=>$oWall));
if ( $this->Wall_AddWall($oWall) ) {
$this->Hook_Run('wall_add_after', array('oWall'=>$oWall));
// Отправляем уведомления
if ($oWall->getWallUserId()!=$oWall->getUserId()) {
$this->Notify_SendWallNew($oWall,$this->oUserCurrent);
}
if ($oWallParent=$oWall->GetPidWall() and $oWallParent->getUserId()!=$oWall->getUserId()) {
$this->Notify_SendWallReply($oWallParent,$oWall,$this->oUserCurrent);
}
// Добавляем событие в ленту
$this->Stream_Write($oWall->getUserId(), 'add_wall', $oWall->getId());
} else {
$this->Message_AddError($this->Lang_Get('common.error.add'),$this->Lang_Get('error'));
}
} else {
$this->Message_AddError($oWall->_getValidateError(),$this->Lang_Get('error'));
}
}
/**
* Удаление записи со стены
*/
public function EventRemove() {
$this->Viewer_SetResponseAjax('json');
if ( ! $this->oUserCurrent ) {
return $this->EventErrorDebug();
}
if ( ! $this->CheckUserProfile() ) {
return $this->EventErrorDebug();
}
// Получаем запись
if ( ! ( $oWall = $this->Wall_GetWallById( getRequestStr('id') ) ) ) {
return $this->EventErrorDebug();
}
// Если разрешено удаление - удаляем
if ( $oWall->isAllowDelete() ) {
$this->Wall_DeleteWall($oWall);
return;
}
return $this->EventErrorDebug();
}
/**
* Ajax подгрузка сообщений стены
*/
public function EventLoad() {
$this->Viewer_SetResponseAjax('json');
if ( ! $this->CheckUserProfile() ) {
return $this->EventErrorDebug();
}
// Формируем фильтр для запроса к БД
$aFilter = array(
'wall_user_id' => $this->oUserProfile->getId(),
'pid' => null
);
if ( is_numeric(getRequest('last_id')) ) {
$aFilter['id_less'] = getRequest('last_id');
} else if ( is_numeric(getRequest('first_id')) ) {
$aFilter['id_more'] = getRequest('first_id');
} else {
return $this->EventErrorDebug();
}
// Получаем сообщения и формируем ответ
$aWall = $this->Wall_GetWall($aFilter, array('id' => 'desc'), 1, Config::Get('module.wall.per_page'));
$this->Viewer_Assign('posts', $aWall['collection'], true);
$this->Viewer_Assign('oUserCurrent', $this->oUserCurrent); // хак, т.к. к этому моменту текущий юзер не загружен в шаблон
$this->Viewer_AssignAjax('html', $this->Viewer_Fetch('components/wall/wall.posts.tpl'));
$this->Viewer_AssignAjax('count_loaded', count($aWall['collection']));
if (count($aWall['collection'])) {
$this->Viewer_AssignAjax('last_id', end($aWall['collection'])->getId());
}
}
/**
* Подгрузка комментариев
*/
public function EventLoadComments() {
$this->Viewer_SetResponseAjax('json');
if ( ! $this->CheckUserProfile() ) {
return $this->EventErrorDebug();
}
if ( ! ($oWall = $this->Wall_GetWallById(getRequestStr('target_id'))) or $oWall->getPid() ) {
return $this->EventErrorDebug();
}
// Формируем фильтр для запроса к БД
$aFilter = array(
'wall_user_id' => $this->oUserProfile->getId(),
'pid' => $oWall->getId()
);
if ( is_numeric(getRequest('last_id')) ) {
$aFilter['id_less'] = getRequest('last_id');
} else if ( is_numeric(getRequest('first_id')) ) {
$aFilter['id_more'] = getRequest('first_id');
} else {
return $this->EventErrorDebug();
}
// Получаем сообщения и формируем ответ
// Необходимо вернуть все ответы, но ставим "разумное" ограничение
$aWall = $this->Wall_GetWall($aFilter, array('id' => 'asc'), 1, 300);
// Передаем переменные
$this->Viewer_Assign('comments', $aWall['collection'], true);
$this->Viewer_AssignAjax('html', $this->Viewer_Fetch('components/wall/wall.comments.tpl'));
$this->Viewer_AssignAjax('count_loaded', count($aWall['collection']));
if ( count($aWall['collection']) ) {
$this->Viewer_AssignAjax('last_id', end($aWall['collection'])->getId());
}
}
}

View file

@ -0,0 +1,42 @@
<?php
/*-------------------------------------------------------
*
* LiveStreet Engine Social Networking
* Copyright © 2008 Mzhelskiy Maxim
*
*--------------------------------------------------------
*
* Official site: www.livestreet.ru
* Contact e-mail: rus.engine@gmail.com
*
* GNU General Public License, version 2:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
---------------------------------------------------------
*/
/**
*
*
* @package blocks
* @since 2.0
*/
class BlockWall extends Block {
/**
* Запуск обработки
*/
public function Exec() {
$wall = $this->Wall_GetWall( array( 'wall_user_id' => (int) $this->GetParam('user_id'), 'pid' => null ), array( 'id' => 'desc' ), 1, Config::Get( 'module.wall.per_page' ) );
$posts = $wall['collection'];
$this->Viewer_Assign('posts', $posts);
$this->Viewer_Assign('count', $wall['count']);
$this->Viewer_Assign('classes', $this->GetParam('classes'));
if ( count($posts) ) {
$this->Viewer_Assign('lastId', end($posts)->getId());
}
$this->SetTemplate('components/wall/wall.tpl');
}
}

View file

@ -52,7 +52,7 @@ class ModuleWall_EntityWall extends Entity {
return true;
}
}
return $this->Lang_Get('wall_add_time_limit');
return $this->Lang_Get('wall.notices.error_add_time_limit');
}
/**
* Валидация родительского сообщения
@ -73,7 +73,7 @@ class ModuleWall_EntityWall extends Entity {
return true;
}
}
return $this->Lang_Get('wall_add_pid_error');
return $this->Lang_Get('wall.notices.error_add_pid');
}
/**
* Возвращает родительскую запись
@ -118,4 +118,12 @@ class ModuleWall_EntityWall extends Entity {
public function getUrlWall() {
return $this->getWallUser()->getUserWebPath().'wall/';
}
/**
* Дата добавления
*
* @return string
*/
public function getDate() {
return $this->getDateAdd();
}
}

View file

@ -352,8 +352,9 @@ $config['router']['page']['ajax'] = 'ActionAjax';
$config['router']['page']['feed'] = 'ActionUserfeed';
$config['router']['page']['stream'] = 'ActionStream';
$config['router']['page']['subscribe'] = 'ActionSubscribe';
$config['router']['page']['content'] = 'ActionContent';
$config['router']['page']['property'] = 'ActionProperty';
$config['router']['page']['content'] = 'ActionContent';
$config['router']['page']['property'] = 'ActionProperty';
$config['router']['page']['wall'] = 'ActionWall';
// Глобальные настройки роутинга
$config['router']['config']['default']['action'] = 'index';
$config['router']['config']['default']['event'] = null;
@ -527,6 +528,8 @@ $config['head']['default']['js'] = array(
"___path.application.web___/frontend/common/js/subscribe.js",
"___path.application.web___/frontend/common/js/geo.js",
"___path.application.web___/frontend/common/js/wall.js",
"___path.application.web___/frontend/common/js/wall-form.js",
"___path.application.web___/frontend/common/js/wall-entry.js",
"___path.application.web___/frontend/common/js/usernote.js",
"___path.application.web___/frontend/common/js/comments.js",
"___path.application.web___/frontend/common/js/blog.js",

View file

@ -0,0 +1,124 @@
/**
* Wall entry
*
* @module ls/wall/entry
*
* @license GNU General Public License, version 2
* @copyright 2013 OOO "ЛС-СОФТ" {@link http://livestreetcms.com}
* @author Denis Shakhov <denis.shakhov@gmail.com>
*/
(function($) {
"use strict";
$.widget( "livestreet.lsWallEntry", {
/**
* Дефолтные опции
*/
options: {
wall: null,
// Ссылки
urls: {
remove: null
},
// Селекторы
selectors: {
wrapper: '.js-wall-entry-container',
remove: '.js-comment-remove',
reply: '.js-comment-reply',
}
},
/**
* Конструктор
*
* @constructor
* @private
*/
_create: function () {
var _this = this;
this.elements = {
remove: this.element.find( this.option( 'selectors.remove' ) ),
reply: this.element.find( this.option( 'selectors.reply' ) )
};
// ID поста
this.id = this.element.data( 'id' );
// Тип записи (комментарий/пост)
this.type = this.element.data( 'type' );
// Форма добавления комментария к текущему посту
this.form = this.getType() === 'post' ? this.option( 'wall' ).lsWall( 'getFormById', this.id ) : null;
//
// События
//
// Удаление
this._on( this.elements.remove, {
click: function( event ) {
this.remove();
event.preventDefault();
}
});
// Показать/скрыть форму ответа
this._on( this.elements.reply, {
click: function( event ) {
this.formToggle();
event.preventDefault();
}
});
},
/**
* Показать/скрыть форму ответа
*/
formToggle: function() {
this.form.lsWallForm( 'toggle' );
},
/**
* Возвращает элементы записи
*
* @return {Array} Элементы записи
*/
getElements: function() {
return this.elements;
},
/**
* Возвращает тип записи (комментарий/пост)
*
* @return {String} Тип записи (комментарий/пост)
*/
getType: function() {
return this.type;
},
/**
* Удаление
*/
remove: function() {
ls.ajax.load( this.option( 'urls.remove' ), { user_id: this.option( 'wall' ).lsWall( 'getUserId' ), id: this.id }, this.onRemove.bind( this ) );
},
/**
* Коллбэк вызываемый после удаления
*/
onRemove: function( response ) {
this.element.fadeOut( 'slow', function() {
this.element.remove();
this.option( 'wall' ).lsWall( 'checkEmpty' );
}.bind(this));
this.option( 'wall' ).lsWall( 'getCommentWrapperById', this.id ).fadeOut( 'slow', function () {
$( this ).remove();
});
},
});
})(jQuery);

View file

@ -0,0 +1,147 @@
/**
* Wall form
*
* @module ls/wall/form
*
* @license GNU General Public License, version 2
* @copyright 2013 OOO "ЛС-СОФТ" {@link http://livestreetcms.com}
* @author Denis Shakhov <denis.shakhov@gmail.com>
*/
(function($) {
"use strict";
$.widget( "livestreet.lsWallForm", {
/**
* Дефолтные опции
*/
options: {
wall: null,
// Ссылки
urls: {
add: null
},
// Селекторы
selectors: {
text: '.js-wall-form-text',
button_submit: '.js-wall-form-submit'
}
},
/**
* Конструктор
*
* @constructor
* @private
*/
_create: function () {
var _this = this;
// Элементы
this.elements = {
text: this.element.find( this.option( 'selectors.text' ) ),
submit: this.element.find( this.option( 'selectors.submit' ) )
};
// ID поста
this.id = this.element.data( 'id' );
// Кнопка "Ответить" в посте
this.reply = this.option( 'wall' ).lsWall( 'getEntryById', this.id ).lsWallEntry( 'getElements' ).reply;
// Отправка формы
this._on({ submit: this.submit });
this.elements.text.on( 'keydown' + this.eventNamespace, null, 'ctrl+return', this.submit.bind( this ) );
// Разворачивание формы
this._on( this.elements.text, { click: this.open } );
// Сворачиваем открытые формы
// при клике вне формы или кнопки Ответить
this.document.on( 'mouseup' + this.eventNamespace, function( e ) {
if ( e.which == 1 &&
this.isOpened() &&
! this.element.is( e.target ) &&
( ! this.reply || ( this.reply && ! this.reply.is( e.target ) ) ) &&
this.element.has( e.target ).length === 0 &&
! this.elements.text.val() ) {
// Сворачиваем форму если у поста формы есть комментарии или если форма корневая
if ( this.option( 'wall' ).lsWall( 'getCommentsByPostId', this.id ).length || this.id === 0 ) {
this.close();
}
// Если у поста нет комментариев то скрываем форму
else {
this.hide();
}
}
}.bind( this ));
},
/**
* Отправка формы
*/
submit: function( event ) {
var text = this.elements.text.val();
ls.utils.formLock( this.element );
this.option( 'wall' ).lsWall( 'add', this.id, text );
event.preventDefault();
},
/**
* Разворачивает форму
*/
open: function() {
this.element.addClass( ls.options.classes.states.open );
},
/**
* Сворачивает форму
*/
close: function() {
this.element.removeClass( ls.options.classes.states.open );
this.elements.text.val('');
},
/**
* Показать форму
*/
show: function() {
this.element.show();
this.open();
this.elements.text.focus();
},
/**
* Скрыть форму
*/
hide: function() {
this.element.hide();
},
/**
* Развернута форма или нет
*/
isOpened: function() {
return this.element.hasClass( ls.options.classes.states.open );
},
/**
* Сворачивает/разворачивает форму
*/
expandToggle: function() {
this[ this.isOpened() ? 'close' : 'open' ]();
},
/**
* Показывает/скрывает форму комментирования
*/
toggle: function() {
this[ this.element.is( ':visible' ) ? 'hide' : 'show' ]();
}
});
})(jQuery);

View file

@ -8,243 +8,198 @@
* @author Denis Shakhov <denis.shakhov@gmail.com>
*/
var ls = ls || {};
ls.wall = (function ($) {
(function($) {
"use strict";
/**
* Дефолтные опции
*
* @private
*/
var _defaults = {
// Селекторы
selectors: {
entry: {
self: '.js-wall-comment',
remove: '.js-comment-remove',
reply: '.js-comment-reply'
$.widget( "livestreet.lsWall", {
/**
* Дефолтные опции
*/
options: {
// Ссылки
urls: {
add: null,
remove: null,
load: null,
load_comments: null
},
form: {
self: '.js-wall-form',
text: '.js-wall-form-text',
submit: '.js-wall-form-submit'
},
get_more: {
self: '.js-wall-get-more',
count: '.js-wall-get-more-count'
},
comment_wrapper: '.js-wall-comment-wrapper',
entry_container: '.js-wall-entry-container',
empty: '#wall-empty'
// Селекторы
selectors: {
entry: {
self: '.js-wall-entry',
remove: '.js-comment-remove',
reply: '.js-comment-reply'
},
comment: '.js-wall-comment',
post: '.js-wall-post',
form: '.js-wall-form',
more: '.js-wall-more',
more_comments: '.js-wall-more-comments',
comment_wrapper: '.js-wall-comment-wrapper',
container: '.js-wall-entry-container',
empty: '.js-wall-alert-empty'
}
},
// Роуты
routers: {
add: aRouter['profile'] + USER_PROFILE_LOGIN + '/wall/add/',
remove: aRouter['profile'] + USER_PROFILE_LOGIN + '/wall/remove/',
load: aRouter['profile'] + USER_PROFILE_LOGIN + '/wall/load/',
load_comments: aRouter['profile'] + USER_PROFILE_LOGIN + '/wall/load-reply/'
}
};
/**
* Инициализация
*
* @param {Object} options Опции
*/
this.init = function(options) {
// Иниц-ем модуль только на странице профиля юзера
if ( ! USER_PROFILE_LOGIN ) return;
/**
* Конструктор
*
* @constructor
* @private
*/
_create: function () {
var _this = this;
var _this = this;
this.elements = {
empty: this.element.find( this.option( 'selectors.empty' ) ),
more: this.element.find( this.option( 'selectors.more' ) ),
more_comments: this.element.find( this.option( 'selectors.more_comments' ) ),
entries: this.element.find( this.option( 'selectors.entry.self' ) ),
forms: this.element.find( this.option( 'selectors.form' ) ),
container: this.element.find( this.option( 'selectors.container' ) )
};
this.options = $.extend({}, _defaults, options);
this.userId = this.getUserId();
this.elements = {
document: $(document),
empty: $(this.options.selectors.empty)
}
// Добавление
this.elements.document.on('submit', this.options.selectors.form.self, function(e) {
_this.add( $(this) );
e.preventDefault();
});
this.elements.document
.on('keyup', this.options.selectors.form.text, function(e) {
if (e.ctrlKey && (e.keyCode || e.which) == 13) {
$(this).closest(_this.options.selectors.form.self).submit();
}
})
.on('click', this.options.selectors.form.text, function(e) {
// TODO: IE8 support
if (e.which == 1) {
_this.form.open($(this).closest(_this.options.selectors.form.self));
// Подгрузка новых постов
this.elements.more.more({
url: this.option( 'urls.load' ),
params: {
user_id: this.getUserId()
}
});
// Показать/скрыть форму добавления комментария
this.elements.document.on('click', this.options.selectors.entry.reply, function(e) {
_this.form.toggle( $(_this.options.selectors.form.self + '[data-id=' + $(this).data('id') + ']') );
e.preventDefault();
});
// Удаление записи
this.elements.document.on('click', this.options.selectors.entry.remove, function(e) {
_this.remove( $(this).data('id') );
e.preventDefault();
});
// Сворачиваем открытые формы
this.elements.document.on('click', function(e) {
// TODO: IE8 support
if (e.which == 1) {
$(_this.options.selectors.form.self + '.' + ls.options.classes.states.open).each(function(key, value) {
var oForm = $(value),
iId = oForm.data('id'),
oReply = $(_this.options.selectors.entry.reply + '[data-id=' + iId + ']');
if ( ! oForm.is(e.target) &&
oForm.has(e.target).length === 0 &&
! oReply.is(e.target) &&
! oForm.find(_this.options.selectors.form.text).val() ) {
if ( $(_this.options.selectors.entry_container + '[data-id=' + iId + ']' ).find(_this.options.selectors.entry.self).length || iId === 0 ) {
_this.form.close(oForm);
} else {
_this.form.toggle(oForm);
}
// Подгрузка комментариев
this.elements.more_comments.livequery( function () {
$( this ).more({
url: _this.option( 'urls.load_comments' ),
params: {
user_id: _this.getUserId()
}
});
}
});
$('.js-more-wall').more({
url: aRouter['profile'] + USER_PROFILE_LOGIN + '/wall/load/'
});
$('.js-more-wall-comments').livequery(function () {
$(this).more({
url: aRouter['profile'] + USER_PROFILE_LOGIN + '/wall/load-reply/'
});
});
};
/**
* Добавляет комментарий к записи
*/
this.add = function(oForm) {
var oTextarea = oForm.find(this.options.selectors.form.text),
oButton = oForm.find(this.options.selectors.form.submit),
iId = oForm.data('id'),
sText = oTextarea.val();
// Записи
this.elements.entries.livequery( function () {
$( this ).lsWallEntry({
wall: _this.element,
urls: {
remove: _this.option( 'urls.remove' )
},
selectors: {
wrapper: _this.option( 'selectors.comment_wrapper' ),
remove: _this.option( 'selectors.entry.remove' ),
reply: _this.option( 'selectors.entry.reply' ),
}
})
});
ls.hook.marker('addBefore');
ls.utils.formLock(oForm);
ls.ajax.load(this.options.routers.add, { sText: sText, iPid: iId }, function(result) {
if (result.bStateError) {
ls.msg.error(null, result.sMsg);
} else {
if (iId === 0) this.elements.empty.hide();
oTextarea.val('');
this.loadNew(iId);
ls.hook.run('ls_wall_add_after', [sText, iId, result]);
}
ls.utils.formUnlock(oForm);
}.bind(this));
};
/**
* Удаление записи/комментария
*
* @param {Number} iId ID записи
*/
this.remove = function(iId) {
var _this = this;
ls.hook.marker('removeBefore');
ls.ajax.load(this.options.routers.remove, { iId: iId }, function(result) {
if (result.bStateError) {
ls.msg.error(null, result.sMsg);
} else {
var entry = $(_this.options.selectors.entry.self + '[data-id=' + iId + ']');
entry.fadeOut('slow', function() {
if ( ! $(_this.options.selectors.entry.self).length ) _this.elements.empty.show();
ls.hook.run('ls_wall_remove_item_fade', [iId, result], this);
// Формы
this.elements.forms.livequery( function () {
$( this ).lsWallForm({
wall: _this.element
});
entry.next(_this.options.selectors.comment_wrapper).fadeOut('slow');
});
},
ls.hook.run('ls_wall_remove_after', [iId, result]);
}
});
};
/**
* Добавление
*
* TODO: Оптимизировать
*/
add: function( pid, text ) {
var form = this.getFormById( pid );
/**
* Подгрузка новых записей
*/
this.loadNew = function(iPid) {
var oContainer = $(this.options.selectors.entry_container + '[data-id=' + iPid + ']'),
iFirstId = oContainer.find(' > ' + this.options.selectors.entry.self + ':' + (iPid === 0 ? 'first' : 'last')).data('id') || -1,
oParams = { iFirstId: iFirstId, iTargetId: iPid };
ls.ajax.load( this.option( 'urls.add' ), { user_id: this.getUserId(), pid: pid, text: text }, function( response ) {
if ( response.bStateError ) {
ls.msg.error(null, response.sMsg);
} else {
if ( pid === 0 ) this.elements.empty.hide();
ls.ajax.load(iPid === 0 ? this.options.routers.load : this.options.routers.load_comments, oParams, function(result) {
if (result.bStateError) {
ls.msg.error(null, result.sMsg);
} else {
if (result.iCountLoaded) {
oContainer[iPid === 0 ? 'prepend' : 'append'](result.sHtml);
this.load( pid );
form.lsWallForm( 'close' );
}
this.form.close( $(this.options.selectors.form.self + '[data-id=' + iPid + ']') );
ls.hook.run('ls_wall_loadnew_after', [iPid, iFirstId, result]);
}
}.bind(this));
};
/**
* Форма
*/
this.form = function(_this) {
/**
* Разворачивает форму
*/
this.open = function(oForm) {
oForm.addClass(ls.options.classes.states.open);
};
ls.utils.formUnlock( form );
}.bind(this));
},
/**
* Сворачивает форму
* Подгружает записи
*
* TODO: Оптимизировать
*/
this.close = function(oForm) {
oForm.removeClass(ls.options.classes.states.open);
};
load: function( pid ) {
var container = this.element.find( this.options.selectors.container + '[data-id=' + pid + ']' ),
firstId = container.find( '>' + this.option( 'selectors.entry.self' ) + ':' + ( pid === 0 ? 'first' : 'last' ) ).data( 'id' ) || -1,
params = { user_id: this.getUserId(), first_id: firstId, target_id: pid };
ls.ajax.load( this.option( pid === 0 ? 'urls.load' : 'urls.load_comments' ), params, function( response ) {
if (response.bStateError) {
ls.msg.error(null, response.sMsg);
} else {
if ( response.count_loaded ) {
container[ pid === 0 ? 'prepend' : 'append' ]( response.html );
}
}
}.bind(this));
},
/**
* Сворачивает/разворачивает форму
* Получает посты
*/
this.expandToggle = function(oForm) {
this.form[ oForm.hasClass(ls.options.classes.states.open) ? 'close' : 'open' ](oForm);
}.bind(_this);
getPosts: function() {
return this.element.find( this.option( 'selectors.post' ) );
},
/**
* Показывает/скрывает форму комментирования
* Получает комментарии по ID поста
*/
this.toggle = function(oForm) {
oForm.toggle().find(this.options.selectors.form.text).focus();
this.form.expandToggle(oForm);
}.bind(_this);
getCommentsByPostId: function( pid ) {
return this.getCommentWrapperById( pid ).find( this.option( 'selectors.comment' ) );
},
return this;
}.call({}, this);
/**
* Получает запись по ID
*/
getEntryById: function( id ) {
return this.element.find( this.option( 'selectors.entry.self' ) + '[data-id=' + id + ']' ).eq( 0 );
},
return this;
}).call(ls.wall || {}, jQuery);
/**
* Получает враппер комментариев по ID поста
*/
getCommentWrapperById: function( id ) {
return this.element.find( this.option( 'selectors.comment_wrapper' ) + '[data-id=' + id + ']' ).eq( 0 );
},
/**
* Получает форму по ID поста
*/
getFormById: function( id ) {
return this.element.find( this.option( 'selectors.form' ) + '[data-id=' + id + ']' ).eq( 0 );
},
/**
* Получает ID владельца стены
*/
getUserId: function() {
return this.userId ? this.userId : this.userId = this.element.data( 'user-id' );
},
/**
* Получает развернутые формы
*/
getOpenedForms: function() {
return this.element.find( this.option( 'selectors.form' ) + '.' + ls.options.classes.states.open );
},
/**
* Проверяет и если нужно показывает/скрывает сообщение о пустом списке
*/
checkEmpty: function() {
this.elements.empty[ this.getPosts().length ? 'hide' : 'show' ]();
}
});
})(jQuery);

View file

@ -724,6 +724,35 @@ return array(
)
),
/**
* Стена
*/
'wall' => array(
'title' => 'Стена',
// Форма
'form' => array(
// Поля
'fields' => array(
'text' => array(
'placeholder' => 'Написать на стене',
'placeholder_reply' => 'Ответить...',
),
),
),
// Всплывающие сообщения
'notices' => array(
'error_add_pid' => 'На данное сообщение невозможно ответить',
'error_add_time_limit' => 'Вам нельзя слишком часто писать на стене'
),
// Сообщения
'alerts' => array(
'unregistered' => 'Только зарегистрированные и авторизованные пользователи могут оставлять записи на стене'
),
),
/**
* Почта
*/
@ -1095,22 +1124,6 @@ return array(
'obscene'=>'Непристойное поведение',
'other'=>'Другое',
),
/**
* Стена
*/
'wall_add_pid_error' => 'На данное сообщение невозможно ответить',
'wall_add_error' => 'Ошибка добавления записи на стены',
'wall_add_time_limit' => 'Вам нельзя слишком часто писать на стене',
'wall_add_title' => 'Написать на стене',
'wall_add_submit' => 'Отправить',
'wall_add_quest' => 'Для возможности оставлять записи на стене необходимо зарегистрироваться.',
'wall_list_empty' => 'Записей на стене нет, вы можете стать первым!',
'wall_load_more' => 'К предыдущим записям',
'wall_load_reply_more' => 'Показать все',
'wall_action_delete' => 'Удалить',
'wall_action_reply' => 'Ответить',
'wall_reply_placeholder' => 'Ответить...',
'wall_reply_submit' => 'Отправить',
/**
* Настройки
*/

View file

@ -222,6 +222,17 @@
{include 'components/user_list_avatar/user_list_avatar.tpl' aUsersList=$aUsersFriend}
{/if}
{**
* Стена
*}
<h2 class="header-table mt-30">{lang name='wall.title'}</h2>
{insert name='block' block='wall' params=[
'classes' => 'js-wall-default',
'user_id' => $oUserProfile->getId()
]}
{hook run='profile_whois_item_end' oUserProfile=$oUserProfile}
{hook run='user_info_end' oUserProfile=$oUserProfile}
{/block}

View file

@ -1,9 +0,0 @@
{**
* Список комментариев к записи на стене
*
* @param array $aReplyWall Список комментариев
*}
{foreach $aReplyWall as $oWallComment}
{include 'actions/ActionProfile/wall.entry.tpl' oWallEntry=$oWallComment bWallEntryShowReply=false sWallEntryClasses='wall-comment'}
{/foreach}

View file

@ -1,12 +0,0 @@
{**
* Стена / Запись (Пост / Комментарий)
*
* @param object $oWallEntry Комментарий
* @param boolean $bWallEntryShowReply Показывать или нет кнопку комментирования
* @param string $sWallEntryClasses Классы
*}
{include 'components/comment/comment.tpl'
oComment = $oWallEntry
bShowReply = $bWallEntryShowReply
sClasses = "wall-comment js-wall-comment $sWallEntryClasses"}

View file

@ -1,22 +0,0 @@
{**
* Стена / Форма добавления записи
*
* @param integer $iWallFormId ID родительского поста
* @param boolean $bWallFormDisplay Отображать форму или нет
* @param string $sWallFormPlaceholder Плейсхолдер
*}
<form class="wall-form js-wall-form {$sWallFormClasses}" data-id="{$iWallFormId|default:0}" {if ! $bWallFormDisplay|default:true}style="display: none"{/if}>
{* Текст *}
{include 'components/field/field.textarea.tpl'
sPlaceholder = "{if $sWallFormPlaceholder}{$sWallFormPlaceholder}{else}{$aLang.wall_add_title}{/if}"
sInputClasses = 'width-full js-wall-form-text'}
{* Подвал формы *}
<footer class="wall-form-footer">
{include 'components/button/button.tpl'
sMods = 'primary'
sClasses = 'js-wall-form-submit'
sText = $aLang.wall_add_submit}
</footer>
</form>

View file

@ -1,36 +0,0 @@
{**
* Список постов на стене
*
* @param array $aWall Список постов
*}
{foreach $aWall as $oPost}
{$aPostComments = $oPost->getLastReplyWall()}
{* Запись *}
{include 'actions/ActionProfile/wall.entry.tpl' oWallEntry=$oPost bWallEntryShowReply=!$aPostComments sWallEntryClasses='wall-post'}
<div class="wall-comments js-wall-comment-wrapper" data-id="{$oPost->getId()}">
{* Кнопка подгрузки комментариев *}
{if count($aPostComments) < $oPost->getCountReply()}
{include 'components/more/more.tpl'
sClasses = 'more-wall-comments js-more-wall-comments'
iCount = $oPost->getCountReply() - Config::Get('module.wall.count_last_reply')
bAppend = 'false'
sAttributes = "data-more-target=\".js-wall-entry-container[data-id={$oPost->getId()}]\" data-proxy-i-last-id=\"{$aPostComments[0]->getId()}\" data-param-i-target-id=\"{$oPost->getId()}\" "
}
{/if}
{* Комментарии *}
<div class="js-wall-entry-container" data-id="{$oPost->getId()}">
{if $aPostComments}
{include 'actions/ActionProfile/wall.comments.tpl' aReplyWall=$aPostComments}
{/if}
</div>
{* Форма добавления комментария *}
{if $oUserCurrent}
{include 'actions/ActionProfile/wall.form.tpl' iWallFormId=$oPost->getId() bWallFormDisplay=$aPostComments sWallFormPlaceholder=$aLang.wall_reply_placeholder}
{/if}
</div>
{/foreach}

View file

@ -5,31 +5,12 @@
{extends 'layouts/layout.user.tpl'}
{block 'layout_user_page_title'}
{$aLang.user_menu_profile_wall}
{lang name='wall.title'}
{/block}
{block 'layout_content' append}
{* Форма добавления записи *}
{if $oUserCurrent}
{include 'actions/ActionProfile/wall.form.tpl'}
{else}
{include 'components/alert/alert.tpl' sMods='info' sClasses='mt-15' mAlerts=$aLang.wall_add_quest}
{/if}
{if ! count($aWall)}
{include 'components/alert/alert.tpl' mAlerts=$aLang.wall_list_empty sMods='empty' sClasses='mt-15' sAttributes='id="wall-empty"'}
{/if}
{* Список записей *}
<div class="js-wall-entry-container" data-id="0">
{include 'actions/ActionProfile/wall.posts.tpl'}
</div>
{* Кнопка подгрузки записей *}
{if $iCountWall - count($aWall)}
{include 'components/more/more.tpl'
sClasses = 'js-more-wall'
iCount = $iCountWall - count($aWall)
sAttributes = "data-more-target=\".js-wall-entry-container[data-id=0]\" data-proxy-i-last-id=\"{$iWallLastId}\" "}
{/if}
{insert name='block' block='wall' params=[
'classes' => 'js-wall-default',
'user_id' => $oUserProfile->getId()
]}
{/block}

View file

@ -36,4 +36,4 @@
/**
* Кнопка подгрузки комментариев
*/
.more.more-wall-comments { border: none; margin: 0 0 2px; }
.more.wall-more-comments { border: none; margin: 0 0 2px; }

View file

@ -304,7 +304,14 @@ jQuery(document).ready(function($){
/**
* Стена
*/
ls.wall.init();
$('.js-wall-default').lsWall({
urls: {
add: aRouter.wall + 'add/',
remove: aRouter.wall + 'remove/',
load: aRouter.wall + 'load/',
load_comments: aRouter.wall + 'load-comments/'
}
});
/**

View file

@ -0,0 +1,9 @@
{**
* Список комментариев к записи на стене
*
* @param array $comments Список комментариев
*}
{foreach $smarty.local.comments as $comment}
{include './wall.entry.tpl' entry=$comment showReply=false classes='wall-comment js-wall-comment' type='comment'}
{/foreach}

View file

@ -0,0 +1,13 @@
{**
* Стена / Запись (Пост / Комментарий)
*
* @param object $entry Комментарий
* @param boolean $showReply Показывать или нет кнопку комментирования
* @param string $classes Классы
*}
{include 'components/comment/comment.tpl'
oComment = $smarty.local.entry
bShowReply = $smarty.local.showReply
sAttributes = "data-type=\"{$smarty.local.type}\""
sClasses = "wall-comment js-wall-entry {$smarty.local.classes}"}

View file

@ -0,0 +1,23 @@
{**
* Стена / Форма добавления записи
*
* @param integer $id ID родительского поста
* @param boolean $display Отображать форму или нет
* @param string $placeholder Плейсхолдер
*}
<form class="wall-form js-wall-form {$smarty.local.classes}" data-id="{$smarty.local.id|default:0}" {if ! $smarty.local.display|default:true}style="display: none"{/if}>
{* Текст *}
{include 'components/field/field.textarea.tpl'
sPlaceholder = "{$smarty.local.placeholder|default:$aLang.wall.form.fields.text.placeholder}"
sInputClasses = 'width-full js-wall-form-text'}
{* Подвал формы *}
<footer class="wall-form-footer">
{include 'components/button/button.tpl'
sType = 'submit'
sMods = 'primary'
sClasses = 'js-wall-form-submit'
sText = $aLang.common.add}
</footer>
</form>

View file

@ -0,0 +1,37 @@
{**
* Список постов на стене
*
* @param array $posts Список постов
*}
{foreach $smarty.local.posts as $post}
{$comments = $post->getLastReplyWall()}
{$postId = $post->getId()}
{* Запись *}
{include './wall.entry.tpl' entry=$post showReply=!$comments classes='wall-post js-wall-post' type='post'}
{* Комментарии *}
<div class="wall-comments js-wall-comment-wrapper" data-id="{$postId}">
{* Кнопка подгрузки комментариев *}
{if count( $comments ) < $post->getCountReply()}
{include 'components/more/more.tpl'
sClasses = 'wall-more-comments js-wall-more-comments'
iCount = $post->getCountReply() - Config::Get('module.wall.count_last_reply')
bAppend = 'false'
sAttributes = "data-more-target=\".js-wall-entry-container[data-id={$postId}]\" data-proxy-last_id=\"{$comments[0]->getId()}\" data-param-target_id=\"{$postId}\" "}
{/if}
{* Комментарии *}
<div class="js-wall-entry-container" data-id="{$postId}">
{if $comments}
{include './wall.comments.tpl' comments=$comments}
{/if}
</div>
{* Форма добавления комментария *}
{if $oUserCurrent}
{include './wall.form.tpl' id=$postId display=$comments placeholder=$aLang.wall.form.fields.text.placeholder_reply}
{/if}
</div>
{/foreach}

View file

@ -0,0 +1,44 @@
{**
* Стена
*
* @param array $posts Посты
* @param array $count Общее кол-во постов на стене
* @param array $lastId ID последнего загруженного поста
* @param array $classes Доп-ые классы
*
* TODO: Локальные переменные
*}
{* Название компонента *}
{$component = 'wall'}
{$loadedCount = count($posts)}
{$moreCount = $count - $loadedCount}
{* Стена *}
<div class="{$component} {mod name=$component mods=$mods} {$classes}" data-user-id="{$oUserProfile->getId()}">
{* Форма добавления записи *}
{if $oUserCurrent}
{include './wall.form.tpl'}
{else}
{include 'components/alert/alert.tpl' sMods='info' sClasses='mt-15' mAlerts=$aLang.wall.alerts.unregistered}
{/if}
{* Список записей *}
<div class="js-wall-entry-container" data-id="0">
{include './wall.posts.tpl' posts=$posts}
</div>
{* Уведомление о пустом списке *}
{if $oUserCurrent || ( ! $oUserCurrent && ! $loadedCount )}
{include 'components/alert/alert.tpl' mAlerts=$aLang.common.empty sMods='empty' sClasses='mt-15 js-wall-alert-empty' sAttributes='id="wall-empty"' bVisible=!$loadedCount}
{/if}
{* Кнопка подгрузки записей *}
{if $moreCount}
{include 'components/more/more.tpl'
sClasses = 'js-wall-more'
iCount = $moreCount
sAttributes = "data-more-target=\".js-wall-entry-container[data-id=0]\" data-proxy-last_id=\"{$lastId}\""}
{/if}
</div>