mirror of
https://github.com/discourse/discourse.git
synced 2024-11-22 09:42:02 +08:00
FEATURE: New 'Reviewable' model to make reviewable items generic
Includes support for flags, reviewable users and queued posts, with REST API backwards compatibility. Co-Authored-By: romanrizzi <romanalejandro@gmail.com> Co-Authored-By: jjaffeux <j.jaffeux@gmail.com>
This commit is contained in:
parent
9a56b398a1
commit
b58867b6e9
|
@ -1,38 +0,0 @@
|
|||
import RestAdapter from "discourse/adapters/rest";
|
||||
|
||||
export default RestAdapter.extend({
|
||||
pathFor(store, type, findArgs) {
|
||||
let args = _.merge({ rest_api: true }, findArgs);
|
||||
delete args.filter;
|
||||
return `/admin/flags/${findArgs.filter}.json?${$.param(args)}`;
|
||||
},
|
||||
|
||||
afterFindAll(results, helper) {
|
||||
results.forEach(flag => {
|
||||
let conversations = [];
|
||||
flag.post_actions.forEach(pa => {
|
||||
if (pa.conversation) {
|
||||
let conversation = {
|
||||
permalink: pa.permalink,
|
||||
hasMore: pa.conversation.has_more,
|
||||
response: {
|
||||
excerpt: pa.conversation.response.excerpt,
|
||||
user: helper.lookup("user", pa.conversation.response.user_id)
|
||||
}
|
||||
};
|
||||
|
||||
if (pa.conversation.reply) {
|
||||
conversation.reply = {
|
||||
excerpt: pa.conversation.reply.excerpt,
|
||||
user: helper.lookup("user", pa.conversation.reply.user_id)
|
||||
};
|
||||
}
|
||||
conversations.push(conversation);
|
||||
}
|
||||
});
|
||||
flag.set("conversations", conversations);
|
||||
});
|
||||
|
||||
return results;
|
||||
}
|
||||
});
|
|
@ -1,3 +0,0 @@
|
|||
export default Ember.Component.extend({
|
||||
classNames: ["flagged-post-response"]
|
||||
});
|
|
@ -1,3 +0,0 @@
|
|||
export default Ember.Component.extend({
|
||||
tagName: "h3"
|
||||
});
|
|
@ -1,58 +0,0 @@
|
|||
import showModal from "discourse/lib/show-modal";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
adminTools: Ember.inject.service(),
|
||||
expanded: false,
|
||||
tagName: "div",
|
||||
classNameBindings: [
|
||||
":flagged-post",
|
||||
"flaggedPost.hidden:hidden-post",
|
||||
"flaggedPost.deleted"
|
||||
],
|
||||
|
||||
canAct: Ember.computed.alias("actableFilter"),
|
||||
|
||||
@computed("filter")
|
||||
actableFilter(filter) {
|
||||
return filter === "active";
|
||||
},
|
||||
|
||||
removeAfter(promise) {
|
||||
return promise.then(() => this.attrs.removePost());
|
||||
},
|
||||
|
||||
_spawnModal(name, model, modalClass) {
|
||||
let controller = showModal(name, { model, admin: true, modalClass });
|
||||
controller.removeAfter = p => this.removeAfter(p);
|
||||
},
|
||||
|
||||
actions: {
|
||||
removeAfter(promise) {
|
||||
return this.removeAfter(promise);
|
||||
},
|
||||
|
||||
disagree() {
|
||||
this.removeAfter(this.get("flaggedPost").disagreeFlags());
|
||||
},
|
||||
|
||||
defer() {
|
||||
this.removeAfter(this.get("flaggedPost").deferFlags());
|
||||
},
|
||||
|
||||
expand() {
|
||||
this.get("flaggedPost")
|
||||
.expandHidden()
|
||||
.then(() => {
|
||||
this.set("expanded", true);
|
||||
});
|
||||
},
|
||||
|
||||
showModerationHistory() {
|
||||
this.get("adminTools").showModerationHistory({
|
||||
filter: "post",
|
||||
post_id: this.get("flaggedPost.id")
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
|
@ -2,6 +2,7 @@ import computed from "ember-addons/ember-computed-decorators";
|
|||
|
||||
const ACTIONS = ["delete", "edit", "none"];
|
||||
export default Ember.Component.extend({
|
||||
postId: null,
|
||||
postAction: null,
|
||||
postEdit: null,
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ export default Ember.Component.extend({
|
|||
},
|
||||
|
||||
// We do a little logic to choose which icon to display and which text
|
||||
@computed("user.flags_agreed", "user.flags_disagreed", "user.flags_ignored")
|
||||
@computed("agreed", "disagreed", "ignored")
|
||||
percentage(agreed, disagreed, ignored) {
|
||||
let total = agreed + disagreed + ignored;
|
||||
let result = { total };
|
||||
|
|
|
@ -13,7 +13,6 @@ export default Ember.Controller.extend(CanCheckEmails, {
|
|||
availableGroups: null,
|
||||
userTitleValue: null,
|
||||
|
||||
showApproval: setting("must_approve_users"),
|
||||
showBadges: setting("enable_badges"),
|
||||
hasLockedTrustLevel: Ember.computed.notEmpty(
|
||||
"model.manual_locked_trust_level"
|
||||
|
@ -215,9 +214,6 @@ export default Ember.Controller.extend(CanCheckEmails, {
|
|||
target_user: this.get("model.username")
|
||||
});
|
||||
},
|
||||
showFlagsReceived() {
|
||||
this.get("adminTools").showFlagsReceived(this.get("model"));
|
||||
},
|
||||
showSuspendModal() {
|
||||
this.get("adminTools").showSuspendModal(this.get("model"));
|
||||
},
|
||||
|
|
|
@ -12,31 +12,7 @@ export default Ember.Controller.extend(CanCheckEmails, {
|
|||
refreshing: false,
|
||||
listFilter: null,
|
||||
selectAll: false,
|
||||
|
||||
queryNew: Ember.computed.equal("query", "new"),
|
||||
queryPending: Ember.computed.equal("query", "pending"),
|
||||
queryHasApproval: Ember.computed.or("queryNew", "queryPending"),
|
||||
showApproval: Ember.computed.and(
|
||||
"siteSettings.must_approve_users",
|
||||
"queryHasApproval"
|
||||
),
|
||||
searchHint: i18n("search_hint"),
|
||||
hasSelection: Ember.computed.gt("selectedCount", 0),
|
||||
|
||||
selectedCount: function() {
|
||||
var model = this.get("model");
|
||||
if (!model || !model.length) return 0;
|
||||
return model.filterBy("selected").length;
|
||||
}.property("model.@each.selected"),
|
||||
|
||||
selectAllChanged: function() {
|
||||
var val = this.get("selectAll");
|
||||
this.get("model").forEach(function(user) {
|
||||
if (user.get("can_approve")) {
|
||||
user.set("selected", val);
|
||||
}
|
||||
});
|
||||
}.observes("selectAll"),
|
||||
|
||||
title: function() {
|
||||
return I18n.t("admin.users.titles." + this.get("query"));
|
||||
|
@ -60,34 +36,8 @@ export default Ember.Controller.extend(CanCheckEmails, {
|
|||
},
|
||||
|
||||
actions: {
|
||||
approveUsers: function() {
|
||||
AdminUser.bulkApprove(this.get("model").filterBy("selected"));
|
||||
this._refreshUsers();
|
||||
},
|
||||
|
||||
rejectUsers: function() {
|
||||
var maxPostAge = this.siteSettings.delete_user_max_post_age;
|
||||
var controller = this;
|
||||
AdminUser.bulkReject(this.get("model").filterBy("selected")).then(
|
||||
function(result) {
|
||||
var message = I18n.t("admin.users.reject_successful", {
|
||||
count: result.success
|
||||
});
|
||||
if (result.failed > 0) {
|
||||
message +=
|
||||
" " +
|
||||
I18n.t("admin.users.reject_failures", { count: result.failed });
|
||||
message +=
|
||||
" " +
|
||||
I18n.t("admin.user.delete_forbidden", { count: maxPostAge });
|
||||
}
|
||||
bootbox.alert(message);
|
||||
controller._refreshUsers();
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
toggleEmailVisibility: function() {
|
||||
toggleEmailVisibility() {
|
||||
this.toggleProperty("showEmails");
|
||||
this._refreshUsers();
|
||||
}
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
export default Ember.Controller.extend({
|
||||
loadingFlags: null,
|
||||
user: null,
|
||||
|
||||
onShow() {
|
||||
this.set("loadingFlags", true);
|
||||
this.store
|
||||
.findAll("flagged-post", {
|
||||
filter: "without_custom",
|
||||
user_id: this.get("model.id")
|
||||
})
|
||||
.then(result => {
|
||||
this.set("loadingFlags", false);
|
||||
this.set("flaggedPosts", result);
|
||||
});
|
||||
}
|
||||
});
|
|
@ -1,21 +0,0 @@
|
|||
import ModalFunctionality from "discourse/mixins/modal-functionality";
|
||||
|
||||
export default Ember.Controller.extend(ModalFunctionality, {
|
||||
loading: null,
|
||||
historyTarget: null,
|
||||
history: null,
|
||||
|
||||
onShow() {
|
||||
this.set("loading", true);
|
||||
this.set("history", null);
|
||||
},
|
||||
|
||||
loadHistory(target) {
|
||||
this.store
|
||||
.findAll("moderation-history", target)
|
||||
.then(result => {
|
||||
this.set("history", result);
|
||||
})
|
||||
.finally(() => this.set("loading", false));
|
||||
}
|
||||
});
|
|
@ -29,7 +29,7 @@ export default Ember.Controller.extend(PenaltyController, {
|
|||
silenced_till: this.get("silenceUntil"),
|
||||
reason: this.get("reason"),
|
||||
message: this.get("message"),
|
||||
post_id: this.get("post.id"),
|
||||
post_id: this.get("postId"),
|
||||
post_action: this.get("postAction"),
|
||||
post_edit: this.get("postEdit")
|
||||
});
|
||||
|
|
|
@ -30,7 +30,7 @@ export default Ember.Controller.extend(PenaltyController, {
|
|||
suspend_until: this.get("suspendUntil"),
|
||||
reason: this.get("reason"),
|
||||
message: this.get("message"),
|
||||
post_id: this.get("post.id"),
|
||||
post_id: this.get("postId"),
|
||||
post_action: this.get("postAction"),
|
||||
post_edit: this.get("postEdit")
|
||||
});
|
||||
|
|
|
@ -7,7 +7,7 @@ export default Ember.Mixin.create(ModalFunctionality, {
|
|||
postEdit: null,
|
||||
postAction: null,
|
||||
user: null,
|
||||
post: null,
|
||||
postId: null,
|
||||
successCallback: null,
|
||||
|
||||
resetModal() {
|
||||
|
@ -15,7 +15,7 @@ export default Ember.Mixin.create(ModalFunctionality, {
|
|||
reason: null,
|
||||
message: null,
|
||||
loadingUser: true,
|
||||
post: null,
|
||||
postId: null,
|
||||
postEdit: null,
|
||||
postAction: "delete",
|
||||
before: null,
|
||||
|
|
|
@ -573,36 +573,6 @@ const AdminUser = Discourse.User.extend({
|
|||
});
|
||||
|
||||
AdminUser.reopenClass({
|
||||
bulkApprove(users) {
|
||||
users.forEach(user => {
|
||||
user.setProperties({
|
||||
approved: true,
|
||||
can_approve: false,
|
||||
selected: false
|
||||
});
|
||||
});
|
||||
|
||||
return ajax("/admin/users/approve-bulk", {
|
||||
type: "PUT",
|
||||
data: { users: users.map(u => u.id) }
|
||||
}).finally(() => bootbox.alert(I18n.t("admin.user.approve_bulk_success")));
|
||||
},
|
||||
|
||||
bulkReject(users) {
|
||||
users.forEach(user => {
|
||||
user.set("can_approve", false);
|
||||
user.set("selected", false);
|
||||
});
|
||||
|
||||
return ajax("/admin/users/reject-bulk", {
|
||||
type: "DELETE",
|
||||
data: {
|
||||
users: users.map(u => u.id),
|
||||
context: window.location.pathname
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
find(user_id) {
|
||||
return ajax("/admin/users/" + user_id + ".json").then(result => {
|
||||
result.loadedDetails = true;
|
||||
|
|
|
@ -1,166 +0,0 @@
|
|||
import { ajax } from "discourse/lib/ajax";
|
||||
import Post from "discourse/models/post";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
|
||||
export default Post.extend({
|
||||
@computed
|
||||
summary() {
|
||||
return _(this.post_actions)
|
||||
.groupBy(function(a) {
|
||||
return a.post_action_type_id;
|
||||
})
|
||||
.map(function(v, k) {
|
||||
return I18n.t("admin.flags.summary.action_type_" + k, {
|
||||
count: v.length
|
||||
});
|
||||
})
|
||||
.join(",");
|
||||
},
|
||||
|
||||
@computed("last_revised_at", "post_actions.@each.created_at")
|
||||
wasEdited(lastRevisedAt) {
|
||||
if (Ember.isEmpty(this.get("last_revised_at"))) {
|
||||
return false;
|
||||
}
|
||||
lastRevisedAt = Date.parse(lastRevisedAt);
|
||||
const postActions = this.get("post_actions") || [];
|
||||
return postActions.some(postAction => {
|
||||
return Date.parse(postAction.created_at) < lastRevisedAt;
|
||||
});
|
||||
},
|
||||
|
||||
@computed("post_actions")
|
||||
hasDisposedBy() {
|
||||
return this.get("post_actions").some(action => action.disposed_by);
|
||||
},
|
||||
|
||||
@computed("post_actions.@each.name_key")
|
||||
flaggedForSpam() {
|
||||
return this.get("post_actions").every(action => action.name_key === "spam");
|
||||
},
|
||||
|
||||
@computed("post_actions.@each.targets_topic")
|
||||
topicFlagged() {
|
||||
return _.any(this.get("post_actions"), function(action) {
|
||||
return action.targets_topic;
|
||||
});
|
||||
},
|
||||
|
||||
@computed("post_actions.@each.targets_topic")
|
||||
postAuthorFlagged() {
|
||||
return _.any(this.get("post_actions"), function(action) {
|
||||
return !action.targets_topic;
|
||||
});
|
||||
},
|
||||
|
||||
@computed("flaggedForSpam")
|
||||
canDeleteAsSpammer(flaggedForSpam) {
|
||||
return (
|
||||
flaggedForSpam &&
|
||||
this.get("user.can_delete_all_posts") &&
|
||||
this.get("user.can_be_deleted")
|
||||
);
|
||||
},
|
||||
|
||||
deletePost() {
|
||||
if (this.get("post_number") === 1) {
|
||||
return ajax("/t/" + this.topic_id, { type: "DELETE", cache: false });
|
||||
} else {
|
||||
return ajax("/posts/" + this.id, { type: "DELETE", cache: false });
|
||||
}
|
||||
},
|
||||
|
||||
disagreeFlags() {
|
||||
return ajax("/admin/flags/disagree/" + this.id, {
|
||||
type: "POST",
|
||||
cache: false
|
||||
}).catch(popupAjaxError);
|
||||
},
|
||||
|
||||
deferFlags(deletePost) {
|
||||
const action = () => {
|
||||
return ajax("/admin/flags/defer/" + this.id, {
|
||||
type: "POST",
|
||||
cache: false,
|
||||
data: { delete_post: deletePost }
|
||||
});
|
||||
};
|
||||
|
||||
if (deletePost && this._hasDeletableReplies()) {
|
||||
return this._actOnFlagAndDeleteReplies(action);
|
||||
} else {
|
||||
return action().catch(popupAjaxError);
|
||||
}
|
||||
},
|
||||
|
||||
agreeFlags(actionOnPost) {
|
||||
const action = () => {
|
||||
return ajax("/admin/flags/agree/" + this.id, {
|
||||
type: "POST",
|
||||
cache: false,
|
||||
data: { action_on_post: actionOnPost }
|
||||
});
|
||||
};
|
||||
|
||||
if (actionOnPost === "delete" && this._hasDeletableReplies()) {
|
||||
return this._actOnFlagAndDeleteReplies(action);
|
||||
} else {
|
||||
return action().catch(popupAjaxError);
|
||||
}
|
||||
},
|
||||
|
||||
_hasDeletableReplies() {
|
||||
return this.get("post_number") > 1 && this.get("reply_count") > 0;
|
||||
},
|
||||
|
||||
_actOnFlagAndDeleteReplies(action) {
|
||||
return new Ember.RSVP.Promise((resolve, reject) => {
|
||||
return ajax(`/posts/${this.id}/reply-ids/all.json`)
|
||||
.then(replies => {
|
||||
const buttons = [];
|
||||
|
||||
buttons.push({
|
||||
label: I18n.t("no_value"),
|
||||
callback() {
|
||||
action()
|
||||
.then(resolve)
|
||||
.catch(error => {
|
||||
popupAjaxError(error);
|
||||
reject();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
buttons.push({
|
||||
label: I18n.t("yes_value"),
|
||||
class: "btn-danger",
|
||||
callback() {
|
||||
Post.deleteMany(replies.map(r => r.id), {
|
||||
agreeWithFirstReplyFlag: false
|
||||
})
|
||||
.then(action)
|
||||
.then(resolve)
|
||||
.catch(error => {
|
||||
popupAjaxError(error);
|
||||
reject();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
bootbox.dialog(
|
||||
I18n.t("admin.flags.delete_replies", { count: replies.length }),
|
||||
buttons
|
||||
);
|
||||
})
|
||||
.catch(error => {
|
||||
popupAjaxError(error);
|
||||
reject();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
postHidden: Ember.computed.alias("hidden"),
|
||||
|
||||
deleted: Ember.computed.or("deleted_at", "topic_deleted_at")
|
||||
});
|
|
@ -1,8 +0,0 @@
|
|||
export default Discourse.Route.extend({
|
||||
redirect() {
|
||||
let segment = this.siteSettings.flags_default_topics
|
||||
? "topics"
|
||||
: "postsActive";
|
||||
this.replaceWith(`adminFlags.${segment}`);
|
||||
}
|
||||
});
|
|
@ -1,20 +0,0 @@
|
|||
import { loadTopicView } from "discourse/models/topic";
|
||||
|
||||
export default Ember.Route.extend({
|
||||
model(params) {
|
||||
let topicRecord = this.store.createRecord("topic", { id: params.id });
|
||||
let topic = loadTopicView(topicRecord).then(() => topicRecord);
|
||||
|
||||
return Ember.RSVP.hash({
|
||||
topic,
|
||||
flaggedPosts: this.store.findAll("flagged-post", {
|
||||
filter: "active",
|
||||
topic_id: params.id
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
setupController(controller, hash) {
|
||||
controller.setProperties(hash);
|
||||
}
|
||||
});
|
|
@ -120,18 +120,6 @@ export default function() {
|
|||
}
|
||||
);
|
||||
|
||||
this.route(
|
||||
"adminFlags",
|
||||
{ path: "/flags", resetNamespace: true },
|
||||
function() {
|
||||
this.route("postsActive", { path: "active" });
|
||||
this.route("postsOld", { path: "old" });
|
||||
this.route("topics", { path: "topics" }, function() {
|
||||
this.route("show", { path: ":id" });
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
this.route(
|
||||
"adminLogs",
|
||||
{ path: "/logs", resetNamespace: true },
|
||||
|
|
|
@ -26,10 +26,6 @@ export default Ember.Service.extend({
|
|||
});
|
||||
},
|
||||
|
||||
showFlagsReceived(user) {
|
||||
showModal(`admin-flags-received`, { admin: true, model: user });
|
||||
},
|
||||
|
||||
checkSpammer(userId) {
|
||||
return AdminUser.find(userId).then(au => this.spammerDetails(au));
|
||||
},
|
||||
|
@ -53,12 +49,7 @@ export default Ember.Service.extend({
|
|||
admin: true,
|
||||
modalClass: `${type}-user-modal`
|
||||
});
|
||||
if (opts.post) {
|
||||
controller.setProperties({
|
||||
post: opts.post,
|
||||
postEdit: opts.post.get("raw")
|
||||
});
|
||||
}
|
||||
controller.setProperties({ postId: opts.postId, postEdit: opts.postEdit });
|
||||
|
||||
return (user.adminUserView
|
||||
? Ember.RSVP.resolve(user)
|
||||
|
@ -81,11 +72,6 @@ export default Ember.Service.extend({
|
|||
this._showControlModal("suspend", user, opts);
|
||||
},
|
||||
|
||||
showModerationHistory(target) {
|
||||
let controller = showModal("admin-moderation-history", { admin: true });
|
||||
controller.loadHistory(target);
|
||||
},
|
||||
|
||||
_deleteSpammer(adminUser) {
|
||||
// Try loading the email if the site supports it
|
||||
let tryEmail = this.siteSettings.moderators_view_emails
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
{{#if currentUser.admin}}
|
||||
{{nav-item route='adminEmail' label='admin.email.title'}}
|
||||
{{/if}}
|
||||
{{nav-item route='adminFlags' label='admin.flags.title'}}
|
||||
{{nav-item route='adminLogs' label='admin.logs.title'}}
|
||||
{{#if currentUser.admin}}
|
||||
{{nav-item route='adminCustomize' label='admin.customize.title'}}
|
||||
|
|
|
@ -8,7 +8,10 @@
|
|||
<div class='flagger-flag-type'>
|
||||
{{post-action-title postAction.post_action_type_id postAction.name_key}}
|
||||
</div>
|
||||
{{user-flag-percentage user=postAction.user}}
|
||||
{{user-flag-percentage
|
||||
agreed=postAction.user.flags_agreed
|
||||
disagreed=postAction.user.flags_disagreed
|
||||
ignored=postAction.user.flags_ignored}}
|
||||
{{/flag-user}}
|
||||
{{/each}}
|
||||
</div>
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
{{#link-to 'adminUser' response.user.id response.user.username class="response-avatar"}}
|
||||
{{avatar response.user imageSize="small"}}
|
||||
{{/link-to}}
|
||||
<div class='excerpt'>{{{response.excerpt}}}</div>
|
||||
{{#if hasMore}}
|
||||
<a href={{permalink}} class="has-more">{{i18n 'admin.flags.more'}}</a>
|
||||
{{/if}}
|
|
@ -1,8 +0,0 @@
|
|||
{{#if flaggedPost.topic.isPrivateMessage}}
|
||||
<span class="private-message-glyph">{{d-icon "envelope"}}</span>
|
||||
{{/if}}
|
||||
{{topic-status topic=flaggedPost.topic}}
|
||||
<a href='{{unbound flaggedPost.url}}'>{{{unbound flaggedPost.topic.fancyTitle}}}</a>
|
||||
{{#if flaggedPost.reply_count}}
|
||||
<span class="flagged-post-reply-count">{{i18n 'admin.flags.replies' count=flaggedPost.reply_count}}</span>
|
||||
{{/if}}
|
|
@ -1,116 +0,0 @@
|
|||
<div class='flagged-post-details'>
|
||||
<div class="flagged-post-avatar">
|
||||
{{#if flaggedPost.postAuthorFlagged}}
|
||||
{{#if flaggedPost.user}}
|
||||
{{#link-to 'adminUser' flaggedPost.user.id flaggedPost.user.username}}
|
||||
{{avatar flaggedPost.user imageSize="large"}}
|
||||
{{/link-to}}
|
||||
{{#if flaggedPost.wasEdited}}
|
||||
<div class='edited-after'>
|
||||
{{d-icon "pencil-alt" title="admin.flags.was_edited"}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{#if canAct}}
|
||||
{{#if flaggedPost.previous_flags_count}}
|
||||
<span title="{{i18n 'admin.flags.previous_flags_count' count=flaggedPost.previous_flags_count}}" class="badge-notification previous-flagged-posts">{{flaggedPost.previous_flags_count}}</span>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div class="flagged-post-contents">
|
||||
<div class='flagged-post-user-details'>
|
||||
<a class='username' href={{user.path}} data-user-card={{flaggedPost.user.username}}>{{format-username flaggedPost.user.username}}</a>
|
||||
{{plugin-outlet
|
||||
name="flagged-post-controls"
|
||||
tagName=""
|
||||
args=(hash flaggedPost=flaggedPost actableFilter=actableFilter topic=topic)}}
|
||||
</div>
|
||||
|
||||
<div class='flagged-post-excerpt'>
|
||||
{{#unless hideTitle}}
|
||||
{{flagged-post-title flaggedPost=flaggedPost}}
|
||||
{{/unless}}
|
||||
{{#if flaggedPost.postAuthorFlagged}}
|
||||
{{#if expanded}}
|
||||
{{{flaggedPost.cooked}}}
|
||||
{{else}}
|
||||
<p>
|
||||
{{{flaggedPost.excerpt}}}
|
||||
<a href {{action "expand"}}>{{i18n "admin.flags.show_full"}}</a>
|
||||
</p>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#if flaggedPost.topicFlagged}}
|
||||
<div class='flagged-post-message'>
|
||||
<span class='text'>{{{i18n 'admin.flags.topic_flagged'}}}</span>
|
||||
<a href={{flaggedPost.url}} class="btn">{{i18n 'admin.flags.visit_topic'}}</a>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#each flaggedPost.conversations as |c|}}
|
||||
<div class='flag-conversation'>
|
||||
{{#if c.response}}
|
||||
{{flagged-post-response response=c.response}}
|
||||
{{#if c.reply}}
|
||||
{{flagged-post-response response=c.reply hasMore=c.hasMore permalink=c.permalink}}
|
||||
{{/if}}
|
||||
<a href={{c.permalink}} class="btn reply-conversation btn-small">
|
||||
{{d-icon "reply"}}
|
||||
{{i18n "admin.flags.reply_message"}}
|
||||
</a>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/each}}
|
||||
|
||||
{{flag-user-lists flaggedPost=flaggedPost showResolvedBy=showResolvedBy}}
|
||||
|
||||
<div class='flagged-post-controls'>
|
||||
{{#if canAct}}
|
||||
{{admin-agree-flag-dropdown
|
||||
post=flaggedPost
|
||||
removeAfter=(action "removeAfter") }}
|
||||
|
||||
{{#if flaggedPost.postHidden}}
|
||||
{{d-button
|
||||
title="admin.flags.disagree_flag_unhide_post_title"
|
||||
class="btn-default disagree-flag"
|
||||
action=(action "disagree")
|
||||
icon="thumbs-o-down"
|
||||
label="admin.flags.disagree_flag_unhide_post"}}
|
||||
{{else}}
|
||||
{{d-button
|
||||
title="admin.flags.disagree_flag_title"
|
||||
class="btn-default disagree-flag"
|
||||
action=(action "disagree")
|
||||
icon="thumbs-o-down"
|
||||
label="admin.flags.disagree_flag"}}
|
||||
{{/if}}
|
||||
|
||||
{{d-button
|
||||
class="btn-default defer-flag"
|
||||
title="admin.flags.ignore_flag_title"
|
||||
action=(action "defer")
|
||||
icon="external-link-alt"
|
||||
label="admin.flags.ignore_flag"}}
|
||||
|
||||
{{admin-delete-flag-dropdown
|
||||
post=flaggedPost
|
||||
removeAfter=(action "removeAfter")}}
|
||||
{{/if}}
|
||||
|
||||
{{d-button
|
||||
class="btn-default"
|
||||
icon="list"
|
||||
label="admin.flags.moderation_history"
|
||||
action=(action "showModerationHistory")}}
|
||||
</div>
|
||||
{{plugin-outlet
|
||||
name="flagged-post-below-controls"
|
||||
tagName=""
|
||||
args=(hash flaggedPost=flaggedPost canAct=canAct actableFilter=actableFilter)}}
|
||||
</div>
|
||||
</div>
|
|
@ -1,17 +0,0 @@
|
|||
{{#if flaggedPosts}}
|
||||
{{#load-more selector=".flagged-post" action=(action "loadMore")}}
|
||||
<div class='flagged-posts'>
|
||||
{{#each flaggedPosts as |flaggedPost|}}
|
||||
{{flagged-post
|
||||
flaggedPost=flaggedPost
|
||||
filter=filter
|
||||
topic=topic
|
||||
showResolvedBy=showResolvedBy
|
||||
removePost=(action "removePost" flaggedPost)
|
||||
hideTitle=topic}}
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/load-more}}
|
||||
{{else}}
|
||||
<p>{{i18n 'admin.flags.no_results'}}</p>
|
||||
{{/if}}
|
|
@ -1,5 +0,0 @@
|
|||
{{#each users as |u|}}
|
||||
{{#link-to 'adminUser' u.id u.username class="flagged-topic-user"}}
|
||||
{{avatar u imageSize="small"}}
|
||||
{{/link-to}}
|
||||
{{/each}}
|
|
@ -1,17 +0,0 @@
|
|||
<td class='date'>
|
||||
{{format-date item.created_at}}
|
||||
</td>
|
||||
<td class='history-item-action'>
|
||||
<div class='action-name'>
|
||||
{{i18n (concat "admin.moderation_history.actions." item.action_name)}}
|
||||
</div>
|
||||
<div class='action-details'>{{item.details}}</div>
|
||||
</td>
|
||||
<td class='history-item-actor'>
|
||||
{{#if item.acting_user}}
|
||||
{{#user-link user=item.acting_user}}
|
||||
{{avatar item.acting_user imageSize="small"}}
|
||||
<span>{{format-username item.acting_user.username}}</span>
|
||||
{{/user-link}}
|
||||
{{/if}}
|
||||
</td>
|
|
@ -1 +0,0 @@
|
|||
{{flagged-posts flaggedPosts=model filter="active"}}
|
|
@ -1 +0,0 @@
|
|||
{{flagged-posts flaggedPosts=model filter="old"}}
|
|
@ -1,54 +0,0 @@
|
|||
{{plugin-outlet name="flagged-topics-before" noTags=true args=(hash flaggedTopics=flaggedTopics)}}
|
||||
|
||||
{{#if flaggedTopics}}
|
||||
<table class='flagged-topics grid'>
|
||||
<thead>
|
||||
{{plugin-outlet name="flagged-topic-header-row" noTags=true}}
|
||||
<th>{{i18n "admin.flags.flagged_topics.topic"}} </th>
|
||||
<th>{{i18n "admin.flags.flagged_topics.type"}}</th>
|
||||
<th>{{I18n "admin.flags.flagged_topics.users"}}</th>
|
||||
<th>{{i18n "admin.flags.flagged_topics.last_flagged"}}</th>
|
||||
<th></th>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each flaggedTopics as |ft|}}
|
||||
<tr class='flagged-topic'>
|
||||
{{plugin-outlet name="flagged-topic-row" noTags=true args=(hash topic=ft.topic)}}
|
||||
|
||||
<td class="topic-title">
|
||||
<div class='combined-title'>
|
||||
{{topic-status topic=ft.topic}}
|
||||
<a href={{ft.topic.relative_url}} target="_blank">{{replace-emoji ft.topic.fancy_title}}</a>
|
||||
</div>
|
||||
</td>
|
||||
<td class="flag-counts">
|
||||
{{#each ft.flag_counts as |fc|}}
|
||||
<div class='flag-counts'>
|
||||
<span class='type-name'>{{post-action-title fc.post_action_type_id fc.name_key}}</span>
|
||||
<span class='type-count'>x{{fc.count}}</span>
|
||||
</div>
|
||||
{{/each}}
|
||||
</td>
|
||||
<td class='flagged-topic-users'>
|
||||
{{flagged-topic-users users=ft.users tagName=""}}
|
||||
</td>
|
||||
<td class="last-flagged">
|
||||
{{format-age ft.last_flag_at}}
|
||||
</td>
|
||||
<td class="flag-details">
|
||||
{{#link-to
|
||||
"adminFlags.topics.show"
|
||||
ft.id
|
||||
class="btn d-button no-text btn-small btn-primary show-details"
|
||||
title=(i18n "admin.flags.show_details")}}
|
||||
{{d-icon "list"}}
|
||||
{{i18n "admin.flags.details"}}
|
||||
{{/link-to}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{else}}
|
||||
{{i18n "admin.flags.flagged_topics.no_results"}}
|
||||
{{/if}}
|
|
@ -1,19 +0,0 @@
|
|||
<div class='flagged-topic-details'>
|
||||
<div class='topic-title'>
|
||||
<h1>
|
||||
{{topic-status topic=topic}}
|
||||
{{#link-to 'topic' topic target="_blank"}}
|
||||
{{{topic.fancyTitle}}}
|
||||
{{/link-to}}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
{{plugin-outlet name="flagged-topic-details-header" args=(hash topic=topic)}}
|
||||
</div>
|
||||
|
||||
<div class='topic-flags'>
|
||||
{{flagged-posts
|
||||
flaggedPosts=flaggedPosts
|
||||
filter="active"
|
||||
topic=topic}}
|
||||
</div>
|
|
@ -1,15 +0,0 @@
|
|||
{{#admin-nav}}
|
||||
{{#if siteSettings.flags_default_topics}}
|
||||
{{nav-item route='adminFlags.topics' label='admin.flags.topics'}}
|
||||
{{nav-item route='adminFlags.postsActive' label='admin.flags.active_posts'}}
|
||||
{{else}}
|
||||
{{nav-item route='adminFlags.postsActive' label='admin.flags.active_posts'}}
|
||||
{{nav-item route='adminFlags.topics' label='admin.flags.topics'}}
|
||||
{{/if}}
|
||||
|
||||
{{nav-item route='adminFlags.postsOld' label='admin.flags.old_posts' class='right'}}
|
||||
{{/admin-nav}}
|
||||
|
||||
<div class="admin-container">
|
||||
{{outlet}}
|
||||
</div>
|
|
@ -1,14 +0,0 @@
|
|||
{{#d-modal-body rawTitle=(i18n "admin.user.flags_received_by" username=model.username)}}
|
||||
{{#conditional-loading-spinner condition=loadingFlags}}
|
||||
{{#each flaggedPosts as |flaggedPost|}}
|
||||
<div class='received-flag flagged-post'>
|
||||
<div class='flagged-post-excerpt'>
|
||||
{{flagged-post-title flaggedPost=flaggedPost}}
|
||||
</div>
|
||||
{{flag-user-lists flaggedPost=flaggedPost showResolvedBy=flaggedPost.hasDisposedBy}}
|
||||
</div>
|
||||
{{else}}
|
||||
{{i18n "admin.user.flags_received_none"}}
|
||||
{{/each}}
|
||||
{{/conditional-loading-spinner}}
|
||||
{{/d-modal-body}}
|
|
@ -1,23 +0,0 @@
|
|||
{{#d-modal-body title="admin.flags.moderation_history"}}
|
||||
{{#conditional-loading-spinner condition=loading}}
|
||||
{{#if history}}
|
||||
<table class='moderation-history'>
|
||||
<tr>
|
||||
<th>{{i18n "admin.logs.created_at"}}</th>
|
||||
<th>{{i18n "admin.logs.action"}}</th>
|
||||
<th>{{i18n "admin.moderation_history.performed_by"}}</th>
|
||||
</tr>
|
||||
{{#each history as |item|}}
|
||||
{{moderation-history-item item=item}}
|
||||
{{/each}}
|
||||
</table>
|
||||
{{else}}
|
||||
<div class='no-results'>
|
||||
{{i18n "admin.moderation_history.no_results"}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/conditional-loading-spinner}}
|
||||
{{/d-modal-body}}
|
||||
<div class="modal-footer">
|
||||
{{d-button action=(route-action "closeModal") label="close"}}
|
||||
</div>
|
|
@ -13,9 +13,9 @@
|
|||
</div>
|
||||
|
||||
{{silence-details reason=reason message=message}}
|
||||
{{#if post}}
|
||||
{{#if postId}}
|
||||
{{penalty-post-action
|
||||
post=post
|
||||
postId=postId
|
||||
postAction=postAction
|
||||
postEdit=postEdit}}
|
||||
{{/if}}
|
||||
|
|
|
@ -14,9 +14,9 @@
|
|||
</div>
|
||||
{{suspension-details reason=reason message=message}}
|
||||
|
||||
{{#if post}}
|
||||
{{#if postId}}
|
||||
{{penalty-post-action
|
||||
post=post
|
||||
postId=postId
|
||||
postAction=postAction
|
||||
postEdit=postEdit}}
|
||||
{{/if}}
|
||||
|
|
|
@ -221,7 +221,7 @@
|
|||
<section class="details">
|
||||
<h1>{{i18n "admin.user.permissions"}}</h1>
|
||||
|
||||
{{#if showApproval}}
|
||||
{{#if siteSettings.must_approve_users}}
|
||||
<div class="display-row">
|
||||
<div class="field">{{i18n "admin.users.approved"}}</div>
|
||||
<div class="value">
|
||||
|
@ -614,12 +614,9 @@
|
|||
</div>
|
||||
<div class="controls">
|
||||
{{#if model.flags_received_count}}
|
||||
{{d-button
|
||||
class="btn-default"
|
||||
action=(action "showFlagsReceived")
|
||||
label="admin.user.show_flags_received"
|
||||
icon="flag"
|
||||
}}
|
||||
{{#link-to 'review' (query-params username=model.username type="ReviewableFlaggedPost" status="all") class="btn"}}
|
||||
{{i18n "admin.user.show_flags_received"}}
|
||||
{{/link-to}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,10 +1,3 @@
|
|||
{{#if hasSelection}}
|
||||
<div id='selected-controls'>
|
||||
<button {{action "approveUsers"}} class='btn'>{{count-i18n key="admin.users.approved_selected" count=selectedCount}}</button>
|
||||
<button {{action "rejectUsers"}} class='btn btn-danger'>{{count-i18n key="admin.users.reject_selected" count=selectedCount}}</button>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="admin-title">
|
||||
<h2>{{title}}</h2>
|
||||
{{#if canCheckEmails}}
|
||||
|
@ -24,9 +17,6 @@
|
|||
{{#if model}}
|
||||
<table class='table users-list grid'>
|
||||
<thead>
|
||||
{{#if showApproval}}
|
||||
<th>{{input type="checkbox" checked=selectAll}}</th>
|
||||
{{/if}}
|
||||
{{admin-directory-toggle field="username" i18nKey='username' order=order ascending=ascending}}
|
||||
{{admin-directory-toggle field="email" i18nKey='email' order=order ascending=ascending}}
|
||||
{{admin-directory-toggle field="last_emailed" i18nKey='admin.users.last_emailed' order=order ascending=ascending}}
|
||||
|
@ -35,7 +25,7 @@
|
|||
{{admin-directory-toggle field="posts_read" i18nKey="admin.user.posts_read_count" order=order ascending=ascending}}
|
||||
{{admin-directory-toggle field="read_time" i18nKey="admin.user.time_read" order=order ascending=ascending}}
|
||||
{{admin-directory-toggle field="created" i18nKey="created" order=order ascending=ascending}}
|
||||
{{#if showApproval}}
|
||||
{{#if siteSettings.must_approve_users}}
|
||||
<th>{{i18n 'admin.users.approved'}}</th>
|
||||
{{/if}}
|
||||
<th> </th>
|
||||
|
@ -43,13 +33,6 @@
|
|||
<tbody>
|
||||
{{#each model as |user|}}
|
||||
<tr class="user {{user.selected}} {{unless user.active 'not-activated'}}">
|
||||
{{#if showApproval}}
|
||||
<td class="approval">
|
||||
{{#if user.can_approve}}
|
||||
{{input type="checkbox" checked=user.selected}}
|
||||
{{/if}}
|
||||
</td>
|
||||
{{/if}}
|
||||
<td class="username">
|
||||
<a href="{{unbound user.path}}" data-user-card="{{unbound user.username}}">
|
||||
{{avatar user imageSize="small"}}
|
||||
|
@ -88,15 +71,10 @@
|
|||
<div>{{{format-duration user.created_at_age}}}</div>
|
||||
</td>
|
||||
|
||||
{{#if showApproval}}
|
||||
<td>
|
||||
{{#if user.approved}}
|
||||
{{i18n 'yes_value'}}
|
||||
{{else}}
|
||||
{{i18n 'no_value'}}
|
||||
{{/if}}
|
||||
</td>
|
||||
{{#if siteSettings.must_approve_users}}
|
||||
<td>{{i18n-yes-no user.approved}}</td>
|
||||
{{/if}}
|
||||
|
||||
<td class="user-status">
|
||||
{{#if user.admin}}
|
||||
{{d-icon "shield-alt" title="admin.title" }}
|
||||
|
|
|
@ -3,9 +3,6 @@
|
|||
<ul class="nav nav-pills">
|
||||
{{nav-item route='adminUsersList.show' routeParam='active' label='admin.users.nav.active'}}
|
||||
{{nav-item route='adminUsersList.show' routeParam='new' label='admin.users.nav.new'}}
|
||||
{{#if siteSettings.must_approve_users}}
|
||||
{{nav-item route='adminUsersList.show' routeParam='pending' label='admin.users.nav.pending'}}
|
||||
{{/if}}
|
||||
{{nav-item route='adminUsersList.show' routeParam='staff' label='admin.users.nav.staff'}}
|
||||
{{nav-item route='adminUsersList.show' routeParam='suspended' label='admin.users.nav.suspended'}}
|
||||
{{nav-item route='adminUsersList.show' routeParam='silenced' label='admin.users.nav.silenced'}}
|
||||
|
|
|
@ -7,8 +7,7 @@ const ADMIN_MODELS = [
|
|||
"embeddable-host",
|
||||
"web-hook",
|
||||
"web-hook-event",
|
||||
"flagged-topic",
|
||||
"moderation-history"
|
||||
"flagged-topic"
|
||||
];
|
||||
|
||||
export function Result(payload, responseJson) {
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
import RestAdapter from "discourse/adapters/rest";
|
||||
|
||||
export default RestAdapter.extend({
|
||||
pathFor() {
|
||||
return "/review/topics";
|
||||
}
|
||||
});
|
|
@ -0,0 +1,9 @@
|
|||
import RestAdapter from "discourse/adapters/rest";
|
||||
|
||||
export default RestAdapter.extend({
|
||||
jsonMode: true,
|
||||
|
||||
pathFor(store, type, findArgs) {
|
||||
return this.appendQueryParams("/review", findArgs);
|
||||
}
|
||||
});
|
|
@ -1,18 +0,0 @@
|
|||
export default Ember.Component.extend({
|
||||
canAct: Ember.computed.equal("filter", "active"),
|
||||
showResolvedBy: Ember.computed.equal("filter", "old"),
|
||||
allLoaded: false,
|
||||
|
||||
actions: {
|
||||
removePost(flaggedPost) {
|
||||
this.get("flaggedPosts").removeObject(flaggedPost);
|
||||
},
|
||||
|
||||
loadMore() {
|
||||
const flaggedPosts = this.get("flaggedPosts");
|
||||
if (flaggedPosts.get("canLoadMore")) {
|
||||
flaggedPosts.loadMore();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,114 +0,0 @@
|
|||
import { propertyEqual } from "discourse/lib/computed";
|
||||
import { bufferedProperty } from "discourse/mixins/buffered-content";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
|
||||
function updateState(state, opts) {
|
||||
opts = opts || {};
|
||||
|
||||
return function() {
|
||||
const post = this.get("post");
|
||||
const args = { state };
|
||||
|
||||
if (opts.deleteUser) {
|
||||
args.delete_user = true;
|
||||
}
|
||||
|
||||
post
|
||||
.update(args)
|
||||
.then(() => {
|
||||
this.removePost(post);
|
||||
})
|
||||
.catch(popupAjaxError);
|
||||
};
|
||||
}
|
||||
|
||||
export default Ember.Component.extend(bufferedProperty("editables"), {
|
||||
editing: propertyEqual("post", "currentlyEditing"),
|
||||
editables: null,
|
||||
_confirmDelete: updateState("rejected", { deleteUser: true }),
|
||||
|
||||
_initEditables: function() {
|
||||
const post = this.get("post");
|
||||
const postOptions = post.get("post_options");
|
||||
|
||||
this.set("editables", {});
|
||||
this.set("editables.raw", post.get("raw"));
|
||||
this.set("editables.category", post.get("category"));
|
||||
this.set("editables.category_id", post.get("category.id"));
|
||||
this.set("editables.title", postOptions.title);
|
||||
this.set("editables.tags", postOptions.tags);
|
||||
}.on("init"),
|
||||
|
||||
_categoryChanged: function() {
|
||||
this.set(
|
||||
"buffered.category",
|
||||
Discourse.Category.findById(this.get("buffered.category_id"))
|
||||
);
|
||||
}.observes("buffered.category_id"),
|
||||
|
||||
editTitleAndCategory: function() {
|
||||
return this.get("editing") && !this.get("post.topic");
|
||||
}.property("editing"),
|
||||
|
||||
tags: function() {
|
||||
return this.get("editables.tags") || this.get("post.topic.tags") || [];
|
||||
}.property("editables.tags"),
|
||||
|
||||
showTags: function() {
|
||||
return (
|
||||
this.siteSettings.tagging_enabled &&
|
||||
!this.get("editing") &&
|
||||
this.get("tags").length > 0
|
||||
);
|
||||
}.property("editing", "tags"),
|
||||
|
||||
editTags: function() {
|
||||
return (
|
||||
this.siteSettings.tagging_enabled &&
|
||||
this.get("editing") &&
|
||||
!this.get("post.topic")
|
||||
);
|
||||
}.property("editing"),
|
||||
|
||||
actions: {
|
||||
approve: updateState("approved"),
|
||||
reject: updateState("rejected"),
|
||||
|
||||
deleteUser() {
|
||||
bootbox.confirm(
|
||||
I18n.t("queue.delete_prompt", {
|
||||
username: this.get("post.user.username")
|
||||
}),
|
||||
confirmed => {
|
||||
if (confirmed) {
|
||||
this._confirmDelete();
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
edit() {
|
||||
// This is stupid but pagedown cannot be on the screen twice or it will break
|
||||
this.set("currentlyEditing", null);
|
||||
Ember.run.scheduleOnce("afterRender", () =>
|
||||
this.set("currentlyEditing", this.get("post"))
|
||||
);
|
||||
},
|
||||
|
||||
confirmEdit() {
|
||||
const buffered = this.get("buffered");
|
||||
|
||||
this.get("post")
|
||||
.update(buffered.getProperties("raw", "title", "tags", "category_id"))
|
||||
.then(() => {
|
||||
this.commitBuffer();
|
||||
this.set("currentlyEditing", null);
|
||||
});
|
||||
},
|
||||
|
||||
cancelEdit() {
|
||||
this.rollbackBuffer();
|
||||
this.set("currentlyEditing", null);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,18 @@
|
|||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
tagName: "",
|
||||
|
||||
multiple: Ember.computed.gt("bundle.actions.length", 1),
|
||||
first: Ember.computed.alias("bundle.actions.firstObject"),
|
||||
|
||||
actions: {
|
||||
performById(id) {
|
||||
this.attrs.performAction(this.get("bundle.actions").findBy("id", id));
|
||||
},
|
||||
|
||||
perform(action) {
|
||||
this.attrs.performAction(action);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,3 @@
|
|||
export default Ember.Component.extend({
|
||||
filteredHistories: Ember.computed.filterBy("histories", "created", false)
|
||||
});
|
|
@ -0,0 +1,153 @@
|
|||
import { ajax } from "discourse/lib/ajax";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import Category from "discourse/models/category";
|
||||
import optionalService from "discourse/lib/optional-service";
|
||||
|
||||
let _components = {};
|
||||
|
||||
export default Ember.Component.extend({
|
||||
adminTools: optionalService(),
|
||||
tagName: "",
|
||||
updating: null,
|
||||
editing: false,
|
||||
_updates: null,
|
||||
|
||||
@computed("reviewable.type")
|
||||
customClass(type) {
|
||||
return type.dasherize();
|
||||
},
|
||||
|
||||
// Find a component to render, if one exists. For example:
|
||||
// `ReviewableUser` will return `reviewable-user`
|
||||
@computed("reviewable.type")
|
||||
reviewableComponent(type) {
|
||||
if (_components[type] !== undefined) {
|
||||
return _components[type];
|
||||
}
|
||||
|
||||
let dasherized = Ember.String.dasherize(type);
|
||||
let templatePath = `components/${dasherized}`;
|
||||
let template =
|
||||
Ember.TEMPLATES[`${templatePath}`] ||
|
||||
Ember.TEMPLATES[`javascripts/${templatePath}`];
|
||||
_components[type] = template ? dasherized : null;
|
||||
return _components[type];
|
||||
},
|
||||
|
||||
_performConfirmed(action) {
|
||||
let reviewable = this.get("reviewable");
|
||||
|
||||
let performAction = () => {
|
||||
let version = reviewable.get("version");
|
||||
this.set("updating", true);
|
||||
return ajax(
|
||||
`/review/${reviewable.id}/perform/${action.id}?version=${version}`,
|
||||
{
|
||||
method: "PUT"
|
||||
}
|
||||
)
|
||||
.then(result => {
|
||||
this.attrs.remove(
|
||||
result.reviewable_perform_result.remove_reviewable_ids
|
||||
);
|
||||
})
|
||||
.catch(popupAjaxError)
|
||||
.finally(() => this.set("updating", false));
|
||||
};
|
||||
|
||||
if (action.client_action) {
|
||||
let actionMethod = this[`client${action.client_action.classify()}`];
|
||||
if (actionMethod) {
|
||||
return actionMethod.call(this, reviewable, performAction);
|
||||
} else {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`No handler for ${action.client_action} found`);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
return performAction();
|
||||
}
|
||||
},
|
||||
|
||||
clientSuspend(reviewable, performAction) {
|
||||
this._penalize("showSuspendModal", reviewable, performAction);
|
||||
},
|
||||
|
||||
clientSilence(reviewable, performAction) {
|
||||
this._penalize("showSilenceModal", reviewable, performAction);
|
||||
},
|
||||
|
||||
_penalize(adminToolMethod, reviewable, performAction) {
|
||||
let adminTools = this.get("adminTools");
|
||||
if (adminTools) {
|
||||
let createdBy = reviewable.get("target_created_by");
|
||||
let postId = reviewable.get("post_id");
|
||||
let postEdit = reviewable.get("raw");
|
||||
return adminTools[adminToolMethod](createdBy, {
|
||||
postId,
|
||||
postEdit,
|
||||
before: performAction
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
edit() {
|
||||
this.set("editing", true);
|
||||
this._updates = { payload: {} };
|
||||
},
|
||||
|
||||
cancelEdit() {
|
||||
this.set("editing", false);
|
||||
},
|
||||
|
||||
saveEdit() {
|
||||
let updates = this._updates;
|
||||
|
||||
// Remove empty objects
|
||||
Object.keys(updates).forEach(name => {
|
||||
let attr = updates[name];
|
||||
if (typeof attr === "object" && Object.keys(attr).length === 0) {
|
||||
delete updates[name];
|
||||
}
|
||||
});
|
||||
|
||||
this.set("updating", true);
|
||||
return this.get("reviewable")
|
||||
.update(updates)
|
||||
.then(() => this.set("editing", false))
|
||||
.catch(popupAjaxError)
|
||||
.finally(() => this.set("updating", false));
|
||||
},
|
||||
|
||||
categoryChanged(category) {
|
||||
if (!category) {
|
||||
category = Category.findUncategorized();
|
||||
}
|
||||
this._updates.category_id = category.id;
|
||||
},
|
||||
|
||||
valueChanged(fieldId, event) {
|
||||
Ember.set(this._updates, fieldId, event.target.value);
|
||||
},
|
||||
|
||||
perform(action) {
|
||||
if (this.get("updating")) {
|
||||
return;
|
||||
}
|
||||
|
||||
let msg = action.get("confirm_message");
|
||||
if (msg) {
|
||||
bootbox.confirm(msg, answer => {
|
||||
if (answer) {
|
||||
return this._performConfirmed(action);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return this._performConfirmed(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
|
@ -3,7 +3,6 @@ import MountWidget from "discourse/components/mount-widget";
|
|||
import { cloak, uncloak } from "discourse/widgets/post-stream";
|
||||
import { isWorkaroundActive } from "discourse/lib/safari-hacks";
|
||||
import offsetCalculator from "discourse/lib/offset-calculator";
|
||||
import optionalService from "discourse/lib/optional-service";
|
||||
|
||||
function findTopView($posts, viewportTop, postsWrapperTop, min, max) {
|
||||
if (max < min) {
|
||||
|
@ -26,7 +25,6 @@ function findTopView($posts, viewportTop, postsWrapperTop, min, max) {
|
|||
}
|
||||
|
||||
export default MountWidget.extend({
|
||||
adminTools: optionalService(),
|
||||
widget: "post-stream",
|
||||
_topVisible: null,
|
||||
_bottomVisible: null,
|
||||
|
@ -329,12 +327,5 @@ export default MountWidget.extend({
|
|||
this.$().off("mouseleave.post-stream");
|
||||
this.appEvents.off("post-stream:refresh", this, "_refresh");
|
||||
this.appEvents.off("post-stream:posted", this, "_posted");
|
||||
},
|
||||
|
||||
showModerationHistory(post) {
|
||||
this.get("adminTools").showModerationHistory({
|
||||
filter: "post",
|
||||
post_id: post.id
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -423,8 +423,7 @@ function applyFlaggedProperties() {
|
|||
});
|
||||
}
|
||||
|
||||
addFlagProperty("currentUser.site_flagged_posts_count");
|
||||
addFlagProperty("currentUser.post_queue_new_count");
|
||||
addFlagProperty("currentUser.reviewable_count");
|
||||
|
||||
export { addFlagProperty, applyFlaggedProperties };
|
||||
|
||||
|
|
|
@ -1,20 +1,11 @@
|
|||
import MountWidget from "discourse/components/mount-widget";
|
||||
import optionalService from "discourse/lib/optional-service";
|
||||
|
||||
export default MountWidget.extend({
|
||||
classNames: "topic-admin-menu-button-container",
|
||||
tagName: "span",
|
||||
widget: "topic-admin-menu-button",
|
||||
adminTools: optionalService(),
|
||||
|
||||
buildArgs() {
|
||||
return this.getProperties("topic", "fixed", "openUpwards", "rightSide");
|
||||
},
|
||||
|
||||
showModerationHistory() {
|
||||
this.get("adminTools").showModerationHistory({
|
||||
filter: "topic",
|
||||
topic_id: this.get("topic.id")
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -93,12 +93,5 @@ export default MountWidget.extend(Docking, {
|
|||
}
|
||||
|
||||
this.dispatch("topic:current-post-scrolled", "timeline-scrollarea");
|
||||
},
|
||||
|
||||
showModerationHistory() {
|
||||
this.get("adminTools").showModerationHistory({
|
||||
filter: "topic",
|
||||
topic_id: this.get("topic.id")
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -56,9 +56,11 @@ export default Ember.Component.extend(LoadMore, {
|
|||
actions: {
|
||||
removeBookmark(userAction) {
|
||||
const stream = this.get("stream");
|
||||
Post.updateBookmark(userAction.get("post_id"), false).then(() => {
|
||||
stream.remove(userAction);
|
||||
});
|
||||
Post.updateBookmark(userAction.get("post_id"), false)
|
||||
.then(() => {
|
||||
stream.remove(userAction);
|
||||
})
|
||||
.catch(popupAjaxError);
|
||||
},
|
||||
|
||||
resumeDraft(item) {
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
import { addFlagProperty as realAddFlagProperty } from "discourse/components/site-header";
|
||||
import deprecated from "discourse-common/lib/deprecated";
|
||||
|
||||
export function addFlagProperty(prop) {
|
||||
deprecated(
|
||||
"importing `addFlagProperty` is deprecated. Use the PluginAPI instead"
|
||||
);
|
||||
realAddFlagProperty(prop);
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
export default Ember.Controller.extend({
|
||||
description: Ember.computed("model.reason", function() {
|
||||
const reason = this.get("model.reason");
|
||||
return reason
|
||||
? I18n.t("queue_reason." + reason + ".description")
|
||||
: I18n.t("queue.approval.description");
|
||||
})
|
||||
});
|
|
@ -0,0 +1,98 @@
|
|||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
queryParams: [
|
||||
"min_score",
|
||||
"type",
|
||||
"status",
|
||||
"category_id",
|
||||
"topic_id",
|
||||
"username"
|
||||
],
|
||||
type: null,
|
||||
status: "pending",
|
||||
min_score: null,
|
||||
category_id: null,
|
||||
reviewables: null,
|
||||
topic_id: null,
|
||||
filtersExpanded: false,
|
||||
username: "",
|
||||
|
||||
init(...args) {
|
||||
this._super(...args);
|
||||
this.set("min_score", this.siteSettings.min_score_default_visibility);
|
||||
this.set("filtersExpanded", !this.site.mobileView);
|
||||
},
|
||||
|
||||
@computed("reviewableTypes")
|
||||
allTypes() {
|
||||
return (this.get("reviewableTypes") || []).map(type => {
|
||||
return {
|
||||
id: type,
|
||||
name: I18n.t(`review.types.${type.underscore()}.title`)
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
@computed
|
||||
statuses() {
|
||||
return [
|
||||
"pending",
|
||||
"approved",
|
||||
"rejected",
|
||||
"ignored",
|
||||
"reviewed",
|
||||
"all"
|
||||
].map(id => {
|
||||
return { id, name: I18n.t(`review.statuses.${id}.title`) };
|
||||
});
|
||||
},
|
||||
|
||||
@computed("filtersExpanded")
|
||||
toggleFiltersIcon(filtersExpanded) {
|
||||
return filtersExpanded ? "chevron-up" : "chevron-down";
|
||||
},
|
||||
|
||||
actions: {
|
||||
remove(ids) {
|
||||
if (!ids) {
|
||||
return;
|
||||
}
|
||||
|
||||
let newList = this.get("reviewables").reject(reviewable => {
|
||||
return ids.indexOf(reviewable.id) !== -1;
|
||||
});
|
||||
this.set("reviewables", newList);
|
||||
},
|
||||
|
||||
resetTopic() {
|
||||
this.set("topic_id", null);
|
||||
this.send("refreshRoute");
|
||||
},
|
||||
|
||||
refresh() {
|
||||
// If filterScore is blank use the default
|
||||
let filterScore = this.get("filterScore");
|
||||
if (!filterScore || filterScore.length === 0) {
|
||||
filterScore = this.siteSettings.min_score_default_visibility;
|
||||
}
|
||||
|
||||
this.setProperties({
|
||||
type: this.get("filterType"),
|
||||
min_score: filterScore,
|
||||
status: this.get("filterStatus"),
|
||||
category_id: this.get("filterCategoryId"),
|
||||
username: this.get("filterUsername")
|
||||
});
|
||||
this.send("refreshRoute");
|
||||
},
|
||||
|
||||
loadMore() {
|
||||
return this.get("reviewables").loadMore();
|
||||
},
|
||||
|
||||
toggleFilters() {
|
||||
this.toggleProperty("filtersExpanded");
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,5 @@
|
|||
function dasherize([value]) {
|
||||
return (value || "").replace(".", "-").dasherize();
|
||||
}
|
||||
|
||||
export default Ember.Helper.helper(dasherize);
|
|
@ -0,0 +1,17 @@
|
|||
export function formatCurrency([reviewable, fieldId]) {
|
||||
// The field `category_id` corresponds to `category`
|
||||
if (fieldId === "category_id") {
|
||||
fieldId = "category.id";
|
||||
}
|
||||
|
||||
let value = Ember.get(reviewable, fieldId);
|
||||
|
||||
// If it's an array, say tags, make a copy so we aren't mutating the original
|
||||
if (Array.isArray(value)) {
|
||||
value = value.slice(0);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
export default Ember.Helper.helper(formatCurrency);
|
|
@ -0,0 +1,5 @@
|
|||
import { registerUnbound } from "discourse-common/lib/helpers";
|
||||
|
||||
registerUnbound("format-score", function(score) {
|
||||
return I18n.toNumber(score || 0, { precision: 1 });
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
import { htmlHelper } from "discourse-common/lib/helpers";
|
||||
import { htmlStatus } from "discourse/helpers/reviewable-status";
|
||||
import { EDITED } from "discourse/models/reviewable-history";
|
||||
|
||||
export default htmlHelper(function(rh) {
|
||||
switch (rh.reviewable_history_type) {
|
||||
case EDITED:
|
||||
return I18n.t("review.history.edited");
|
||||
default:
|
||||
return htmlStatus(rh.status);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,31 @@
|
|||
import { htmlHelper } from "discourse-common/lib/helpers";
|
||||
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||
|
||||
import {
|
||||
PENDING,
|
||||
APPROVED,
|
||||
REJECTED,
|
||||
IGNORED,
|
||||
DELETED
|
||||
} from "discourse/models/reviewable";
|
||||
|
||||
export function htmlStatus(status) {
|
||||
switch (status) {
|
||||
case PENDING:
|
||||
return I18n.t("review.statuses.pending.title");
|
||||
case APPROVED:
|
||||
return `${iconHTML("check")} ${I18n.t("review.statuses.approved.title")}`;
|
||||
case REJECTED:
|
||||
return `${iconHTML("times")} ${I18n.t("review.statuses.rejected.title")}`;
|
||||
case IGNORED:
|
||||
return `${iconHTML("external-link-alt")} ${I18n.t(
|
||||
"review.statuses.ignored.title"
|
||||
)}`;
|
||||
case DELETED:
|
||||
return `${iconHTML("trash")} ${I18n.t("review.statuses.deleted.title")}`;
|
||||
}
|
||||
}
|
||||
|
||||
export default htmlHelper(status => {
|
||||
return htmlStatus(status);
|
||||
});
|
|
@ -21,18 +21,12 @@ export default {
|
|||
const appEvents = container.lookup("app-events:main");
|
||||
|
||||
if (user) {
|
||||
if (user.get("staff")) {
|
||||
bus.subscribe("/flagged_counts", data => {
|
||||
user.set("site_flagged_posts_count", data.total);
|
||||
});
|
||||
bus.subscribe("/queue_counts", data => {
|
||||
user.set("post_queue_new_count", data.post_queue_new_count);
|
||||
if (data.post_queue_new_count > 0) {
|
||||
user.set("show_queued_posts", 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bus.subscribe("/reviewable_counts", data => {
|
||||
user.set("reviewable_count", data.reviewable_count);
|
||||
if (data.reviewable_count > 0) {
|
||||
user.set("show_reviewables", 1);
|
||||
}
|
||||
});
|
||||
bus.subscribe(
|
||||
`/notification/${user.get("id")}`,
|
||||
data => {
|
||||
|
|
|
@ -10,6 +10,7 @@ export default Ember.ArrayProxy.extend({
|
|||
findArgs: null,
|
||||
store: null,
|
||||
__type: null,
|
||||
resultSetMeta: null,
|
||||
|
||||
canLoadMore: function() {
|
||||
return this.get("length") < this.get("totalRows");
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
import RestModel from "discourse/models/rest";
|
||||
|
||||
export const CREATED = 0;
|
||||
export const TRANSITIONED_TO = 1;
|
||||
export const EDITED = 2;
|
||||
|
||||
export default RestModel.extend({
|
||||
created: Ember.computed.equal("reviewable_history_type", CREATED)
|
||||
});
|
50
app/assets/javascripts/discourse/models/reviewable.js.es6
Normal file
50
app/assets/javascripts/discourse/models/reviewable.js.es6
Normal file
|
@ -0,0 +1,50 @@
|
|||
import { ajax } from "discourse/lib/ajax";
|
||||
import RestModel from "discourse/models/rest";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import Category from "discourse/models/category";
|
||||
|
||||
export const PENDING = 0;
|
||||
export const APPROVED = 1;
|
||||
export const REJECTED = 2;
|
||||
export const IGNORED = 3;
|
||||
export const DELETED = 4;
|
||||
|
||||
export default RestModel.extend({
|
||||
pending: Ember.computed.equal("status", PENDING),
|
||||
approved: Ember.computed.equal("status", APPROVED),
|
||||
rejected: Ember.computed.equal("status", REJECTED),
|
||||
ignored: Ember.computed.equal("status", IGNORED),
|
||||
|
||||
@computed("type")
|
||||
humanType(type) {
|
||||
return I18n.t(`review.types.${type.underscore()}.title`, {
|
||||
defaultValue: ""
|
||||
});
|
||||
},
|
||||
|
||||
update(updates) {
|
||||
// If no changes, do nothing
|
||||
if (Object.keys(updates).length === 0) {
|
||||
return Ember.RSVP.resolve();
|
||||
}
|
||||
|
||||
let adapter = this.store.adapterFor("reviewable");
|
||||
return ajax(
|
||||
`/review/${this.get("id")}?version=${this.get("version")}`,
|
||||
adapter.getPayload("PUT", { reviewable: updates })
|
||||
).then(updated => {
|
||||
updated.payload = Object.assign(
|
||||
{},
|
||||
this.get("payload") || {},
|
||||
updated.payload || {}
|
||||
);
|
||||
|
||||
if (updated.category_id) {
|
||||
updated.category = Category.findById(updated.category_id);
|
||||
delete updated.category_id;
|
||||
}
|
||||
|
||||
this.setProperties(updated);
|
||||
});
|
||||
}
|
||||
});
|
|
@ -48,7 +48,7 @@ export default Ember.Object.extend({
|
|||
_plurals: {
|
||||
"post-reply": "post-replies",
|
||||
"post-reply-history": "post_reply_histories",
|
||||
"moderation-history": "moderation_history"
|
||||
reviewable_history: "reviewable_histories"
|
||||
},
|
||||
|
||||
init() {
|
||||
|
@ -223,6 +223,7 @@ export default Ember.Object.extend({
|
|||
totalRows: pageTarget["total_rows_" + typeName] || content.length,
|
||||
loadMoreUrl: pageTarget["load_more_" + typeName],
|
||||
refreshUrl: pageTarget["refresh_" + typeName],
|
||||
resultSetMeta: result.meta,
|
||||
store: this,
|
||||
__type: type
|
||||
};
|
||||
|
@ -328,8 +329,6 @@ export default Ember.Object.extend({
|
|||
|
||||
root = root || obj;
|
||||
|
||||
// Experimental: If serialized with a certain option we'll wire up embedded objects
|
||||
// automatically.
|
||||
if (root.__rest_serializer === "1") {
|
||||
this._hydrateEmbedded(type, obj, root);
|
||||
}
|
||||
|
|
|
@ -126,7 +126,6 @@ export default function() {
|
|||
);
|
||||
|
||||
this.route("badges");
|
||||
this.route("flaggedPosts", { path: "/flagged-posts" });
|
||||
this.route("deletedPosts", { path: "/deleted-posts" });
|
||||
|
||||
this.route(
|
||||
|
@ -170,6 +169,11 @@ export default function() {
|
|||
}
|
||||
);
|
||||
|
||||
this.route("review", { path: "/review" }, function() {
|
||||
this.route("show", { path: "/:reviewable_id" });
|
||||
this.route("index", { path: "/" });
|
||||
this.route("topics", { path: "/topics" });
|
||||
});
|
||||
this.route("signup", { path: "/signup" });
|
||||
this.route("login", { path: "/login" });
|
||||
this.route("login-preferences");
|
||||
|
@ -188,8 +192,6 @@ export default function() {
|
|||
this.route("show", { path: "/:id/:slug" });
|
||||
});
|
||||
|
||||
this.route("queued-posts", { path: "/queued-posts", resetNamespace: true });
|
||||
|
||||
this.route("full-page-search", { path: "/search" });
|
||||
|
||||
this.route("tags", { resetNamespace: true }, function() {
|
||||
|
|
|
@ -52,10 +52,10 @@ const ApplicationRoute = Discourse.Route.extend(OpenComposer, {
|
|||
},
|
||||
|
||||
postWasEnqueued(details) {
|
||||
const title = details.reason
|
||||
? "queue_reason." + details.reason + ".title"
|
||||
: "queue.approval.title";
|
||||
showModal("post-enqueued", { model: details, title });
|
||||
showModal("post-enqueued", {
|
||||
model: details,
|
||||
title: "review.approval.title"
|
||||
});
|
||||
},
|
||||
|
||||
composePrivateMessage(user, post) {
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
|
||||
export default DiscourseRoute.extend({
|
||||
model() {
|
||||
return this.store.find("queuedPost", { status: "new" });
|
||||
},
|
||||
|
||||
actions: {
|
||||
removePost(post) {
|
||||
this.modelFor("queued-posts").removeObject(post);
|
||||
},
|
||||
|
||||
refresh() {
|
||||
this.modelFor("queued-posts").refresh();
|
||||
}
|
||||
}
|
||||
});
|
31
app/assets/javascripts/discourse/routes/review-index.js.es6
Normal file
31
app/assets/javascripts/discourse/routes/review-index.js.es6
Normal file
|
@ -0,0 +1,31 @@
|
|||
export default Discourse.Route.extend({
|
||||
model(params) {
|
||||
// `0` is a valid query param
|
||||
if (params.min_score != null) {
|
||||
params.min_score = params.min_score.toString();
|
||||
}
|
||||
return this.store.findAll("reviewable", params);
|
||||
},
|
||||
|
||||
setupController(controller, model) {
|
||||
let meta = model.resultSetMeta;
|
||||
controller.setProperties({
|
||||
reviewables: model,
|
||||
type: meta.type,
|
||||
filterType: meta.type,
|
||||
filterStatus: meta.status,
|
||||
filterTopic: meta.topic_id,
|
||||
filterCategoryId: meta.category_id,
|
||||
min_score: meta.min_score,
|
||||
filterScore: meta.min_score,
|
||||
reviewableTypes: meta.reviewable_types,
|
||||
filterUsername: meta.username
|
||||
});
|
||||
},
|
||||
|
||||
actions: {
|
||||
refreshRoute() {
|
||||
this.refresh();
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,5 @@
|
|||
export default Discourse.Route.extend({
|
||||
setupController(controller, model) {
|
||||
controller.set("reviewable", model);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,9 @@
|
|||
export default Discourse.Route.extend({
|
||||
model() {
|
||||
return this.store.findAll("reviewable-topic");
|
||||
},
|
||||
|
||||
setupController(controller, model) {
|
||||
controller.set("reviewableTopics", model);
|
||||
}
|
||||
});
|
5
app/assets/javascripts/discourse/routes/review.js.es6
Normal file
5
app/assets/javascripts/discourse/routes/review.js.es6
Normal file
|
@ -0,0 +1,5 @@
|
|||
export default Discourse.Route.extend({
|
||||
titleToken() {
|
||||
return I18n.t("review.title");
|
||||
}
|
||||
});
|
|
@ -1,3 +0,0 @@
|
|||
import createAdminUserPostsRoute from "discourse/routes/build-admin-user-posts-route";
|
||||
|
||||
export default createAdminUserPostsRoute("flagged");
|
|
@ -39,7 +39,14 @@
|
|||
</div>
|
||||
|
||||
{{conditional-loading-spinner condition=loading}}
|
||||
{{textarea autocomplete="discourse" tabindex=tabindex value=value class="d-editor-input" placeholder=placeholderTranslated disabled=disabled}}
|
||||
{{textarea
|
||||
autocomplete="discourse"
|
||||
tabindex=tabindex
|
||||
value=value
|
||||
class="d-editor-input"
|
||||
placeholder=placeholderTranslated
|
||||
disabled=disabled
|
||||
change=change}}
|
||||
{{popup-input-tip validation=validation}}
|
||||
{{plugin-outlet name="after-d-editor" tagName="" args=outletArgs}}
|
||||
</div>
|
||||
|
|
|
@ -1,96 +0,0 @@
|
|||
<div class='queued-post'>
|
||||
<div class='poster'>
|
||||
{{#user-link user=post.user}}
|
||||
{{avatar post.user imageSize="large"}}
|
||||
{{/user-link}}
|
||||
</div>
|
||||
<div class='cooked'>
|
||||
<div class='names'>
|
||||
<span class="username">
|
||||
{{#user-link user=post.user}}
|
||||
{{post.user.username}}
|
||||
{{/user-link}}
|
||||
{{#if post.user.silenced}}
|
||||
{{d-icon "ban" title="user.silenced_tooltip"}}
|
||||
{{/if}}
|
||||
</span>
|
||||
</div>
|
||||
<div class='post-info'>
|
||||
<span class='post-date'>{{age-with-tooltip post.created_at}}</span>
|
||||
</div>
|
||||
<div class='clearfix'></div>
|
||||
|
||||
{{#if editTitleAndCategory}}
|
||||
<span class="edit-title">
|
||||
{{text-field value=buffered.title maxlength=siteSettings.max_topic_title_length}}
|
||||
</span>
|
||||
{{category-chooser value=buffered.category_id}}
|
||||
{{else}}
|
||||
<span class='post-title'>
|
||||
{{i18n "queue.topic"}}
|
||||
{{#if post.topic}}
|
||||
{{topic-link post.topic}}
|
||||
{{else}}
|
||||
{{editables.title}}
|
||||
{{/if}}
|
||||
{{category-badge editables.category}}
|
||||
</span>
|
||||
{{/if}}
|
||||
|
||||
<div class='body'>
|
||||
{{#if editing}}
|
||||
{{d-editor value=buffered.raw}}
|
||||
{{else}}
|
||||
{{cook-text editables.raw}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#if showTags}}
|
||||
<div class="list-tags">
|
||||
{{#each tags as |t|}}
|
||||
{{discourse-tag t}}
|
||||
{{/each}}
|
||||
</div>
|
||||
{{else if editTags}}
|
||||
{{tag-chooser tags=buffered.tags categoryId=buffered.category_id}}
|
||||
{{/if}}
|
||||
|
||||
<div class='queue-controls'>
|
||||
{{#if editing}}
|
||||
{{d-button action=(action "confirmEdit")
|
||||
label="queue.confirm"
|
||||
disabled=post.isSaving
|
||||
class="btn-primary confirm"}}
|
||||
{{d-button action=(action "cancelEdit")
|
||||
label="queue.cancel"
|
||||
icon="times"
|
||||
disabled=post.isSaving
|
||||
class="btn-danger cancel"}}
|
||||
{{else}}
|
||||
{{d-button action=(action "approve")
|
||||
disabled=post.isSaving
|
||||
label="queue.approve"
|
||||
icon="check"
|
||||
class="btn-primary approve"}}
|
||||
{{d-button action=(action "reject")
|
||||
disabled=post.isSaving
|
||||
label="queue.reject"
|
||||
icon="times"
|
||||
class="btn-danger reject"}}
|
||||
{{#if post.can_delete_user}}
|
||||
{{d-button action=(action "deleteUser")
|
||||
disabled=post.isSaving
|
||||
label="queue.delete_user"
|
||||
icon="trash-alt"
|
||||
class="btn-danger delete-user"}}
|
||||
{{/if}}
|
||||
{{d-button action=(action "edit")
|
||||
disabled=post.isSaving
|
||||
label="queue.edit"
|
||||
icon="pencil-alt"
|
||||
class="edit"}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearfix'></div>
|
||||
</div>
|
|
@ -0,0 +1,17 @@
|
|||
{{#if multiple}}
|
||||
{{dropdown-select-box
|
||||
headerIcon=bundle.icon
|
||||
class="reviewable-action-dropdown"
|
||||
nameProperty="label"
|
||||
title=bundle.label
|
||||
content=bundle.actions
|
||||
onSelect=(action "performById")
|
||||
disabled=reviewableUpdating}}
|
||||
{{else}}
|
||||
{{d-button
|
||||
class=(concat "reviewable-action " (dasherize first.id))
|
||||
icon=first.icon
|
||||
action=(action "perform" first)
|
||||
translatedLabel=first.label
|
||||
disabled=reviewableUpdating}}
|
||||
{{/if}}
|
|
@ -0,0 +1,5 @@
|
|||
{{#if post}}
|
||||
<div class='reviewable-conversation-post'>
|
||||
{{#link-to 'user' post.user class="username"}}@{{post.user.username}}{{/link-to}} {{{post.excerpt}}}
|
||||
</div>
|
||||
{{/if}}
|
|
@ -0,0 +1 @@
|
|||
{{category-chooser value=value onChooseCategory=categoryChanged}}
|
|
@ -0,0 +1 @@
|
|||
{{d-editor value=value change=valueChanged}}
|
|
@ -0,0 +1 @@
|
|||
{{mini-tag-chooser tags=value onChangeTags=valueChanged}}
|
|
@ -0,0 +1 @@
|
|||
{{input value=value change=valueChanged class='reviewable-input-text'}}
|
|
@ -0,0 +1 @@
|
|||
{{textarea value=value change=valueChanged class="reviewable-input-textarea"}}
|
|
@ -0,0 +1,23 @@
|
|||
<div class='created-by'>
|
||||
{{#user-link user=reviewable.target_created_by}}
|
||||
{{avatar reviewable.target_created_by imageSize="large"}}
|
||||
{{/user-link}}
|
||||
</div>
|
||||
|
||||
<div class='post-contents'>
|
||||
<div class='names'>
|
||||
<span class="username">
|
||||
{{#user-link user=reviewable.target_created_by}}
|
||||
{{reviewable.target_created_by.username}}
|
||||
{{/user-link}}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{{reviewable-topic-link topic=reviewable.topic}}
|
||||
|
||||
<div class='post-body'>
|
||||
{{{reviewable.cooked}}}
|
||||
</div>
|
||||
|
||||
{{yield}}
|
||||
</div>
|
|
@ -0,0 +1,23 @@
|
|||
{{#if filteredHistories}}
|
||||
<table class='reviewable-histories'>
|
||||
<tr>
|
||||
<th colspan="3">{{i18n "review.history.title"}}</th>
|
||||
</tr>
|
||||
{{#each filteredHistories as |rh|}}
|
||||
{{#unless rh.created}}
|
||||
<tr>
|
||||
<td>{{format-date rh.created_at format="medium"}}</td>
|
||||
<td>
|
||||
{{#user-link user=rs.user}}
|
||||
{{avatar rh.created_by imageSize="tiny"}}
|
||||
{{rh.created_by.username}}
|
||||
{{/user-link}}
|
||||
</td>
|
||||
<td>
|
||||
{{reviewable-history-description rh}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/unless}}
|
||||
{{/each}}
|
||||
</table>
|
||||
{{/if}}
|
|
@ -0,0 +1,79 @@
|
|||
<div class='reviewable-item {{customClass}}' data-reviewable-id={{reviewable.id}}>
|
||||
<div class='reviewable-meta-data'>
|
||||
<div class='reviewable-type'>{{reviewable.humanType}}</div>
|
||||
<div class='reviewable-category'>
|
||||
{{category-badge reviewable.category}}
|
||||
</div>
|
||||
|
||||
<div class='created-at'>
|
||||
{{#link-to 'review.show' reviewable.id}}{{age-with-tooltip reviewable.created_at}}{{/link-to}}
|
||||
</div>
|
||||
<div class='score' title={{i18n "review.scores.score"}}>{{format-score reviewable.score}}</div>
|
||||
|
||||
<div class='status'>
|
||||
{{#if reviewable.approved}}
|
||||
{{d-icon "check"}} {{i18n "review.statuses.approved.title"}}
|
||||
{{else if reviewable.rejected}}
|
||||
{{d-icon "times"}} {{i18n "review.statuses.rejected.title"}}
|
||||
{{else if reviewable.ignored}}
|
||||
{{d-icon "external-link-alt"}} {{i18n "review.statuses.ignored.title"}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='reviewable-contents'>
|
||||
{{#if editing}}
|
||||
<div class='editable-fields'>
|
||||
{{#each reviewable.editable_fields as |f|}}
|
||||
<div class='editable-field {{dasherize f.id}}'>
|
||||
{{component
|
||||
(concat "reviewable-field-" f.type)
|
||||
tagName=''
|
||||
value=(editable-value reviewable f.id)
|
||||
tagCategoryId=reviewable.category.id
|
||||
valueChanged=(action "valueChanged" f.id)
|
||||
categoryChanged=(action "categoryChanged")}}
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{else}}
|
||||
{{#component reviewableComponent reviewable=reviewable tagName=''}}
|
||||
{{reviewable-scores scores=reviewable.reviewable_scores}}
|
||||
{{reviewable-histories histories=reviewable.reviewable_histories}}
|
||||
{{/component}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class='reviewable-actions'>
|
||||
{{#if editing}}
|
||||
{{d-button
|
||||
class="btn-primary reviewable-action save-edit"
|
||||
disabled=updating
|
||||
icon="check"
|
||||
action=(action "saveEdit")
|
||||
label="review.save"}}
|
||||
{{d-button
|
||||
class="btn-danger reviewable-action cancel-edit"
|
||||
disabled=updating
|
||||
icon="times"
|
||||
action=(action "cancelEdit")
|
||||
label="review.cancel"}}
|
||||
{{else}}
|
||||
{{#each reviewable.bundled_actions as |bundle|}}
|
||||
{{reviewable-bundled-action
|
||||
bundle=bundle
|
||||
performAction=(action "perform")
|
||||
reviewableUpdating=updating}}
|
||||
{{/each}}
|
||||
|
||||
{{#if reviewable.can_edit}}
|
||||
{{d-button
|
||||
class="reviewable-action edit"
|
||||
disabled=updating
|
||||
icon="pencil-alt"
|
||||
action=(action "edit")
|
||||
label="review.edit"}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
</div>
|
|
@ -0,0 +1,37 @@
|
|||
<div class='created-by'>
|
||||
{{#user-link user=reviewable.created_by}}
|
||||
{{avatar reviewable.created_by imageSize="large"}}
|
||||
{{/user-link}}
|
||||
</div>
|
||||
|
||||
<div class='post-contents'>
|
||||
<div class='names'>
|
||||
<span class="username">
|
||||
{{#user-link user=reviewable.created_by}}
|
||||
{{reviewable.created_by.username}}
|
||||
{{/user-link}}
|
||||
{{#if reviewable.created_by.silenced}}
|
||||
{{d-icon "ban" title="user.silenced_tooltip"}}
|
||||
{{/if}}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{{#reviewable-topic-link topic=reviewable.topic}}
|
||||
{{i18n "review.new_topic"}}
|
||||
{{reviewable.payload.title}}
|
||||
{{/reviewable-topic-link}}
|
||||
|
||||
<div class='post-body'>
|
||||
{{cook-text reviewable.payload.raw}}
|
||||
</div>
|
||||
|
||||
{{#if reviewable.payload.tags}}
|
||||
<div class="list-tags">
|
||||
{{#each reviewable.payload.tags as |t|}}
|
||||
{{discourse-tag t}}
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{yield}}
|
||||
</div>
|
|
@ -0,0 +1,41 @@
|
|||
{{#if scores}}
|
||||
<table class='reviewable-scores'>
|
||||
<tr>
|
||||
<th class='user'>{{i18n "review.scores.submitted_by"}}</th>
|
||||
<th>{{i18n "review.scores.description"}}</th>
|
||||
<th>{{i18n "review.scores.score"}}</th>
|
||||
</tr>
|
||||
{{#each scores as |rs|}}
|
||||
<tr class='reviewable-score'>
|
||||
<td class='user'>
|
||||
{{#user-link user=rs.user}}
|
||||
{{avatar rs.user imageSize="tiny"}}
|
||||
{{rs.user.username}}
|
||||
{{/user-link}}
|
||||
{{user-flag-percentage
|
||||
agreed=rs.agree_stats.agreed
|
||||
disagreed=rs.agree_stats.disagreed
|
||||
ignored=rs.agree_stats.ignored}}
|
||||
</td>
|
||||
<td>{{rs.score_type.title}}</td>
|
||||
<td>{{format-score rs.score}}</td>
|
||||
</tr>
|
||||
{{#if rs.reviewable_conversation}}
|
||||
<tr>
|
||||
<td colspan='3'>
|
||||
<div class='reviewable-conversation'>
|
||||
{{#each rs.reviewable_conversation.conversation_posts as |p|}}
|
||||
{{reviewable-conversation-post post=p}}
|
||||
{{/each}}
|
||||
<div class='controls'>
|
||||
<a href={{rs.reviewable_conversation.permalink}} class='btn btn-small'>
|
||||
{{i18n "review.conversation.view_full"}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</table>
|
||||
{{/if}}
|
|
@ -0,0 +1,10 @@
|
|||
<div class='post-topic'>
|
||||
{{#if topic}}
|
||||
{{i18n "review.topic"}}
|
||||
{{topic-status topic=topic}}
|
||||
{{topic-link topic}}
|
||||
{{i18n "review.topic_replies" count=topic.reply_count}}
|
||||
{{else}}
|
||||
{{yield}}
|
||||
{{/if}}
|
||||
</div>
|
|
@ -0,0 +1,8 @@
|
|||
<div class='reviewable-user-info'>
|
||||
<div class='reviewable-user-details'>
|
||||
{{reviewable.username}}
|
||||
{{reviewable.email}}
|
||||
</div>
|
||||
|
||||
{{yield}}
|
||||
</div>
|
|
@ -1,8 +1,8 @@
|
|||
{{#d-modal-body}}
|
||||
<p>{{{description}}}</p>
|
||||
<p>{{i18n "review.approval.description"}}</p>
|
||||
|
||||
<p>{{{i18n "queue.approval.pending_posts" count=model.pending_count}}}</p>
|
||||
<p>{{{i18n "review.approval.pending_posts" count=model.pending_count}}}</p>
|
||||
{{/d-modal-body}}
|
||||
<div class="modal-footer">
|
||||
{{d-button action=(route-action "closeModal") class="btn-primary" label="queue.approval.ok"}}
|
||||
{{d-button action=(route-action "closeModal") class="btn-primary" label="review.approval.ok"}}
|
||||
</div>
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
<div class='container'>
|
||||
<div class='queued-posts'>
|
||||
{{#each model as |post|}}
|
||||
{{queued-post post=post currentlyEditing=editing removePost=(route-action "removePost" post)}}
|
||||
{{else}}
|
||||
<p>{{i18n "queue.none"}}</p>
|
||||
{{/each}}
|
||||
|
||||
{{d-button action=(route-action "refresh") label="refresh" icon="sync" disabled=model.refreshing class="btn-default" id='refresh-queued'}}
|
||||
</div>
|
||||
</div>
|
83
app/assets/javascripts/discourse/templates/review-index.hbs
Normal file
83
app/assets/javascripts/discourse/templates/review-index.hbs
Normal file
|
@ -0,0 +1,83 @@
|
|||
<div class="reviewable">
|
||||
<ul class="nav nav-pills reviewable-title">
|
||||
{{nav-item route='review.index' label='review.view_all'}}
|
||||
{{nav-item route='review.topics' label='review.grouped_by_topic'}}
|
||||
</ul>
|
||||
|
||||
<div class="reviewable-container">
|
||||
<div class="reviewable-list">
|
||||
{{#if reviewables}}
|
||||
{{#load-more selector=".reviewable-item" action=(action "loadMore")}}
|
||||
<div class='reviewables'>
|
||||
{{#each reviewables as |r|}}
|
||||
{{reviewable-item reviewable=r remove=(action "remove")}}
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/load-more}}
|
||||
{{conditional-loading-spinner condition=reviewables.loadingMore}}
|
||||
{{else}}
|
||||
<div class="no-review">
|
||||
{{i18n "review.none"}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div class='reviewable-filters'>
|
||||
<div class='reviewable-filter'>
|
||||
<label class='filter-label'>{{i18n "review.filters.status"}}</label>
|
||||
{{combo-box value=filterStatus content=statuses}}
|
||||
</div>
|
||||
|
||||
{{#if filtersExpanded}}
|
||||
<div class='reviewable-filter'>
|
||||
<label class='filter-label'>{{i18n "review.filters.type.title"}}</label>
|
||||
{{combo-box value=filterType content=allTypes none="review.filters.type.all"}}
|
||||
</div>
|
||||
|
||||
<div class='reviewable-filter'>
|
||||
<label class='filter-label'>{{i18n "review.filters.minimum_score"}}</label>
|
||||
{{input value=filterScore class="score-filter"}}
|
||||
</div>
|
||||
|
||||
<div class='reviewable-filter'>
|
||||
<label class='filter-label'>{{i18n "review.filters.category"}}</label>
|
||||
{{category-chooser none="category.all" value=filterCategoryId}}
|
||||
</div>
|
||||
|
||||
<div class='reviewable-filter topic-filter'>
|
||||
{{i18n "review.filtered_user"}}
|
||||
{{user-selector
|
||||
excludeCurrentUser=false
|
||||
usernames=filterUsername
|
||||
fullWidthWrap="true"
|
||||
class="user-selector"
|
||||
single="true"
|
||||
canReceiveUpdates="true"}}
|
||||
</div>
|
||||
|
||||
{{#if filterTopic}}
|
||||
<div class='reviewable-filter topic-filter'>
|
||||
{{i18n "review.filtered_topic"}}
|
||||
{{d-button label="review.show_all_topics" icon="times" action=(action "resetTopic")}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
<div class='reviewable-filters-actions'>
|
||||
{{d-button
|
||||
icon="sync"
|
||||
label="review.filters.refresh"
|
||||
|
||||
class="btn-primary refresh" action=(action "refresh")}}
|
||||
|
||||
{{#if site.mobileView}}
|
||||
{{d-button
|
||||
label="show_help"
|
||||
icon=toggleFiltersIcon
|
||||
class="btn-default expand-secondary-filters"
|
||||
action=(action "toggleFilters")}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1 @@
|
|||
{{reviewable-item reviewable=reviewable}}
|
45
app/assets/javascripts/discourse/templates/review-topics.hbs
Normal file
45
app/assets/javascripts/discourse/templates/review-topics.hbs
Normal file
|
@ -0,0 +1,45 @@
|
|||
<div class="reviewable">
|
||||
<ul class="nav nav-pills reviewable-title">
|
||||
{{nav-item route='review.index' routeParam=(query-params topic_id=null) label='review.view_all'}}
|
||||
{{nav-item route='review.topics' label='review.grouped_by_topic'}}
|
||||
</ul>
|
||||
|
||||
{{#if reviewableTopics}}
|
||||
<table class='reviewable-topics'>
|
||||
<thead>
|
||||
<th>{{i18n "review.topics.topic"}} </th>
|
||||
<th>{{i18n "review.topics.reviewable_count"}}</th>
|
||||
<th>{{i18n "review.topics.reported_by"}}</th>
|
||||
<th></th>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each reviewableTopics as |rt|}}
|
||||
<tr class='reviewable-topic'>
|
||||
<td class="topic-title">
|
||||
<div class='combined-title'>
|
||||
{{topic-status topic=rt}}
|
||||
<a href={{rt.relative_url}} target="_blank">{{replace-emoji rt.fancy_title}}</a>
|
||||
</div>
|
||||
</td>
|
||||
<td class="reviewable-count">
|
||||
{{rt.stats.count}}
|
||||
</td>
|
||||
<td class="reported-by">
|
||||
{{i18n "review.topics.unique_users" count=rt.stats.unique_users}}
|
||||
</td>
|
||||
<td class="reviewable-details">
|
||||
{{#link-to "review.index" (query-params topic_id=rt.id) class="btn btn-primary btn-small"}}
|
||||
{{d-icon "list"}}
|
||||
<span>{{i18n "review.topics.details"}}</span>
|
||||
{{/link-to}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{else}}
|
||||
<div class="no-review">
|
||||
{{i18n "review.none"}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
1
app/assets/javascripts/discourse/templates/review.hbs
Normal file
1
app/assets/javascripts/discourse/templates/review.hbs
Normal file
|
@ -0,0 +1 @@
|
|||
{{outlet}}
|
|
@ -213,14 +213,13 @@
|
|||
|
||||
{{#if model.pending_posts_count}}
|
||||
<div class="has-pending-posts">
|
||||
{{{i18n "queue.has_pending_posts" count=model.pending_posts_count}}}
|
||||
<div>
|
||||
{{{i18n "review.topic_has_pending" count=model.pending_posts_count}}}
|
||||
</div>
|
||||
|
||||
{{#if currentUser.show_queued_posts}}
|
||||
{{#link-to "queued-posts"}}
|
||||
{{d-icon "check"}}
|
||||
{{i18n "queue.view_pending"}}
|
||||
{{/link-to}}
|
||||
{{/if}}
|
||||
{{#link-to 'review' (query-params topic_id=model.id type="ReviewableQueuedPost" status="pending")}}
|
||||
{{i18n "review.view_pending"}}
|
||||
{{/link-to}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
|
|
|
@ -10,9 +10,9 @@
|
|||
{{/if}}
|
||||
{{#if model.number_of_flagged_posts}}
|
||||
<div>
|
||||
{{#link-to 'user.flaggedPosts' model}}
|
||||
{{#link-to 'review' (query-params username=model.username status='all' type='ReviewableFlaggedPost')}}
|
||||
<span class="flagged-posts">{{model.number_of_flagged_posts}}</span>{{i18n 'user.staff_counters.flagged_posts'}}
|
||||
{{/link-to}}
|
||||
{{/link-to}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if model.number_of_deleted_posts}}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { createWidget } from "discourse/widgets/widget";
|
||||
import { iconNode } from "discourse-common/lib/icon-library";
|
||||
import { h } from "virtual-dom";
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
|
||||
export const ButtonClass = {
|
||||
tagName: "button.widget-button.btn",
|
||||
|
@ -83,6 +84,10 @@ export const ButtonClass = {
|
|||
this.sendWidgetAction(attrs.secondaryAction);
|
||||
}
|
||||
|
||||
if (attrs.url) {
|
||||
return DiscourseURL.routeTo(attrs.url);
|
||||
}
|
||||
|
||||
if (attrs.sendActionEvent) {
|
||||
return this.sendWidgetAction(attrs.action, e);
|
||||
}
|
||||
|
|
|
@ -46,8 +46,7 @@ export default createWidget("hamburger-menu", {
|
|||
},
|
||||
|
||||
adminLinks() {
|
||||
const { currentUser, siteSettings } = this;
|
||||
let flagsPath = siteSettings.flags_default_topics ? "topics" : "active";
|
||||
const { currentUser } = this;
|
||||
|
||||
const links = [
|
||||
{
|
||||
|
@ -55,27 +54,16 @@ export default createWidget("hamburger-menu", {
|
|||
className: "admin-link",
|
||||
icon: "wrench",
|
||||
label: "admin_title"
|
||||
},
|
||||
{
|
||||
href: `/admin/flags/${flagsPath}`,
|
||||
className: "flagged-posts-link",
|
||||
icon: "flag",
|
||||
label: "flags_title",
|
||||
badgeClass: "flagged-posts",
|
||||
badgeTitle: "notifications.total_flagged",
|
||||
badgeCount: "site_flagged_posts_count"
|
||||
}
|
||||
];
|
||||
|
||||
if (currentUser.show_queued_posts) {
|
||||
links.push({
|
||||
route: "queued-posts",
|
||||
className: "queued-posts-link",
|
||||
label: "queue.title",
|
||||
badgeCount: "post_queue_new_count",
|
||||
badgeClass: "queued-posts"
|
||||
});
|
||||
}
|
||||
links.push({
|
||||
route: "review",
|
||||
className: "review",
|
||||
label: "review.title",
|
||||
badgeCount: "reviewable_count",
|
||||
badgeClass: "reviewables"
|
||||
});
|
||||
|
||||
if (currentUser.admin) {
|
||||
links.push({
|
||||
|
|
|
@ -6,10 +6,7 @@ createWidget(
|
|||
"post-admin-menu-button",
|
||||
jQuery.extend(ButtonClass, {
|
||||
tagName: "li.btn",
|
||||
click() {
|
||||
this.sendWidgetAction("closeAdminMenu");
|
||||
return this.sendWidgetAction(this.attrs.action);
|
||||
}
|
||||
secondaryAction: "closeAdminMenu"
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -23,8 +20,8 @@ export function buildManageButtons(attrs, currentUser, siteSettings) {
|
|||
contents.push({
|
||||
icon: "list",
|
||||
className: "btn-default",
|
||||
label: "admin.flags.moderation_history",
|
||||
action: "showModerationHistory"
|
||||
label: "review.moderation_history",
|
||||
url: `/review?topic_id=${attrs.topicId}&status=all`
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user