mirror of
https://github.com/discourse/discourse.git
synced 2025-05-22 16:51:22 +08:00
Shows who deleted a post
This commit is contained in:
@ -22,17 +22,22 @@ Discourse.ActionSummary = Discourse.Model.extend({
|
|||||||
}
|
}
|
||||||
}.property('count', 'acted', 'actionType'),
|
}.property('count', 'acted', 'actionType'),
|
||||||
|
|
||||||
canAlsoAction: function() {
|
canAlsoAction: Em.computed.and('can_act', 'actionType.notCustomFlag'),
|
||||||
if (this.get('hidden')) return false;
|
usersCollapsed: Em.computed.not('usersExpanded'),
|
||||||
return this.get('can_act');
|
usersExpanded: Em.computed.gt('users.length', 0),
|
||||||
}.property('can_act', 'hidden'),
|
|
||||||
|
|
||||||
// Remove it
|
// Remove it
|
||||||
removeAction: function() {
|
removeAction: function() {
|
||||||
this.set('acted', false);
|
this.setProperties({
|
||||||
this.set('count', this.get('count') - 1);
|
acted: false,
|
||||||
this.set('can_act', true);
|
count: this.get('count') - 1,
|
||||||
return this.set('can_undo', false);
|
can_act: true,
|
||||||
|
can_undo: false
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.get('usersExpanded')) {
|
||||||
|
this.get('users').removeObject(Discourse.User.current());
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Perform this action
|
// Perform this action
|
||||||
@ -42,10 +47,12 @@ Discourse.ActionSummary = Discourse.Model.extend({
|
|||||||
var action = this.get('actionType.name_key');
|
var action = this.get('actionType.name_key');
|
||||||
|
|
||||||
// Mark it as acted
|
// Mark it as acted
|
||||||
this.set('acted', true);
|
this.setProperties({
|
||||||
this.set('count', this.get('count') + 1);
|
acted: true,
|
||||||
this.set('can_act', false);
|
count: this.get('count') + 1,
|
||||||
this.set('can_undo', true);
|
can_act: false,
|
||||||
|
can_undo: true
|
||||||
|
});
|
||||||
|
|
||||||
if(action === 'notify_moderators' || action === 'notify_user') {
|
if(action === 'notify_moderators' || action === 'notify_user') {
|
||||||
this.set('can_undo',false);
|
this.set('can_undo',false);
|
||||||
@ -53,8 +60,8 @@ Discourse.ActionSummary = Discourse.Model.extend({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add ourselves to the users who liked it if present
|
// Add ourselves to the users who liked it if present
|
||||||
if (this.present('users')) {
|
if (this.get('usersExpanded')) {
|
||||||
this.users.pushObject(Discourse.User.current());
|
this.get('users').addObject(Discourse.User.current());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create our post action
|
// Create our post action
|
||||||
@ -113,7 +120,11 @@ Discourse.ActionSummary = Discourse.Model.extend({
|
|||||||
var users = Em.A();
|
var users = Em.A();
|
||||||
actionSummary.set('users', users);
|
actionSummary.set('users', users);
|
||||||
_.each(result,function(user) {
|
_.each(result,function(user) {
|
||||||
|
if (user.id === Discourse.User.current('id')) {
|
||||||
|
users.pushObject(Discourse.User.current());
|
||||||
|
} else {
|
||||||
users.pushObject(Discourse.User.create(user));
|
users.pushObject(Discourse.User.create(user));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -186,6 +186,11 @@ Discourse.Post = Discourse.Model.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
recover: function() {
|
recover: function() {
|
||||||
|
this.setProperties({
|
||||||
|
deleted_at: null,
|
||||||
|
deleted_by: null
|
||||||
|
});
|
||||||
|
|
||||||
return Discourse.ajax("/posts/" + (this.get('id')) + "/recover", { type: 'PUT', cache: false });
|
return Discourse.ajax("/posts/" + (this.get('id')) + "/recover", { type: 'PUT', cache: false });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -6,7 +6,9 @@
|
|||||||
@namespace Discourse
|
@namespace Discourse
|
||||||
@module Discourse
|
@module Discourse
|
||||||
**/
|
**/
|
||||||
Discourse.PostActionType = Discourse.Model.extend({});
|
Discourse.PostActionType = Discourse.Model.extend({
|
||||||
|
notCustomFlag: Em.computed.not('is_custom_flag')
|
||||||
|
});
|
||||||
|
|
||||||
Discourse.PostActionType.reopenClass({
|
Discourse.PostActionType.reopenClass({
|
||||||
MAX_MESSAGE_LENGTH: 500
|
MAX_MESSAGE_LENGTH: 500
|
||||||
|
@ -55,6 +55,13 @@
|
|||||||
</div>
|
</div>
|
||||||
{{view Discourse.RepliesView contentBinding="replies" postViewBinding="view"}}
|
{{view Discourse.RepliesView contentBinding="replies" postViewBinding="view"}}
|
||||||
{{view Discourse.ActionsHistoryView contentBinding="actionsHistory"}}
|
{{view Discourse.ActionsHistoryView contentBinding="actionsHistory"}}
|
||||||
|
|
||||||
|
{{#if deleted_by}}
|
||||||
|
<div class='post-actions'>
|
||||||
|
{{i18n post.deleted_by}} {{avatar deleted_by imageSize="tiny"}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
{{view Discourse.TopicSummaryView postBinding="this"}}
|
{{view Discourse.TopicSummaryView postBinding="this"}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -10,24 +10,27 @@
|
|||||||
Discourse.ActionsHistoryView = Discourse.View.extend({
|
Discourse.ActionsHistoryView = Discourse.View.extend({
|
||||||
tagName: 'section',
|
tagName: 'section',
|
||||||
classNameBindings: [':post-actions', 'hidden'],
|
classNameBindings: [':post-actions', 'hidden'],
|
||||||
|
|
||||||
hidden: Em.computed.empty('content'),
|
hidden: Em.computed.empty('content'),
|
||||||
|
shouldRerender: Discourse.View.renderIfChanged('content.@each', 'content.users.length'),
|
||||||
shouldRerender: Discourse.View.renderIfChanged('content.@each', 'content.users.@each'),
|
|
||||||
|
|
||||||
// This was creating way too many bound ifs and subviews in the handlebars version.
|
// This was creating way too many bound ifs and subviews in the handlebars version.
|
||||||
render: function(buffer) {
|
render: function(buffer) {
|
||||||
if (!this.present('content')) return;
|
if (!this.present('content')) return;
|
||||||
|
|
||||||
return this.get('content').forEach(function(c) {
|
this.get('content').forEach(function(c) {
|
||||||
var actionString, iconsHtml;
|
|
||||||
buffer.push("<div class='post-action'>");
|
buffer.push("<div class='post-action'>");
|
||||||
|
|
||||||
|
var renderActionIf = function(property, dataAttribute, text) {
|
||||||
|
if (!c.get(property)) { return; }
|
||||||
|
buffer.push(" <a href='#' data-" + dataAttribute + "='" + c.get('id') + "'>" + text + "</a>.");
|
||||||
|
};
|
||||||
|
|
||||||
// TODO multi line expansion for flags
|
// TODO multi line expansion for flags
|
||||||
|
var iconsHtml = "";
|
||||||
|
if (c.get('usersExpanded')) {
|
||||||
var postUrl;
|
var postUrl;
|
||||||
if (c.get('users')) {
|
|
||||||
iconsHtml = "";
|
|
||||||
c.get('users').forEach(function(u) {
|
c.get('users').forEach(function(u) {
|
||||||
|
console.log(u);
|
||||||
iconsHtml += "<a href=\"" + Discourse.getURL("/users/") + (u.get('username_lower')) + "\">";
|
iconsHtml += "<a href=\"" + Discourse.getURL("/users/") + (u.get('username_lower')) + "\">";
|
||||||
if (u.post_url) {
|
if (u.post_url) {
|
||||||
postUrl = postUrl || u.post_url;
|
postUrl = postUrl || u.post_url;
|
||||||
@ -42,44 +45,35 @@ Discourse.ActionsHistoryView = Discourse.View.extend({
|
|||||||
});
|
});
|
||||||
|
|
||||||
var key = 'post.actions.people.' + c.get('actionType.name_key');
|
var key = 'post.actions.people.' + c.get('actionType.name_key');
|
||||||
if(postUrl) {
|
if (postUrl) { key = key + "_with_url"; }
|
||||||
key = key + "_with_url";
|
|
||||||
}
|
|
||||||
buffer.push(" " + I18n.t(key, { icons: iconsHtml, postUrl: postUrl}) + ".");
|
buffer.push(" " + I18n.t(key, { icons: iconsHtml, postUrl: postUrl}) + ".");
|
||||||
} else {
|
|
||||||
buffer.push("<a href='#' data-who-acted='" + (c.get('id')) + "'>" + (c.get('description')) + "</a>.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c.get('can_act') && !c.get('actionType.is_custom_flag')) {
|
|
||||||
actionString = I18n.t("post.actions.it_too." + c.get('actionType.name_key'));
|
|
||||||
buffer.push(" <a href='#' data-act='" + (c.get('id')) + "'>" + actionString + "</a>.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c.get('can_undo')) {
|
|
||||||
actionString = I18n.t("post.actions.undo." + c.get('actionType.name_key') );
|
|
||||||
buffer.push(" <a href='#' data-undo='" + (c.get('id')) + "'>" + actionString + "</a>.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c.get('can_clear_flags')) {
|
|
||||||
buffer.push(" <a href='#' data-clear-flags='" + (c.get('id')) + "'>" + (I18n.t("post.actions.clear_flags", { count: c.count })) + "</a>.");
|
|
||||||
}
|
}
|
||||||
|
renderActionIf('usersCollapsed', 'who-acted', c.get('description'));
|
||||||
|
renderActionIf('canAlsoAction', 'act', I18n.t("post.actions.it_too." + c.get('actionType.name_key')));
|
||||||
|
renderActionIf('can_undo', 'undo', I18n.t("post.actions.undo." + c.get('actionType.name_key')));
|
||||||
|
renderActionIf('can_clear_flags', 'clear-flags', I18n.t("post.actions.clear_flags", { count: c.count }));
|
||||||
|
|
||||||
buffer.push("</div>");
|
buffer.push("</div>");
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
actionTypeById: function(actionTypeId) {
|
||||||
|
return this.get('content').findProperty('id', actionTypeId);
|
||||||
|
},
|
||||||
|
|
||||||
click: function(e) {
|
click: function(e) {
|
||||||
var $target, actionTypeId;
|
var $target = $(e.target),
|
||||||
$target = $(e.target);
|
actionTypeId;
|
||||||
|
|
||||||
if (actionTypeId = $target.data('clear-flags')) {
|
if (actionTypeId = $target.data('clear-flags')) {
|
||||||
this.get('controller').clearFlags(this.content.findProperty('id', actionTypeId));
|
this.get('controller').clearFlags(this.actionTypeById(actionTypeId));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// User wants to know who actioned it
|
// User wants to know who actioned it
|
||||||
if (actionTypeId = $target.data('who-acted')) {
|
if (actionTypeId = $target.data('who-acted')) {
|
||||||
this.get('controller').whoActed(this.content.findProperty('id', actionTypeId));
|
this.get('controller').whoActed(this.actionTypeById(actionTypeId));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,6 +118,14 @@ class PostSerializer < BasicPostSerializer
|
|||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def deleted_by
|
||||||
|
BasicUserSerializer.new(object.deleted_by, root: false).as_json
|
||||||
|
end
|
||||||
|
|
||||||
|
def include_deleted_by?
|
||||||
|
object.deleted_by.present?
|
||||||
|
end
|
||||||
|
|
||||||
# Summary of the actions taken on this post
|
# Summary of the actions taken on this post
|
||||||
def actions_summary
|
def actions_summary
|
||||||
result = []
|
result = []
|
||||||
|
@ -732,6 +732,7 @@ en:
|
|||||||
continue_discussion: "Continuing the discussion from {{postLink}}:"
|
continue_discussion: "Continuing the discussion from {{postLink}}:"
|
||||||
follow_quote: "go to the quoted post"
|
follow_quote: "go to the quoted post"
|
||||||
deleted_by_author: "(post removed by author)"
|
deleted_by_author: "(post removed by author)"
|
||||||
|
deleted_by: "deleted by"
|
||||||
expand_collapse: "expand/collapse"
|
expand_collapse: "expand/collapse"
|
||||||
|
|
||||||
has_replies:
|
has_replies:
|
||||||
|
@ -64,6 +64,11 @@ test('destroy by staff', function() {
|
|||||||
present(post.get('deleted_at'), "it has a `deleted_at` field.");
|
present(post.get('deleted_at'), "it has a `deleted_at` field.");
|
||||||
equal(post.get('deleted_by'), user, "it has the user in the `deleted_by` field");
|
equal(post.get('deleted_by'), user, "it has the user in the `deleted_by` field");
|
||||||
ok(Discourse.ajax.calledOnce, "it made an AJAX call");
|
ok(Discourse.ajax.calledOnce, "it made an AJAX call");
|
||||||
|
|
||||||
|
post.recover();
|
||||||
|
blank(post.get('deleted_at'), "it clears `deleted_at` when recovering");
|
||||||
|
blank(post.get('deleted_by'), "it clears `deleted_by` when recovering");
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('destroy by non-staff', function() {
|
test('destroy by non-staff', function() {
|
||||||
|
Reference in New Issue
Block a user