Add the ability to ban posts

This commit is contained in:
rebel 2023-05-12 02:52:23 +02:00
parent 782f069031
commit 1d00a8f60d
10 changed files with 146 additions and 1 deletions

View file

@ -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) { %>
@ -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>
<% } %>

View file

@ -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)
);
@ -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();

View file

@ -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"),
})
@ -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)
);
@ -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;
@ -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");
}

View file

@ -334,6 +334,23 @@ class Post extends events.EventTarget {
});
}
ban() {
return api
.post(uri.formatApiLink("post-ban", 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), {

View file

@ -116,6 +116,7 @@ privileges:
'posts:bulk-edit:tags': power
'posts:bulk-edit:safety': power
'posts:bulk-edit:delete': power
'posts:ban': moderator
'tags:create': regular
'tags:edit:names': power

View file

@ -183,6 +183,18 @@ def delete_post(ctx: rest.Context, params: Dict[str, str]) -> rest.Response:
return {}
@rest.routes.post("/post-ban/(?P<post_id>[^/]+)/?")
def ban_post(ctx: rest.Context, params: Dict[str, str]) -> rest.Response:
auth.verify_privilege(ctx.user, "posts:ban")
post = _get_post(params)
versions.verify_version(post, ctx)
posts.ban(posts.create_ban(post))
snapshots.delete(post, ctx.user)
posts.delete(post)
ctx.session.commit()
return {}
@rest.routes.post("/post-merge/?")
def merge_posts(
ctx: rest.Context, _params: Dict[str, str] = {}

View file

@ -50,6 +50,12 @@ class PostAlreadyUploadedError(errors.ValidationError):
)
class PostBannedError(errors.ValidationError):
def __init__(self, message: str = "This file was banned", extra_fields: Dict[str, str] = None) -> None:
super().__init__(message, extra_fields)
class InvalidPostIdError(errors.ValidationError):
pass
@ -425,6 +431,15 @@ def create_post(
return post, new_tags
def create_ban(post: model.Post) -> model.PostBan:
ban = model.PostBan()
ban.checksum = post.checksum
ban.time = datetime.utcnow()
db.session.add(ban)
return ban
def update_post_safety(post: model.Post, safety: str) -> None:
assert post
safety = util.flip(SAFETY_MAP).get(safety, None)
@ -634,6 +649,7 @@ def update_post_content(post: model.Post, content: Optional[bytes]) -> None:
.filter(model.Post.post_id != post.post_id)
.one_or_none()
)
if (
other_post
and other_post.post_id
@ -641,6 +657,15 @@ def update_post_content(post: model.Post, content: Optional[bytes]) -> None:
):
raise PostAlreadyUploadedError(other_post)
post_ban = (db.session.query(model.PostBan)
.filter(model.PostBan.checksum == post.checksum)
.one_or_none()
)
if (post_ban):
raise PostBannedError()
if update_signature:
purge_post_signature(post)
post.signature = generate_post_signature(post, content)
@ -806,6 +831,11 @@ def delete(post: model.Post) -> None:
db.session.delete(post)
def ban(ban: model.PostBan) -> None:
assert ban
db.session.add(ban)
def merge_posts(
source_post: model.Post, target_post: model.Post, replace_content: bool
) -> None:

View file

@ -0,0 +1,29 @@
'''
create ban table
Revision ID: cc2956cb8ee7
Created at: 2023-05-12 02:04:22.592006
'''
import sqlalchemy as sa
from alembic import op
revision = 'cc2956cb8ee7'
down_revision = 'adcd63ff76a2'
branch_labels = None
depends_on = None
def upgrade():
op.create_table(
"post_ban",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("checksum", sa.Unicode(64), nullable=False),
sa.Column("time", sa.DateTime, nullable=False),
sa.PrimaryKeyConstraint("id")
)
op.create_unique_constraint("uq_ban_checksum", "post_ban", ["checksum"])
def downgrade():
op.drop_table("post_ban")

View file

@ -5,6 +5,7 @@ from szurubooru.model.pool import Pool, PoolName, PoolPost
from szurubooru.model.pool_category import PoolCategory
from szurubooru.model.post import (
Post,
PostBan,
PostFavorite,
PostFeature,
PostNote,

View file

@ -94,6 +94,14 @@ class PostFavorite(Base):
)
class PostBan(Base):
__tablename__ = "post_ban"
ban_id = sa.Column("id", sa.Integer, primary_key=True)
checksum = sa.Column("checksum", sa.Unicode(64), nullable=False)
time = sa.Column("time", sa.DateTime, nullable=False)
class PostNote(Base):
__tablename__ = "post_note"