Files
NodeBB/public/src/client/topic/postTools.js

449 lines
13 KiB
JavaScript
Raw Normal View History

'use strict';
2016-12-23 16:35:48 +03:00
define('forum/topic/postTools', [
'share',
'navigator',
'components',
'translator',
'forum/topic/votes',
'api',
], function (share, navigator, components, translator, votes, api) {
2016-02-18 18:32:08 +02:00
var PostTools = {};
2016-06-30 11:38:33 +03:00
var staleReplyAnyway = false;
PostTools.init = function (tid) {
2016-06-30 11:38:33 +03:00
staleReplyAnyway = false;
2015-10-24 20:50:43 -04:00
renderMenu();
addPostHandlers(tid);
share.addShareHandlers(ajaxify.data.titleRaw);
2016-12-23 16:35:48 +03:00
votes.addVoteHandler();
PostTools.updatePostCount(ajaxify.data.postcount);
};
2015-10-24 20:50:43 -04:00
function renderMenu() {
$('[component="topic"]').on('show.bs.dropdown', '.moderator-tools', function () {
2015-10-24 20:50:43 -04:00
var $this = $(this);
var dropdownMenu = $this.find('.dropdown-menu');
if (dropdownMenu.html()) {
return;
}
var postEl = $this.parents('[data-pid]');
var pid = postEl.attr('data-pid');
var index = parseInt(postEl.attr('data-index'), 10);
2016-03-01 18:16:51 -05:00
2017-02-18 12:30:49 -07:00
socket.emit('posts.loadPostTools', { pid: pid, cid: ajaxify.data.cid }, function (err, data) {
2015-10-24 20:50:43 -04:00
if (err) {
2016-08-10 22:53:30 +03:00
return app.alertError(err.message);
2015-10-24 20:50:43 -04:00
}
data.posts.display_move_tools = data.posts.display_move_tools && index !== 0;
2016-03-08 14:38:44 +02:00
2018-02-15 14:52:49 -05:00
app.parseAndTranslate('partials/topic/post-menu-list', data, function (html) {
dropdownMenu.html(html);
require(['clipboard'], function (clipboard) {
new clipboard('[data-clipboard-text]');
2015-10-24 20:50:43 -04:00
});
2018-02-15 14:52:49 -05:00
$(window).trigger('action:post.tools.load');
2015-10-24 20:50:43 -04:00
});
});
});
}
PostTools.toggle = function (pid, isDeleted) {
var postEl = components.get('post', 'pid', pid);
2016-10-08 19:09:48 +03:00
postEl.find('[component="post/quote"], [component="post/bookmark"], [component="post/reply"], [component="post/flag"], [component="user/chat"]')
.toggleClass('hidden', isDeleted);
2018-12-19 12:09:36 -05:00
postEl.find('[component="post/delete"]').toggleClass('hidden', isDeleted).parent().attr('hidden', isDeleted ? '' : null);
postEl.find('[component="post/restore"]').toggleClass('hidden', !isDeleted).parent().attr('hidden', !isDeleted ? '' : null);
postEl.find('[component="post/purge"]').toggleClass('hidden', !isDeleted).parent().attr('hidden', !isDeleted ? '' : null);
2018-07-17 21:14:53 -04:00
PostTools.removeMenu(postEl);
};
PostTools.removeMenu = function (postEl) {
2016-08-29 15:51:46 +03:00
postEl.find('[component="post/tools"] .dropdown-menu').html('');
};
PostTools.updatePostCount = function (postCount) {
var postCountEl = components.get('topic/post-count');
postCountEl.html(postCount).attr('title', postCount);
utils.makeNumbersHumanReadable(postCountEl);
navigator.setCount(postCount);
};
function addPostHandlers(tid) {
2015-03-17 14:09:08 -04:00
var postContainer = components.get('topic');
postContainer.on('click', '[component="post/quote"]', function () {
2016-02-18 18:32:08 +02:00
onQuoteClicked($(this), tid);
});
postContainer.on('click', '[component="post/reply"]', function () {
2016-02-18 18:32:08 +02:00
onReplyClicked($(this), tid);
2015-03-20 16:13:34 -04:00
});
Squashed commit of the following: commit 9c86d9b2904e14927cd7e9679b92aec0951d1063 Merge: ebfa63a 5a7f811 Author: Julian Lam <julian@nodebb.org> Date: Thu Jul 20 08:41:39 2017 -0400 Merge branch 'noscript-login' of https://github.com/An-dz/NodeBB into noscript commit 5a7f81185e8f9bd7d2d011c3d495988be7e437a3 Author: André Zanghelini <an_dz@simutrans-forum> Date: Mon Jul 17 23:07:14 2017 -0300 Rename clashing variable 'next' commit ebfa63a984073a58c17aa408c363cdb03ef89985 Merge: c1801cd f159d0d Author: Julian Lam <julian@nodebb.org> Date: Mon Jul 17 16:30:40 2017 -0400 Merge branch 'noscript-logout' of https://github.com/An-dz/NodeBB into noscript commit c1801cda14e6363491e30b659902e2ae71f7e1f7 Merge: 7a5f9f3 9fd542d Author: Julian Lam <julian@nodebb.org> Date: Mon Jul 17 16:30:31 2017 -0400 Merge branch 'noscript-register' of https://github.com/An-dz/NodeBB into noscript commit 7a5f9f35abc834bb72ddddc9ca07d34f2fde8353 Merge: 44851f9 d37b95c Author: Julian Lam <julian@nodebb.org> Date: Mon Jul 17 16:30:10 2017 -0400 Merge branch 'noscript-compose' of https://github.com/An-dz/NodeBB into noscript commit f159d0d9ef1b7f600e830a96fdb4b9c87c79bb4a Author: André Zanghelini <an_dz@simutrans-forum> Date: Thu Jul 6 12:16:38 2017 -0300 Prevent form submit Required for theme change commit d37b95cb71d32d4483190609798e244c331db165 Author: André Zanghelini <an_dz@simutrans-forum> Date: Thu Jul 6 01:49:52 2017 -0300 Prevent link action with scripts Required for the theme change that changes the buttons to `a` tags. commit 9fd542d8970b7d1a4126f4edc4b44eab7d708fb0 Author: André Zanghelini <an_dz@simutrans-forum> Date: Wed Jul 5 19:57:56 2017 -0300 Fix tests commit cdad5bf8c2891ad76f7441fd4d8a74b058a14e6d Author: André Zanghelini <an_dz@simutrans-forum> Date: Wed Jul 5 19:09:17 2017 -0300 Update error handling commit 4ff11cd136a4fb98483f837e2cebc741380dfe76 Author: André Zanghelini <an_dz@simutrans-forum> Date: Wed Jul 5 17:29:08 2017 -0300 Remove async waterfall commit df01d44e821a70c984b89e9585a325c3e02c6e37 Author: André Zanghelini <an_dz@simutrans-forum> Date: Wed Jul 5 16:59:43 2017 -0300 Set noscript compose as noscript at start commit 4bcc380da72239b8315cc849a77a3036e06e4a12 Author: André Zanghelini <an_dz@simutrans-forum> Date: Wed Jul 5 16:59:12 2017 -0300 Remove last useless next commit b5eac6fea11e209934c0648a7e75ad07a2167123 Author: André Zanghelini <an_dz@simutrans-forum> Date: Sun Jul 2 18:35:08 2017 -0300 Last function requires no next commit 20a5cce6e6e32a454c304c448383707ec44c75a8 Author: André Zanghelini <an_dz@simutrans-forum> Date: Sun Jul 2 18:06:58 2017 -0300 Remove more useless next calls commit 85ee22a79bcbbb1995106f43d4c74d6ba9206cab Author: André Zanghelini <an_dz@simutrans-forum> Date: Sun Jul 2 17:46:07 2017 -0300 Remove useless next calls commit 7d984c47ad24faac1fe537dee4a5a7d697e8634c Author: André Zanghelini <an_dz@simutrans-forum> Date: Sun Jul 2 15:45:31 2017 -0300 Support old themes commit 4a09dfbd08253115c342a9e829c4e6940cecb8cc Author: André Zanghelini <an_dz@simutrans-forum> Date: Sun Jul 2 15:37:23 2017 -0300 Moved all error handling into helpers function commit 391aa6e67ef9ab67304005e14ac0633cdb630713 Author: André Zanghelini <an_dz@simutrans-forum> Date: Thu Jun 8 15:37:37 2017 -0300 ESLint - Fix mixed conditionals commit 80ccc6fd581d791f31e7ab62de8de611837bfc3c Author: André Zanghelini <an_dz@simutrans-forum> Date: Sat Jun 3 18:08:15 2017 -0300 Compose without scripts commit 2aca811256721238ca0cede4954213d369009885 Author: André Zanghelini <an_dz@simutrans-forum> Date: Sat Jun 3 18:00:44 2017 -0300 Register without scripts commit 097bb51577fb26f8e22f86dc274cb670ab606a8a Author: André Zanghelini <an_dz@simutrans-forum> Date: Sat Jun 3 16:42:15 2017 -0300 Logout without scripts commit d497e08109891079656fee1c145043a9c0e55f2e Author: André Zanghelini <an_dz@simutrans-forum> Date: Sat Jun 3 16:27:10 2017 -0300 Login without script
2017-07-20 08:51:04 -04:00
$('.topic').on('click', '[component="topic/reply"]', function (e) {
e.preventDefault();
2016-02-18 18:32:08 +02:00
onReplyClicked($(this), tid);
});
$('.topic').on('click', '[component="topic/reply-as-topic"]', function () {
translator.translate('[[topic:link_back, ' + ajaxify.data.titleRaw + ', ' + config.relative_path + '/topic/' + ajaxify.data.slug + ']]', function (body) {
2016-02-23 12:55:46 -05:00
$(window).trigger('action:composer.topic.new', {
cid: ajaxify.data.cid,
2017-02-17 19:31:21 -07:00
body: body,
2016-02-23 12:55:46 -05:00
});
2015-12-21 12:51:49 +02:00
});
});
postContainer.on('click', '[component="post/bookmark"]', function () {
2016-12-23 16:35:48 +03:00
return bookmarkPost($(this), getData($(this), 'data-pid'));
});
postContainer.on('click', '[component="post/upvote"]', function () {
2020-10-06 14:47:03 -04:00
return votes.toggleVote($(this), '.upvoted', 1);
});
postContainer.on('click', '[component="post/downvote"]', function () {
2020-10-06 14:47:03 -04:00
return votes.toggleVote($(this), '.downvoted', -1);
});
postContainer.on('click', '[component="post/vote-count"]', function () {
2016-12-23 16:35:48 +03:00
votes.showVotes(getData($(this), 'data-pid'));
2014-11-15 12:37:22 -05:00
});
postContainer.on('click', '[component="post/flag"]', function () {
2015-10-13 16:36:43 -04:00
var pid = getData($(this), 'data-pid');
require(['flags'], function (flags) {
flags.showFlagModal({
type: 'post',
2017-02-24 12:47:46 -05:00
id: pid,
});
2015-10-13 16:36:43 -04:00
});
});
postContainer.on('click', '[component="post/flagUser"]', function () {
var uid = getData($(this), 'data-uid');
require(['flags'], function (flags) {
flags.showFlagModal({
type: 'user',
id: uid,
});
});
});
postContainer.on('click', '[component="post/edit"]', function () {
2015-04-01 23:28:21 -04:00
var btn = $(this);
2016-05-21 19:26:06 +03:00
var timestamp = parseInt(getData(btn, 'data-timestamp'), 10);
var postEditDuration = parseInt(ajaxify.data.postEditDuration, 10);
2016-06-28 12:34:09 +03:00
if (checkDuration(postEditDuration, timestamp, 'post-edit-duration-expired')) {
$(window).trigger('action:composer.post.edit', {
2017-02-17 19:31:21 -07:00
pid: getData(btn, 'data-pid'),
2016-06-28 12:34:09 +03:00
});
}
});
if (config.enablePostHistory && ajaxify.data.privileges['posts:history']) {
postContainer.on('click', '[component="post/view-history"], [component="post/edit-indicator"]', function () {
var btn = $(this);
2018-11-17 20:50:07 -05:00
require(['forum/topic/diffs'], function (diffs) {
diffs.open(getData(btn, 'data-pid'));
});
});
}
2018-02-16 21:22:26 -05:00
postContainer.on('click', '[component="post/delete"]', function () {
2016-06-28 12:34:09 +03:00
var btn = $(this);
var timestamp = parseInt(getData(btn, 'data-timestamp'), 10);
var postDeleteDuration = parseInt(ajaxify.data.postDeleteDuration, 10);
if (checkDuration(postDeleteDuration, timestamp, 'post-delete-duration-expired')) {
togglePostDelete($(this));
2016-06-28 12:34:09 +03:00
}
});
function checkDuration(duration, postTimestamp, languageKey) {
if (!ajaxify.data.privileges.isAdminOrMod && duration && Date.now() - postTimestamp > duration * 1000) {
var numDays = Math.floor(duration / 86400);
var numHours = Math.floor((duration % 86400) / 3600);
var numMinutes = Math.floor(((duration % 86400) % 3600) / 60);
var numSeconds = ((duration % 86400) % 3600) % 60;
var msg = '[[error:' + languageKey + ', ' + duration + ']]';
2016-05-21 19:26:06 +03:00
if (numDays) {
if (numHours) {
2016-06-28 12:34:09 +03:00
msg = '[[error:' + languageKey + '-days-hours, ' + numDays + ', ' + numHours + ']]';
2016-05-21 19:26:06 +03:00
} else {
2016-06-28 12:34:09 +03:00
msg = '[[error:' + languageKey + '-days, ' + numDays + ']]';
}
2016-05-21 19:26:06 +03:00
} else if (numHours) {
if (numMinutes) {
2016-06-28 12:34:09 +03:00
msg = '[[error:' + languageKey + '-hours-minutes, ' + numHours + ', ' + numMinutes + ']]';
2016-05-21 19:26:06 +03:00
} else {
2016-06-28 12:34:09 +03:00
msg = '[[error:' + languageKey + '-hours, ' + numHours + ']]';
}
2016-05-21 19:26:06 +03:00
} else if (numMinutes) {
if (numSeconds) {
2016-06-28 12:34:09 +03:00
msg = '[[error:' + languageKey + '-minutes-seconds, ' + numMinutes + ', ' + numSeconds + ']]';
2016-05-21 19:26:06 +03:00
} else {
2016-06-28 12:34:09 +03:00
msg = '[[error:' + languageKey + '-minutes, ' + numMinutes + ']]';
}
2016-05-21 19:26:06 +03:00
}
2016-06-28 12:34:09 +03:00
app.alertError(msg);
return false;
2016-05-21 19:26:06 +03:00
}
2016-06-28 12:34:09 +03:00
return true;
}
postContainer.on('click', '[component="post/restore"]', function () {
togglePostDelete($(this));
});
postContainer.on('click', '[component="post/purge"]', function () {
purgePost($(this));
});
postContainer.on('click', '[component="post/move"]', function () {
2018-11-17 20:50:07 -05:00
var btn = $(this);
require(['forum/topic/move-post'], function (movePost) {
movePost.init(btn.parents('[data-pid]'));
});
});
postContainer.on('click', '[component="post/change-owner"]', function () {
var btn = $(this);
require(['forum/topic/change-owner'], function (changeOwner) {
changeOwner.init(btn.parents('[data-pid]'));
});
});
2018-02-15 14:52:49 -05:00
postContainer.on('click', '[component="post/ban-ip"]', function () {
var ip = $(this).attr('data-ip');
socket.emit('blacklist.addRule', ip, function (err) {
if (err) {
return app.alertError(err.message);
}
app.alertSuccess('[[admin/manage/blacklist:ban-ip]]');
});
});
postContainer.on('click', '[component="post/chat"]', function () {
openChat($(this));
});
}
2016-02-18 18:32:08 +02:00
function onReplyClicked(button, tid) {
2017-02-16 15:01:06 +03:00
var selectedNode = getSelectedNode();
showStaleWarning(function () {
var username = getUserSlug(button);
2016-06-21 13:37:17 +03:00
if (getData(button, 'data-uid') === '0' || !getData(button, 'data-userslug')) {
username = '';
}
2016-06-21 13:37:17 +03:00
var toPid = button.is('[component="post/reply"]') ? getData(button, 'data-pid') : null;
2017-02-16 15:01:06 +03:00
if (selectedNode.text && (!toPid || !selectedNode.pid || toPid === selectedNode.pid)) {
username = username || selectedNode.username;
2016-06-21 13:37:17 +03:00
$(window).trigger('action:composer.addQuote', {
tid: tid,
pid: toPid,
topicName: ajaxify.data.titleRaw,
username: username,
2017-02-16 15:01:06 +03:00
text: selectedNode.text,
2017-02-17 19:31:21 -07:00
selectedPid: selectedNode.pid,
2016-06-21 13:37:17 +03:00
});
} else {
$(window).trigger('action:composer.post.new', {
tid: tid,
pid: toPid,
topicName: ajaxify.data.titleRaw,
2019-02-06 18:36:58 -05:00
text: username ? username + ' ' : ($('[component="topic/quickreply/text"]').val() || ''),
2016-06-21 13:37:17 +03:00
});
}
});
}
2016-02-18 18:32:08 +02:00
function onQuoteClicked(button, tid) {
2017-02-16 15:01:06 +03:00
var selectedNode = getSelectedNode();
2016-06-30 11:38:33 +03:00
showStaleWarning(function () {
var username = getUserSlug(button);
2017-02-18 18:55:33 -07:00
var toPid = getData(button, 'data-pid');
2015-10-06 05:28:05 -04:00
2016-06-20 13:06:08 +03:00
function quote(text) {
$(window).trigger('action:composer.addQuote', {
tid: tid,
2017-02-16 15:01:06 +03:00
pid: toPid,
2016-06-20 13:06:08 +03:00
username: username,
topicName: ajaxify.data.titleRaw,
2017-02-17 19:31:21 -07:00
text: text,
2016-06-20 13:06:08 +03:00
});
}
2017-02-16 15:01:06 +03:00
if (selectedNode.text && toPid && toPid === selectedNode.pid) {
return quote(selectedNode.text);
2016-06-21 13:37:17 +03:00
}
2017-02-16 15:01:06 +03:00
socket.emit('posts.getRawPost', toPid, function (err, post) {
2016-06-21 13:37:17 +03:00
if (err) {
return app.alertError(err.message);
2016-06-20 13:06:08 +03:00
}
2016-06-21 13:37:17 +03:00
quote(post);
});
});
}
2017-02-16 15:01:06 +03:00
function getSelectedNode() {
var selectedText = '';
var selectedPid;
var username = '';
2016-06-20 13:06:08 +03:00
var selection = window.getSelection ? window.getSelection() : document.selection.createRange();
2017-02-16 15:01:06 +03:00
var postContents = $('[component="post"] [component="post/content"]');
var content;
2017-02-16 15:11:31 +03:00
postContents.each(function (index, el) {
2017-02-16 15:01:06 +03:00
if (selection && selection.containsNode && el && selection.containsNode(el, true)) {
content = el;
}
});
2016-06-20 13:06:08 +03:00
2017-02-16 15:01:06 +03:00
if (content) {
2016-06-20 13:06:08 +03:00
var bounds = document.createRange();
bounds.selectNodeContents(content);
var range = selection.getRangeAt(0).cloneRange();
if (range.compareBoundaryPoints(Range.START_TO_START, bounds) < 0) {
range.setStart(bounds.startContainer, bounds.startOffset);
}
if (range.compareBoundaryPoints(Range.END_TO_END, bounds) > 0) {
range.setEnd(bounds.endContainer, bounds.endOffset);
}
bounds.detach();
2017-02-16 15:01:06 +03:00
selectedText = range.toString();
var postEl = $(content).parents('[component="post"]');
selectedPid = postEl.attr('data-pid');
username = getUserSlug($(content));
2016-06-20 13:06:08 +03:00
range.detach();
}
2017-02-18 12:30:49 -07:00
return { text: selectedText, pid: selectedPid, username: username };
2016-06-20 13:06:08 +03:00
}
2016-10-08 19:09:48 +03:00
function bookmarkPost(button, pid) {
var method = button.attr('data-bookmarked') === 'false' ? 'posts.bookmark' : 'posts.unbookmark';
socket.emit(method, {
pid: pid,
2017-05-01 21:38:03 -04:00
room_id: 'topic_' + ajaxify.data.tid,
}, function (err) {
if (err) {
app.alertError(err.message);
}
});
return false;
}
function getData(button, data) {
return button.parents('[data-pid]').attr(data);
}
function getUserSlug(button) {
var slug = '';
2016-03-08 14:38:44 +02:00
var post = button.parents('[data-pid]');
2015-09-26 18:29:27 -04:00
if (button.attr('component') === 'topic/reply') {
return slug;
2015-09-26 18:29:27 -04:00
}
2016-03-08 14:38:44 +02:00
if (post.length) {
slug = utils.slugify(post.attr('data-username'), true);
}
if (post.length && post.attr('data-uid') !== '0') {
slug = '@' + slug;
}
return slug;
}
function togglePostDelete(button) {
2016-06-28 12:34:09 +03:00
var pid = getData(button, 'data-pid');
var postEl = components.get('post', 'pid', pid);
var action = !postEl.hasClass('deleted') ? 'delete' : 'restore';
postAction(action, pid);
}
function purgePost(button) {
postAction('purge', getData(button, 'data-pid'));
}
function postAction(action, pid) {
translator.translate('[[topic:post_' + action + '_confirm]]', function (msg) {
bootbox.confirm(msg, function (confirm) {
if (!confirm) {
return;
}
const route = action === 'purge' ? '' : '/state';
const method = action === 'restore' ? 'put' : 'del';
api[method](`/posts/${pid}${route}`, undefined, undefined, err => app.alertError(err.status.message));
});
});
}
function openChat(button) {
2015-04-02 17:11:55 +08:00
var post = button.parents('[data-pid]');
app.newChat(post.attr('data-uid'));
button.parents('.btn-group').find('.dropdown-toggle').click();
return false;
}
function showStaleWarning(callback) {
var staleThreshold = Math.min(Date.now() - (1000 * 60 * 60 * 24 * ajaxify.data.topicStaleDays), 8640000000000000);
if (staleReplyAnyway || ajaxify.data.lastposttime >= staleThreshold) {
2016-06-21 13:37:17 +03:00
return callback();
}
translator.translate('[[topic:stale.warning]]', function (translated) {
2016-06-21 13:37:17 +03:00
var warning = bootbox.dialog({
2017-02-18 02:38:03 -07:00
title: '[[topic:stale.title]]',
message: translated,
buttons: {
reply: {
label: '[[topic:stale.reply_anyway]]',
className: 'btn-link',
callback: function () {
staleReplyAnyway = true;
callback();
2016-06-21 13:37:17 +03:00
},
2017-02-18 02:38:03 -07:00
},
create: {
label: '[[topic:stale.create]]',
className: 'btn-primary',
callback: function () {
translator.translate('[[topic:link_back, ' + ajaxify.data.title + ', ' + config.relative_path + '/topic/' + ajaxify.data.slug + ']]', function (body) {
$(window).trigger('action:composer.topic.new', {
cid: ajaxify.data.cid,
body: body,
2016-06-21 13:37:17 +03:00
});
2017-02-18 02:38:03 -07:00
});
2017-02-17 19:31:21 -07:00
},
},
2017-02-18 02:38:03 -07:00
},
});
2016-06-21 13:37:17 +03:00
warning.modal();
});
2015-10-06 05:28:05 -04:00
}
return PostTools;
});