@@ -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" %>
+
+ <% 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'),