diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index 51ac3dea5..b9b7c297e 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -168,6 +168,7 @@ class PostsController < ApplicationController tag_string_diff source_diff parent_id old_parent_id source old_source + title old_title description old_description rating old_rating edit_reason diff --git a/app/controllers/uploads_controller.rb b/app/controllers/uploads_controller.rb index 8bc05bdc2..4dead05c1 100644 --- a/app/controllers/uploads_controller.rb +++ b/app/controllers/uploads_controller.rb @@ -72,7 +72,7 @@ class UploadsController < ApplicationController def upload_params permitted_params = %i[ - file direct_url source tag_string rating parent_id description description as_pending + file direct_url source tag_string rating parent_id title description description as_pending ] permitted_params << :locked_tags if CurrentUser.is_admin? diff --git a/app/decorators/posts_decorator.rb b/app/decorators/posts_decorator.rb index f2b16f810..fdc24ef15 100644 --- a/app/decorators/posts_decorator.rb +++ b/app/decorators/posts_decorator.rb @@ -50,13 +50,15 @@ class PostsDecorator < ApplicationDecorator status_flags << 'U' if post.is_pending? status_flags << 'F' if post.is_flagged? + title = t.tag.span(post.title, class: "post-score-title") + title = title + t.tag.br if post.title.present? post_score_icon = "#{'↑' if post.score > 0}#{'↓' if post.score < 0}#{'↕' if post.score == 0}" score = t.tag.span("#{post_score_icon}#{post.score}", class: "post-score-score #{score_class(post.score)}") favs = t.tag.span("♥#{post.fav_count}", class: "post-score-faves") comments = t.tag.span "C#{post.visible_comment_count(CurrentUser)}", class: 'post-score-comments' rating = t.tag.span(post.rating.upcase, class: "post-score-rating") status = t.tag.span(status_flags.join(''), class: 'post-score-extras') - t.tag.div score + favs + comments + rating + status, class: 'post-score', id: "post-score-#{post.id}" + t.tag.div title + score + favs + comments + rating + status, class: 'post-score', id: "post-score-#{post.id}" end def preview_html(t, options = {}) diff --git a/app/indexes/post_index.rb b/app/indexes/post_index.rb index 9da92151e..e9bf6fe51 100644 --- a/app/indexes/post_index.rb +++ b/app/indexes/post_index.rb @@ -61,6 +61,7 @@ module PostIndex rating: { type: "keyword" }, file_ext: { type: "keyword" }, source: { type: "keyword" }, + title: { type: "text" }, description: { type: "text" }, notes: { type: "text" }, del_reason: { type: "keyword" }, @@ -273,6 +274,7 @@ module PostIndex rating: rating, file_ext: file_ext, source: source_array, + title: title.present? ? title : nil, description: description.present? ? description : nil, rating_locked: is_rating_locked, diff --git a/app/indexes/post_version_index.rb b/app/indexes/post_version_index.rb index 701ea52e9..d9ae65f90 100644 --- a/app/indexes/post_version_index.rb +++ b/app/indexes/post_version_index.rb @@ -19,9 +19,11 @@ module PostVersionIndex parent_id: { type: "integer" }, rating: { type: "keyword" }, source: { type: "keyword" }, + title: { type: "text" }, description: { type: "text" }, reason: { type: "text" }, + title_changed: { type: "boolean" }, description_changed: { type: "boolean" }, parent_id_changed: { type: "boolean" }, source_changed: { type: "boolean" }, @@ -80,9 +82,11 @@ module PostVersionIndex parent_id: parent_id, rating: rating, source: source, + title: title, description: description, reason: reason, + title_changed: title_changed, description_changed: description_changed, parent_id_changed: parent_changed, source_changed: source_changed, diff --git a/app/javascript/src/javascripts/uploader/uploader.vue.erb b/app/javascript/src/javascripts/uploader/uploader.vue.erb index e87d12c6c..163fb0ab8 100644 --- a/app/javascript/src/javascripts/uploader/uploader.vue.erb +++ b/app/javascript/src/javascripts/uploader/uploader.vue.erb @@ -199,6 +199,14 @@ +
+
+ +
+
+ +
+
@@ -371,6 +379,7 @@ loadingRelated: false, parentID: '', + title: '', description: '', rating: '', error: '', @@ -426,6 +435,7 @@ }; fillField('parentID', 'parent'); + fillField('title', 'title'); fillField('description', 'description'); fillTags(); fillRating(); @@ -459,6 +469,7 @@ data.append('upload[tag_string]', this.tags); data.append('upload[rating]', this.rating); data.append('upload[source]', this.sources.join('\n')); + data.append('upload[title]', this.title); data.append('upload[description]', this.description); data.append('upload[parent_id]', this.parentID); if (this.allowLockedTags) diff --git a/app/javascript/src/styles/specific/post_versions.scss b/app/javascript/src/styles/specific/post_versions.scss index 5a64172a8..970795b76 100644 --- a/app/javascript/src/styles/specific/post_versions.scss +++ b/app/javascript/src/styles/specific/post_versions.scss @@ -106,14 +106,25 @@ div#c-post-versions { @include grid-border(righ); } + .pv-title-label { + grid-row: 1; + @include grid-col(7, 8); + } + + .pv-title { + grid-row: 2; + @include grid-col(7, 8); + @include grid-border(right); + } + .pv-description-label { grid-row: 1; - @include grid-col(7, 9); + @include grid-col(8, 9); } .pv-description { grid-row: 2; - @include grid-col(7, 9); + @include grid-col(8, 9); @include grid-border(right); } diff --git a/app/logical/elastic_post_query_builder.rb b/app/logical/elastic_post_query_builder.rb index 8a61c92de..17d909699 100644 --- a/app/logical/elastic_post_query_builder.rb +++ b/app/logical/elastic_post_query_builder.rb @@ -108,6 +108,7 @@ class ElasticPostQueryBuilder < ElasticQueryBuilder add_array_relation(:rating, :rating) add_array_relation(:filetype, :file_ext) add_array_relation(:delreason, :del_reason, action: :wildcard) + add_array_relation(:title, :title, action: :match_phrase_prefix) add_array_relation(:description, :description, action: :match_phrase_prefix) add_array_relation(:note, :notes, action: :match_phrase_prefix) add_array_relation(:sources, :source, any_none_key: :source, action: :wildcard) @@ -149,6 +150,10 @@ class ElasticPostQueryBuilder < ElasticQueryBuilder (q[:hassource] ? must : must_not).push({exists: {field: :source}}) end + if q.include?(:hastitle) + (q[:hastitle] ? must : must_not).push({exists: {field: :title}}) + end + if q.include?(:hasdescription) (q[:hasdescription] ? must : must_not).push({exists: {field: :description}}) end diff --git a/app/logical/tag_query.rb b/app/logical/tag_query.rb index be7a392a9..633bad8c0 100644 --- a/app/logical/tag_query.rb +++ b/app/logical/tag_query.rb @@ -8,11 +8,11 @@ class TagQuery ].freeze BOOLEAN_METATAGS = %w[ - hassource hasdescription isparent ischild inpool pending_replacements artverified + hassource hastitle hasdescription isparent ischild inpool pending_replacements artverified ].freeze NEGATABLE_METATAGS = %w[ - id filetype type rating description parent user user_id approver flagger deletedby delreason + id filetype type rating title description parent user user_id approver flagger deletedby delreason source status pool set fav favoritedby note locked upvote votedup downvote voteddown voted width height mpixels ratio filesize duration score favcount date age change tagcount commenter comm noter noteupdater @@ -290,6 +290,9 @@ class TagQuery when "filetype", "-filetype", "~filetype", "type", "-type", "~type" add_to_query(type, :filetype) { g2.downcase } + when "title", "-title", "~title" + add_to_query(type, :title) { g2 } + when "description", "-description", "~description" add_to_query(type, :description) { g2 } diff --git a/app/logical/upload_service.rb b/app/logical/upload_service.rb index a0423331e..c53c39a15 100644 --- a/app/logical/upload_service.rb +++ b/app/logical/upload_service.rb @@ -51,6 +51,7 @@ class UploadService p.tag_string = upload.tag_string p.locked_tags = upload.locked_tags p.is_rating_locked = upload.locked_rating if upload.locked_rating.present? + p.title = upload.title.strip p.description = upload.description.strip p.md5 = upload.md5 p.file_ext = upload.file_ext diff --git a/app/models/post.rb b/app/models/post.rb index aa2685c01..381a25c30 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -21,6 +21,7 @@ class Post < ApplicationRecord validates :md5, uniqueness: { :on => :create, message: ->(obj, data) {"duplicate: #{Post.find_by_md5(obj.md5).id}"} } validates :rating, inclusion: { in: %w(s q e), message: "rating must be s, q, or e" } validates :bg_color, format: { with: /\A[A-Fa-f0-9]{6}\z/ }, allow_nil: true + validates :title, length: { maximum: Danbooru.config.post_title_max_size }, if: :title_changed? validates :description, length: { maximum: Danbooru.config.post_descr_max_size }, if: :description_changed? validate :added_tags_are_valid, if: :should_process_tags? validate :removed_tags_are_valid, if: :should_process_tags? @@ -1174,6 +1175,7 @@ class Post < ApplicationRecord def backup_post_data_destroy(reason: "") post_data = { id: id, + title: title, description: description, md5: md5, tags: tag_string, @@ -1324,7 +1326,7 @@ class Post < ApplicationRecord end def saved_change_to_watched_attributes? - saved_change_to_rating? || saved_change_to_source? || saved_change_to_parent_id? || saved_change_to_tag_string? || saved_change_to_locked_tags? || saved_change_to_description? + saved_change_to_rating? || saved_change_to_source? || saved_change_to_parent_id? || saved_change_to_tag_string? || saved_change_to_locked_tags? || saved_change_to_title? || saved_change_to_description? end def create_new_version @@ -1342,6 +1344,7 @@ class Post < ApplicationRecord self.rating = target.rating self.source = target.source self.parent_id = target.parent_id + self.title = target.title self.description = target.description self.edit_reason = "Revert to version #{target.version}" end diff --git a/app/models/post_replacement.rb b/app/models/post_replacement.rb index aec76b0e5..565b6b582 100644 --- a/app/models/post_replacement.rb +++ b/app/models/post_replacement.rb @@ -247,6 +247,7 @@ class PostReplacement < ApplicationRecord rating: post.rating, source: "#{self.source}\n" + post.source, parent_id: post.id, + title: post.title, description: post.description, locked_tags: post.locked_tags, replacement_id: self.id diff --git a/app/models/post_version.rb b/app/models/post_version.rb index ea5e28e10..a494e30c3 100644 --- a/app/models/post_version.rb +++ b/app/models/post_version.rb @@ -94,10 +94,15 @@ class PostVersion < ApplicationRecord must << { match: { reason: params[:reason] } } end + if params[:title].present? + must << { match: { title: params[:title] } } + end + if params[:description].present? must << { match: { description: params[:description] } } end + must = boolean_match(:title_changed, params[:title_changed], must) must = boolean_match(:description_changed, params[:description_changed], must) must = boolean_match(:source_changed, params[:source_changed], must) @@ -172,6 +177,7 @@ class PostVersion < ApplicationRecord updater_ip_addr: CurrentUser.ip_addr, tags: post.tag_string, locked_tags: post.locked_tags, + title: post.title, description: post.description, reason: post.edit_reason }) @@ -203,6 +209,7 @@ class PostVersion < ApplicationRecord self.rating_changed = prev.nil? || rating != prev.try(:rating) self.parent_changed = prev.nil? || parent_id != prev.try(:parent_id) self.source_changed = prev.nil? || source != prev.try(:source) + self.title_changed = prev.nil? || title != prev.try(:title) self.description_changed = prev.nil? || description != prev.try(:description) end @@ -346,6 +353,10 @@ class PostVersion < ApplicationRecord def undo raise UndoError, "Version 1 is not undoable" unless undoable? + if title_changed + post.title = previous.title + end + if description_changed post.description = previous.description end diff --git a/app/presenters/post_presenter.rb b/app/presenters/post_presenter.rb index 534df2170..de9f25995 100644 --- a/app/presenters/post_presenter.rb +++ b/app/presenters/post_presenter.rb @@ -148,6 +148,7 @@ class PostPresenter < Presenter comment_count: post.visible_comment_count(CurrentUser), change_seq: post.change_seq, uploader_id: post.uploader_id, + title: post.title, description: post.description, flags: { pending: post.is_pending, diff --git a/app/serializers/post_serializer.rb b/app/serializers/post_serializer.rb index 26a3585d9..31a7e195c 100644 --- a/app/serializers/post_serializer.rb +++ b/app/serializers/post_serializer.rb @@ -138,6 +138,6 @@ class PostSerializer < ActiveModel::Serializer end attributes :id, :created_at, :updated_at, :file, :preview, :sample, :score, :tags, :locked_tags, :change_seq, :flags, - :rating, :fav_count, :sources, :pools, :relationships, :approver_id, :uploader_id, :description, + :rating, :fav_count, :sources, :pools, :relationships, :approver_id, :uploader_id, :title, :description, :comment_count, :is_favorited, :has_notes, :duration end diff --git a/app/views/post_versions/_listing.html.erb b/app/views/post_versions/_listing.html.erb index 4f7ec5ac9..d384d590c 100644 --- a/app/views/post_versions/_listing.html.erb +++ b/app/views/post_versions/_listing.html.erb @@ -15,6 +15,9 @@
Reason
+
+ Title +
Description
@@ -37,6 +40,21 @@
<%= post_version.reason %>
+
+ <% if post_version.title.present? %> +
+ <%= post_version.title_changed ? "Show Title" : "No change" %>
+
+

Title

+
X
+
+

<%= format_text(post_version.title) %>

+
+
+ <% elsif post_version.title_changed && post_version.version != 1%> + Cleared + <% end %> +
<% if post_version.description.present? %>
diff --git a/app/views/post_versions/_search.html.erb b/app/views/post_versions/_search.html.erb index 45182e2a7..f5c370fe9 100644 --- a/app/views/post_versions/_search.html.erb +++ b/app/views/post_versions/_search.html.erb @@ -2,6 +2,8 @@ <%= f.user :updater, label: "User" %> <%= f.input :post_id, label: "Post #" %> <%= f.input :reason, label: "Reason" %> + <%= f.input :title, label: "Title" %> + <%= f.input :title_changed, label: "Title Changed", collection: [%w[Yes true], %w[No false]], include_blank: true %> <%= f.input :description, label: "Description" %> <%= f.input :description_changed, label: "Description Changed", collection: [%w[Yes true], %w[No false]], include_blank: true %> <%= f.input :rating_changed, label: "Rating Changed To", collection: rating_collection + [%w[Any any]], include_blank: true %> diff --git a/app/views/posts/partials/show/_edit.html.erb b/app/views/posts/partials/show/_edit.html.erb index 3b56ff31c..167e55d04 100644 --- a/app/views/posts/partials/show/_edit.html.erb +++ b/app/views/posts/partials/show/_edit.html.erb @@ -40,6 +40,7 @@ <%= f.input :parent_id, as: :string, label: "Parent", input_html: { size: 5 } %> <%= f.input :source, as: :text, label: "Sources", input_html: { size: "60x5", spellcheck: false } %> + <%= f.input :title, as: :text, label: "Title", input_html: { size: "60x5", spellcheck: true } %>
<%= f.input :description, as: :dtext, limit: Danbooru.config.post_descr_max_size, allow_color: true %> diff --git a/app/views/posts/show.html.erb b/app/views/posts/show.html.erb index a18ae35e8..2a52fca9e 100644 --- a/app/views/posts/show.html.erb +++ b/app/views/posts/show.html.erb @@ -172,6 +172,17 @@ <% end %> + <% if @post.title.present? %> +
+
> + Title +
+ <%= format_text(@post.title, max_thumbs: 0) %> +
+
+
+ <% end %> + <% if @post.description.present? %>
> diff --git a/config/danbooru_default_config.rb b/config/danbooru_default_config.rb index 9de88c5e9..a56fe553a 100644 --- a/config/danbooru_default_config.rb +++ b/config/danbooru_default_config.rb @@ -317,6 +317,10 @@ module Danbooru 10_000 end + def post_title_max_size + 128 + end + def post_descr_max_size 50_000 end diff --git a/db/migrate/20241009023749_add_title_to_posts.rb b/db/migrate/20241009023749_add_title_to_posts.rb new file mode 100644 index 000000000..9f3a12b02 --- /dev/null +++ b/db/migrate/20241009023749_add_title_to_posts.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddTitleToPosts < ActiveRecord::Migration[7.1] + def change + add_column(:posts, :title, :text, null: false, default: '') + end +end diff --git a/db/migrate/20241009155301_add_title_to_post_versions.rb b/db/migrate/20241009155301_add_title_to_post_versions.rb new file mode 100644 index 000000000..9c4bb1502 --- /dev/null +++ b/db/migrate/20241009155301_add_title_to_post_versions.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddTitleToPostVersions < ActiveRecord::Migration[7.1] + def change + add_column :post_versions, :title, :text + end +end diff --git a/db/migrate/20241009155325_add_title_changed_to_post_versions.rb b/db/migrate/20241009155325_add_title_changed_to_post_versions.rb new file mode 100644 index 000000000..b8c53fbd4 --- /dev/null +++ b/db/migrate/20241009155325_add_title_changed_to_post_versions.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddTitleChangedToPostVersions < ActiveRecord::Migration[7.1] + def change + add_column(:post_versions, :title_changed, :boolean, null: false, default: false) + end +end diff --git a/db/migrate/20241010174014_add_title_to_uploads.rb b/db/migrate/20241010174014_add_title_to_uploads.rb new file mode 100644 index 000000000..04feedabc --- /dev/null +++ b/db/migrate/20241010174014_add_title_to_uploads.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddTitleToUploads < ActiveRecord::Migration[7.1] + def change + add_column(:uploads, :title, :text, null: false, default: '') + end +end diff --git a/db/migrate/20241029202902_update_posts_trigger_change_seq.rb b/db/migrate/20241029202902_update_posts_trigger_change_seq.rb new file mode 100644 index 000000000..854ce70f9 --- /dev/null +++ b/db/migrate/20241029202902_update_posts_trigger_change_seq.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +class UpdatePostsTriggerChangeSeq < ActiveRecord::Migration[7.1] + def up + execute <<-SQL + + CREATE OR REPLACE FUNCTION public.posts_trigger_change_seq() RETURNS trigger + LANGUAGE plpgsql + AS $$ + BEGIN + IF NEW.tag_string != OLD.tag_string OR NEW.parent_id != OLD.parent_id OR NEW.source != OLD.source OR NEW.approver_id != OLD.approver_id OR NEW.rating != OLD.rating OR NEW.title != OLD.title OR NEW.description != OLD.description OR NEW.md5 != OLD.md5 OR NEW.is_deleted != OLD.is_deleted OR NEW.is_pending != OLD.is_pending OR NEW.is_flagged != OLD.is_flagged OR NEW.is_rating_locked != OLD.is_rating_locked OR NEW.is_status_locked != OLD.is_status_locked OR NEW.is_note_locked != OLD.is_note_locked OR NEW.bit_flags != OLD.bit_flags OR NEW.has_active_children != OLD.has_active_children OR NEW.last_noted_at != OLD.last_noted_at + THEN + NEW.change_seq = nextval('public.posts_change_seq_seq'); + END IF; + RETURN NEW; + END; + $$; + + SQL + end + + def down + execute <<-SQL + + CREATE OR REPLACE FUNCTION public.posts_trigger_change_seq() RETURNS trigger + LANGUAGE plpgsql + AS $$ + BEGIN + IF NEW.tag_string != OLD.tag_string OR NEW.parent_id != OLD.parent_id OR NEW.source != OLD.source OR NEW.approver_id != OLD.approver_id OR NEW.rating != OLD.rating OR NEW.description != OLD.description OR NEW.md5 != OLD.md5 OR NEW.is_deleted != OLD.is_deleted OR NEW.is_pending != OLD.is_pending OR NEW.is_flagged != OLD.is_flagged OR NEW.is_rating_locked != OLD.is_rating_locked OR NEW.is_status_locked != OLD.is_status_locked OR NEW.is_note_locked != OLD.is_note_locked OR NEW.bit_flags != OLD.bit_flags OR NEW.has_active_children != OLD.has_active_children OR NEW.last_noted_at != OLD.last_noted_at + THEN + NEW.change_seq = nextval('public.posts_change_seq_seq'); + END IF; + RETURN NEW; + END; + $$; + + SQL + end +end diff --git a/db/structure.sql b/db/structure.sql index 9a990fad0..bb322f56f 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -31,7 +31,7 @@ CREATE FUNCTION public.posts_trigger_change_seq() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN - IF NEW.tag_string != OLD.tag_string OR NEW.parent_id != OLD.parent_id OR NEW.source != OLD.source OR NEW.approver_id != OLD.approver_id OR NEW.rating != OLD.rating OR NEW.description != OLD.description OR NEW.md5 != OLD.md5 OR NEW.is_deleted != OLD.is_deleted OR NEW.is_pending != OLD.is_pending OR NEW.is_flagged != OLD.is_flagged OR NEW.is_rating_locked != OLD.is_rating_locked OR NEW.is_status_locked != OLD.is_status_locked OR NEW.is_note_locked != OLD.is_note_locked OR NEW.bit_flags != OLD.bit_flags OR NEW.has_active_children != OLD.has_active_children OR NEW.last_noted_at != OLD.last_noted_at + IF NEW.tag_string != OLD.tag_string OR NEW.parent_id != OLD.parent_id OR NEW.source != OLD.source OR NEW.approver_id != OLD.approver_id OR NEW.rating != OLD.rating OR NEW.title != OLD.title OR NEW.description != OLD.description OR NEW.md5 != OLD.md5 OR NEW.is_deleted != OLD.is_deleted OR NEW.is_pending != OLD.is_pending OR NEW.is_flagged != OLD.is_flagged OR NEW.is_rating_locked != OLD.is_rating_locked OR NEW.is_status_locked != OLD.is_status_locked OR NEW.is_note_locked != OLD.is_note_locked OR NEW.bit_flags != OLD.bit_flags OR NEW.has_active_children != OLD.has_active_children OR NEW.last_noted_at != OLD.last_noted_at THEN NEW.change_seq = nextval('public.posts_change_seq_seq'); END IF; @@ -1570,6 +1570,8 @@ CREATE TABLE public.post_versions ( parent_changed boolean DEFAULT false NOT NULL, source text, source_changed boolean DEFAULT false NOT NULL, + title text, + title_changed boolean DEFAULT false NOT NULL, description text, description_changed boolean DEFAULT false NOT NULL, version integer DEFAULT 1 NOT NULL, @@ -1678,6 +1680,7 @@ CREATE TABLE public.posts ( locked_tags text, tag_count_species integer DEFAULT 0 NOT NULL, tag_count_invalid integer DEFAULT 0 NOT NULL, + title text DEFAULT ''::text NOT NULL, description text DEFAULT ''::text NOT NULL, comment_count integer DEFAULT 0 NOT NULL, change_seq bigint NOT NULL, @@ -2136,6 +2139,7 @@ CREATE TABLE public.uploads ( file_size integer, image_width integer, image_height integer, + title text DEFAULT ''::text NOT NULL, description text DEFAULT ''::text NOT NULL ); @@ -4677,6 +4681,11 @@ ALTER TABLE ONLY public.avoid_postings SET search_path TO "$user", public; INSERT INTO "schema_migrations" (version) VALUES +('20241029202902'), +('20241010174014'), +('20241009155325'), +('20241009155301'), +('20241009023749'), ('20241001045543'), ('20240726170041'), ('20240709134926'),