Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the ability to ban posts #577

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions client/css/banned-posts.styl
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
@import colors

.content-wrapper.banned-posts
width: 100%
max-width: 45em
table
border-spacing: 0
width: 100%
tr.default td
background: $default-banned-post-background-color
td, th
padding: .4em
&.color
input[type=text]
width: 8em
&.usages
text-align: center
&.remove, &.set-default
white-space: pre
th
white-space: nowrap
&:first-child
padding-left: 0
&:last-child
padding-right: 0
tfoot
display: none
form
width: auto
13 changes: 13 additions & 0 deletions client/html/banned_post_entry.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<tr data-category='<%- ctx.postBan.checksum %>'>
<td class='name'>
<%- ctx.postBan.checksum %>
</td>
<td class='time'>
<%= ctx.makeRelativeTime(ctx.postBan.time) %>
</td>
<% if (ctx.canDelete) { %>
<td class='remove'>
<a href>Unban</a>
</td>
<% } %>
</tr>
25 changes: 25 additions & 0 deletions client/html/banned_post_list.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<div class='content-wrapper banned-posts'>
<form>
<h1>Banned posts</h1>
<div class="table-wrap">
<table>
<thead>
<tr>
<th class='checksum'>Checksum</th>
<th class='time'>Time of ban</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>

<div class='messages'></div>

<% if (ctx.canDelete) { %>
<div class='buttons'>
<input type='submit' class='save' value='Save changes'>
</div>
<% } %>
</form>
</div>
5 changes: 4 additions & 1 deletion client/html/post_edit_sidebar.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@
</section>
<% } %>

<% if (ctx.canFeaturePosts || ctx.canDeletePosts || ctx.canMergePosts) { %>
<% if (ctx.canFeaturePosts || ctx.canDeletePosts || ctx.canMergePosts || ctx.canBanPosts) { %>
<section class='management'>
<ul>
<% if (ctx.canFeaturePosts) { %>
Expand All @@ -120,6 +120,9 @@
<% if (ctx.canDeletePosts) { %>
<li><a href class='delete'>Delete this post</a></li>
<% } %>
<% if (ctx.canBanPosts) { %>
<li><a href class='ban'>Ban this post</a></li>
<% } %>
</ul>
</section>
<% } %>
Expand Down
59 changes: 59 additions & 0 deletions client/js/controllers/banned_post_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"use strict";

const api = require("../api.js");
const BannedPostList = require("../models/banned_post_list.js");
const topNavigation = require("../models/top_navigation.js");
const BannedPostsView = require("../views/banned_posts_view.js");
const EmptyView = require("../views/empty_view.js");

class BannedPostController {
constructor() {
if (!api.hasPrivilege("posts:ban:list")) {
this._view = new EmptyView();
this._view.showError(
"You don't have privileges to view banned posts."
);
return;
}

topNavigation.activate("banned-posts");
topNavigation.setTitle("Listing banned posts");
BannedPostList.get().then(
(response) => {
this._bannedPosts = response.results;
this._view = new BannedPostsView({
bannedPosts: this._bannedPosts,
canDelete: api.hasPrivilege("poolCategories:delete")
});
this._view.addEventListener("submit", (e) =>
this._evtSubmit(e)
);
},
(error) => {
this._view = new EmptyView();
this._view.showError(error.message);
}
);
}

_evtSubmit(e) {
this._view.clearMessages();
this._view.disableForm();
this._bannedPosts.save().then(
() => {
this._view.enableForm();
this._view.showSuccess("Changes saved.");
},
(error) => {
this._view.enableForm();
this._view.showError(error.message);
}
);
}
}

module.exports = (router) => {
router.enter(["banned-posts"], (ctx, next) => {
ctx.controller = new BannedPostController(ctx, next);
});
};
19 changes: 19 additions & 0 deletions client/js/controllers/post_main_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ class PostMainController extends BasePostController {
this._view.sidebarControl.addEventListener("delete", (e) =>
this._evtDeletePost(e)
);
this._view.sidebarControl.addEventListener("ban", (e) =>
this._evtBanPost(e)
);
this._view.sidebarControl.addEventListener("merge", (e) =>
this._evtMergePost(e)
);
Expand Down Expand Up @@ -165,6 +168,22 @@ class PostMainController extends BasePostController {
);
}

_evtBanPost(e) {
this._view.sidebarControl.disableForm();
this._view.sidebarControl.clearMessages();
e.detail.post.ban().then(
() => {
misc.disableExitConfirmation();
const ctx = router.show(uri.formatClientLink("posts"));
ctx.controller.showSuccess("Post banned.");
},
(error) => {
this._view.sidebarControl.showError(error.message);
this._view.sidebarControl.enableForm();
}
);
}

_evtUpdatePost(e) {
this._view.sidebarControl.disableForm();
this._view.sidebarControl.clearMessages();
Expand Down
3 changes: 3 additions & 0 deletions client/js/controllers/top_navigation_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ class TopNavigationController {
if (!api.hasPrivilege("pools:list")) {
topNavigation.hide("pools");
}
if (!api.hasPrivilege("posts:ban:list")) {
topNavigation.hide("banned-posts");
}
if (api.isLoggedIn()) {
if (!api.hasPrivilege("users:create:any")) {
topNavigation.hide("register");
Expand Down
25 changes: 25 additions & 0 deletions client/js/controls/post_edit_sidebar_control.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class PostEditSidebarControl extends events.EventTarget {
"posts:create:anonymous"
),
canDeletePosts: api.hasPrivilege("posts:delete"),
canBanPosts: api.hasPrivilege("posts:ban"),
canFeaturePosts: api.hasPrivilege("posts:feature"),
canMergePosts: api.hasPrivilege("posts:merge"),
})
Expand Down Expand Up @@ -186,6 +187,12 @@ class PostEditSidebarControl extends events.EventTarget {
);
}

if (this._banLinkNode) {
this._banLinkNode.addEventListener("click", (e) =>
this._evtBanClick(e)
);
}

this._postNotesOverlayControl.addEventListener("blur", (e) =>
this._evtNoteBlur(e)
);
Expand Down Expand Up @@ -301,6 +308,19 @@ class PostEditSidebarControl extends events.EventTarget {
}
}

_evtBanClick(e) {
e.preventDefault();
if (confirm("Are you sure you want to ban this post?")) {
this.dispatchEvent(
new CustomEvent("ban", {
detail: {
post: this._post,
},
})
);
}
}

_evtNoteTextChangeRequest(e) {
if (this._editedNote) {
this._editedNote.text = this._noteTextareaNode.value;
Expand Down Expand Up @@ -517,6 +537,11 @@ class PostEditSidebarControl extends events.EventTarget {
return this._formNode.querySelector(".management .delete");
}

get _banLinkNode() {
return this._formNode.querySelector(".management .ban");
}


get _addNoteLinkNode() {
return this._formNode.querySelector(".notes .add");
}
Expand Down
1 change: 1 addition & 0 deletions client/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ Promise.resolve()
controllers.push(
require("./controllers/user_registration_controller.js")
);
controllers.push(require("./controllers/banned_post_controller.js"));

// 404 controller needs to be registered last
controllers.push(require("./controllers/not_found_controller.js"));
Expand Down
57 changes: 57 additions & 0 deletions client/js/models/banned_post.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"use strict";

const api = require("../api.js");
const uri = require("../util/uri.js");
const events = require("../events.js");

class BannedPost extends events.EventTarget {
constructor() {
super();
this._checksum = "";
this._time = new Date();
}

get checksum() {
return this._checksum;
}

get time() {
return this._time;
}

set checksum(value) {
this._checksum = value;
}

set time(value) {
this._time = value;
}

static fromResponse(response) {
const ret = new BannedPost();
ret._updateFromResponse(response);
return ret;
}

delete() {
return api
.delete(uri.formatApiLink("post-ban", this._checksum))
.then((response) => {
this.dispatchEvent(
new CustomEvent("delete", {
detail: {
bannedPost: this,
},
})
);
return Promise.resolve();
});
}

_updateFromResponse(response) {
this._checksum = response.checksum;
this.time = response.time;
}
}

module.exports = BannedPost;
47 changes: 47 additions & 0 deletions client/js/models/banned_post_list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
const api = require("../api.js");
const uri = require("../util/uri.js");
const AbstractList = require("./abstract_list.js");
const BannedPost = require("./banned_post.js");

class BannedPostList extends AbstractList {
constructor() {
super();
this._deletedBans = [];
this.addEventListener("remove", (e) => this._evtBannedPostDeleted(e));
}

static get() {
return api
.get(uri.formatApiLink("post-ban"))
.then((response) => {
return Promise.resolve(
Object.assign({}, response, {
results: BannedPostList.fromResponse(
response.results
),
})
);
});
}

save() {
let promises = [];
for (let bannedPost of this._deletedBans) {
promises.push(bannedPost.delete());
}

return Promise.all(promises).then((response) => {
this._deletedBans = [];
return Promise.resolve();
});
}

_evtBannedPostDeleted(e) {
this._deletedBans.push(e.detail.bannedPost);
}
}

BannedPostList._itemClass = BannedPost;
BannedPostList._itemName = "bannedPost";

module.exports = BannedPostList;
18 changes: 18 additions & 0 deletions client/js/models/post.js
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,24 @@ class Post extends events.EventTarget {
});
}

ban() {
return api
.post(uri.formatApiLink("post-ban"), {
post_id: this.id,
version: this._version
})
.then((response) => {
this.dispatchEvent(
new CustomEvent("ban", {
detail: {
post: this
}
})
);
return Promise.resolve();
})
}

delete() {
return api
.delete(uri.formatApiLink("post", this.id), {
Expand Down
1 change: 1 addition & 0 deletions client/js/models/top_navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ function _makeTopNavigation() {
ret.add("login", new TopNavigationItem("L", "Log in", "login"));
ret.add("logout", new TopNavigationItem("O", "Logout", "logout"));
ret.add("help", new TopNavigationItem("E", "Help", "help"));
ret.add("banned-posts", new TopNavigationItem("B", "Banned posts", "banned-posts"));
ret.add(
"settings",
new TopNavigationItem(null, "<i class='fa fa-cog'></i>", "settings")
Expand Down
Loading