diff --git a/client/css/post-main-view.styl b/client/css/post-main-view.styl index 48f3c158..a8973a90 100644 --- a/client/css/post-main-view.styl +++ b/client/css/post-main-view.styl @@ -40,11 +40,19 @@ width: 100% .post-container - margin-bottom: 2em + margin-bottom: 0.6em .post-content margin: 0 + .description-container + background: $top-navigation-color + padding: 0.6em + + #post-description + resize: vertical + min-height: 200px + @media (max-width: 800px) .post-view flex-wrap: wrap diff --git a/client/html/post_main.tpl b/client/html/post_main.tpl index 54c57333..c0c1e9df 100644 --- a/client/html/post_main.tpl +++ b/client/html/post_main.tpl @@ -54,6 +54,18 @@
+ <% if (ctx.editMode) { %> +

Description

+ <%= ctx.makeTextarea({ + id: 'post-description', + value: ctx.post.description, + }) %> + <% } else if (ctx.post.description != undefined) { %> +
+ <%= ctx.makeMarkdown(ctx.post.description) %> +
+ <% } %> + <% if (ctx.canListComments) { %>
<% } %> diff --git a/client/js/controllers/post_main_controller.js b/client/js/controllers/post_main_controller.js index 95cfdb52..35ae7d79 100644 --- a/client/js/controllers/post_main_controller.js +++ b/client/js/controllers/post_main_controller.js @@ -187,6 +187,9 @@ class PostMainController extends BasePostController { if (e.detail.source !== undefined) { post.source = e.detail.source; } + if (e.detail.description !== undefined) { + post.description = e.detail.description; + } post.save().then( () => { this._view.sidebarControl.showSuccess("Post saved."); diff --git a/client/js/controls/post_edit_sidebar_control.js b/client/js/controls/post_edit_sidebar_control.js index b8ad9dab..f0875f0f 100644 --- a/client/js/controls/post_edit_sidebar_control.js +++ b/client/js/controls/post_edit_sidebar_control.js @@ -435,6 +435,10 @@ class PostEditSidebarControl extends events.EventTarget { source: this._sourceInputNode ? this._sourceInputNode.value : undefined, + + description: this._descriptionTextareaNode + ? this._descriptionTextareaNode.value + : undefined, }, }) ); @@ -538,6 +542,10 @@ class PostEditSidebarControl extends events.EventTarget { return this._formNode.querySelector(".notes textarea"); } + get _descriptionTextareaNode() { + return document.querySelector("textarea#post-description"); + } + enableForm() { views.enableForm(this._formNode); } diff --git a/client/js/models/post.js b/client/js/models/post.js index 2fb3d34c..24b63ecb 100644 --- a/client/js/models/post.js +++ b/client/js/models/post.js @@ -102,6 +102,10 @@ class Post extends events.EventTarget { return this._flags; } + get description() { + return this._description; + } + get tags() { return this._tags; } @@ -154,6 +158,10 @@ class Post extends events.EventTarget { this._flags = value; } + set description(value) { + this._description = value; + } + set safety(value) { this._safety = value; } @@ -277,6 +285,9 @@ class Post extends events.EventTarget { if (this._source !== this._orig._source) { detail.source = this._source; } + if (this._description !== this._orig._description) { + detail.description = this._description; + } let apiPromise = this._id ? api.put(uri.formatApiLink("post", this.id), detail, files) @@ -484,6 +495,7 @@ class Post extends events.EventTarget { _fileSize: response.fileSize, _flags: [...(response.flags || [])], + _description: response.description, _relations: [...(response.relations || [])], _score: response.score, diff --git a/client/js/views/post_main_view.js b/client/js/views/post_main_view.js index c38a9337..b80b3d63 100644 --- a/client/js/views/post_main_view.js +++ b/client/js/views/post_main_view.js @@ -4,6 +4,7 @@ const iosCorrectedInnerHeight = require("ios-inner-height"); const router = require("../router.js"); const views = require("../util/views.js"); const uri = require("../util/uri.js"); +const misc = require("../util/misc.js"); const keyboard = require("../util/keyboard.js"); const Touch = require("../util/touch.js"); const PostContentControl = require("../controls/post_content_control.js"); diff --git a/server/szurubooru/api/post_api.py b/server/szurubooru/api/post_api.py index daba7f7e..1b48f7bd 100644 --- a/server/szurubooru/api/post_api.py +++ b/server/szurubooru/api/post_api.py @@ -165,6 +165,11 @@ def update_post(ctx: rest.Context, params: Dict[str, str]) -> rest.Response: if ctx.has_file("thumbnail"): auth.verify_privilege(ctx.user, "posts:edit:thumbnail") posts.update_post_thumbnail(post, ctx.get_file("thumbnail")) + if ctx.has_param("description"): + # auth.verify_privilege(ctx.user, "posts:edit:description") + posts.update_post_description( + post, ctx.get_param_as_string("description") + ) post.last_edit_time = datetime.utcnow() ctx.session.flush() snapshots.modify(post, ctx.user) diff --git a/server/szurubooru/func/posts.py b/server/szurubooru/func/posts.py index 0493681e..9ade4e8c 100644 --- a/server/szurubooru/func/posts.py +++ b/server/szurubooru/func/posts.py @@ -78,6 +78,10 @@ class InvalidPostFlagError(errors.ValidationError): pass +class InvalidPostDescriptionError(errors.ValidationError): + pass + + SAFETY_MAP = { model.Post.SAFETY_SAFE: "safe", model.Post.SAFETY_SKETCHY: "sketchy", @@ -182,6 +186,7 @@ class PostSerializer(serialization.BaseSerializer): "thumbnailUrl": self.serialize_thumbnail_url, "flags": self.serialize_flags, "tags": self.serialize_tags, + "description": self.serialize_description, "relations": self.serialize_relations, "user": self.serialize_user, "score": self.serialize_score, @@ -259,6 +264,9 @@ class PostSerializer(serialization.BaseSerializer): for tag in tags.sort_tags(self.post.tags) ] + def serialize_description(self) -> Any: + return self.post.description + def serialize_relations(self) -> Any: return sorted( { @@ -791,6 +799,13 @@ def update_post_flags(post: model.Post, flags: List[str]) -> None: post.flags = target_flags +def update_post_description(post: model.Post, description: str) -> None: + assert post + if util.value_exceeds_column_size(description, model.Post.description): + raise InvalidPostDescriptionError("Description is too long.") + post.description = description or None + + def feature_post(post: model.Post, user: Optional[model.User]) -> None: assert post post_feature = model.PostFeature() diff --git a/server/szurubooru/migrations/versions/58bba7e0c554_add_description_to_post.py b/server/szurubooru/migrations/versions/58bba7e0c554_add_description_to_post.py new file mode 100644 index 00000000..cc841408 --- /dev/null +++ b/server/szurubooru/migrations/versions/58bba7e0c554_add_description_to_post.py @@ -0,0 +1,24 @@ +""" +add_description_to_post + +Revision ID: 58bba7e0c554 +Created at: 2021-01-30 18:06:11.511449 +""" + +import sqlalchemy as sa +from alembic import op + +revision = "58bba7e0c554" +down_revision = "adcd63ff76a2" +branch_labels = None +depends_on = None + + +def upgrade(): + op.add_column( + "post", sa.Column("description", sa.UnicodeText(), nullable=True) + ) + + +def downgrade(): + op.drop_column("post", "description") diff --git a/server/szurubooru/model/post.py b/server/szurubooru/model/post.py index 49e748dc..3553a595 100644 --- a/server/szurubooru/model/post.py +++ b/server/szurubooru/model/post.py @@ -213,6 +213,7 @@ class Post(Base): safety = sa.Column("safety", sa.Unicode(32), nullable=False) source = sa.Column("source", sa.Unicode(2048)) flags_string = sa.Column("flags", sa.Unicode(32), default="") + description = sa.Column("description", sa.UnicodeText(), nullable=True) # content description type = sa.Column("type", sa.Unicode(32), nullable=False)