Merge branch 'master' into master2

This commit is contained in:
edshot99 2024-12-08 05:27:58 -06:00
commit c5edb7fa41
34 changed files with 443 additions and 411 deletions

View File

@ -17,7 +17,7 @@ class UserFeedbacksController < ApplicationController
def show
@user_feedback = UserFeedback.find(params[:id])
raise(User::PrivilegeError) if !CurrentUser.user.is_moderator? && @user_feedback.is_deleted?
raise(User::PrivilegeError) if !CurrentUser.user.is_staff? && @user_feedback.is_deleted?
respond_with(@user_feedback)
end
@ -88,12 +88,14 @@ class UserFeedbacksController < ApplicationController
def user_feedback_params(context)
permitted_params = %i[body category]
permitted_params += %i[user_id user_name] if context == :create
permitted_params += [:send_update_dmail] if context == :update
permitted_params += %i[send_update_dmail] if context == :update
params.fetch(:user_feedback, {}).permit(permitted_params)
end
def search_params
permit_search_params(%i[deleted body_matches user_id user_name creator_id creator_name category])
permitted_params = %i[body_matches user_id user_name creator_id creator_name category]
permitted_params += %i[deleted] if CurrentUser.is_staff?
permit_search_params(permitted_params)
end
end

View File

@ -9,15 +9,15 @@ class PostsDecorator < ApplicationDecorator
def preview_class(options)
post = object
klass = ["post-preview"]
klass << "post-status-pending" if post.is_pending?
klass << "post-status-flagged" if post.is_flagged?
klass << "post-status-deleted" if post.is_deleted?
klass << "post-status-has-parent" if post.parent_id
klass << "post-status-has-children" if post.has_visible_children?
klass << "post-rating-safe" if post.rating == "s"
klass << "post-rating-questionable" if post.rating == "q"
klass << "post-rating-explicit" if post.rating == "e"
klass = ["thumbnail"]
klass << "pending" if post.is_pending?
klass << "flagged" if post.is_flagged?
klass << "deleted" if post.is_deleted?
klass << "has-parent" if post.parent_id
klass << "has-children" if post.has_visible_children?
klass << "rating-safe" if post.rating == "s"
klass << "rating-questionable" if post.rating == "q"
klass << "rating-explicit" if post.rating == "e"
klass << "blacklistable" unless options[:no_blacklist]
klass
end
@ -42,23 +42,23 @@ class PostsDecorator < ApplicationDecorator
score > 0 ? 'score-positive' : 'score-negative'
end
def stats_section(t)
def stats_section(template)
post = object
status_flags = []
status_flags << 'P' if post.parent_id
status_flags << 'C' if post.has_children?
status_flags << 'U' if post.is_pending?
status_flags << 'F' if post.is_flagged?
status_flags << "P" if post.parent_id
status_flags << "C" if post.has_children?
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 += t.tag.br if post.title.present?
title = template.tag.span(post.title, class: "title")
title += template.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 title + score + favs + comments + rating + status, class: 'post-score', id: "post-score-#{post.id}"
score = template.tag.span("#{post_score_icon}#{post.score}", class: "score #{score_class(post.score)}")
favs = template.tag.span("#{post.fav_count}", class: "favorites")
comments = template.tag.span "C#{post.visible_comment_count(CurrentUser)}", class: "comments"
rating = template.tag.span(post.rating.upcase, class: "rating")
# status = template.tag.span(status_flags.join, class: "extras")
template.tag.div score + favs + comments + rating, class: "desc"
end
def preview_html(t, options = {})
@ -117,16 +117,10 @@ class PostsDecorator < ApplicationDecorator
post.preview_file_url
end
alt_text = post.tag_string
alt_text = "post ##{post.id}"
has_cropped = post.has_cropped?
pool = options[:pool]
similarity = options[:similarity]&.round
size = options[:size] ? post.file_size : nil
img_contents = t.link_to t.polymorphic_path(link_target, link_params) do
t.tag.picture do
t.concat t.tag.source media: "(max-width: 800px)", srcset: cropped_url
@ -134,13 +128,7 @@ class PostsDecorator < ApplicationDecorator
t.concat t.tag.img class: "has-cropped-#{has_cropped}", src: preview_url, title: tooltip, alt: alt_text
end
end
desc_contents = if options[:stats] || pool || similarity || size
t.tag.div class: "desc" do
stats_section(t) if options[:stats]
end
else
"".html_safe
end
desc_contents = options[:stats] ? stats_section(t) : "".html_safe
t.tag.article(**article_attrs) do
img_contents + desc_contents
end

View File

@ -117,7 +117,7 @@ module ApplicationHelper
end
def body_attributes(user = CurrentUser.user)
attributes = [:id, :name, :level, :level_string, :can_approve_posts?, :can_upload_free?, :per_page]
attributes = %i[id name level level_string can_approve_posts? can_upload_free? per_page]
attributes += User::Roles.map { |role| :"is_#{role}?" }
controller_param = params[:controller].parameterize.dasherize
@ -125,12 +125,13 @@ module ApplicationHelper
{
lang: "en",
class: "c-#{controller_param} a-#{action_param} #{"resp" unless disable_mobile_mode?}",
class: "c-#{controller_param} a-#{action_param} #{'resp' unless disable_mobile_mode?}",
data: {
controller: controller_param,
action: action_param,
**data_attributes_for(user, "user", attributes)
}
**data_attributes_for(user, "user", attributes),
disable_cropped_thumbnails: Danbooru.config.enable_image_cropping? && CurrentUser.user.disable_cropped_thumbnails?,
},
}
end

View File

@ -87,18 +87,18 @@ module PostsHelper
def post_stats_section(post)
status_flags = []
status_flags << 'P' if post.parent_id
status_flags << 'C' if post.has_active_children?
status_flags << 'U' if post.is_pending?
status_flags << 'F' if post.is_flagged?
status_flags << "P" if post.parent_id
status_flags << "C" if post.has_active_children?
status_flags << "U" if post.is_pending?
status_flags << "F" if post.is_flagged?
post_score_icon = "#{'↑' if post.score > 0}#{'↓' if post.score < 0}#{'↕' if post.score == 0}"
score = tag.span("#{post_score_icon}#{post.score}", class: "post-score-score #{score_class(post.score)}")
favs = tag.span("#{post.fav_count}", class: "post-score-faves")
comments = tag.span "C#{post.visible_comment_count(CurrentUser)}", class: 'post-score-comments'
rating = tag.span(post.rating.upcase, class: "post-score-rating")
status = tag.span(status_flags.join(''), class: 'post-score-extras')
tag.div score + favs + comments + rating + status, class: 'post-score', id: "post-score-#{post.id}"
score = tag.span("#{post_score_icon}#{post.score}", class: "score #{score_class(post.score)}")
favs = tag.span("#{post.fav_count}", class: "favorites")
comments = tag.span "C#{post.visible_comment_count(CurrentUser)}", class: "comments"
rating = tag.span(post.rating.upcase, class: "rating")
# status = tag.span(status_flags.join, class: "extras")
tag.div score + favs + comments + rating, class: "desc"
end
def user_record_meta(user)

View File

@ -14,10 +14,10 @@ module WikiPagesHelper
end
def wiki_page_post_previews(wiki_page)
tag.div(id: "wiki-page-posts") do
tag.section(id: "wiki-page-posts", class: "posts-container") do
if Post.fast_count(wiki_page.title) > 0
view_all_link = link_to("view all", posts_path(tags: wiki_page.title))
header = tag.h2("Posts (#{view_all_link})".html_safe)
header = tag.h2("Posts (#{view_all_link})".html_safe, class: "posts-container-header")
header + wiki_page.post_set.presenter.post_previews_html(self)
end
end

View File

@ -55,7 +55,7 @@ PostModeMenu.initialize_selector = function () {
};
PostModeMenu.initialize_preview_link = function () {
$(".post-preview").on("click.danbooru", PostModeMenu.click);
$(".thumbnail").on("click.danbooru", PostModeMenu.click);
};
PostModeMenu.initialize_edit_form = function () {
@ -107,7 +107,7 @@ PostModeMenu.initialize_tag_script_field = function () {
PostModeMenu.tag_script_apply_all = function (event) {
event.preventDefault();
$("article.post-preview").trigger("click");
$("article.thumbnail").trigger("click");
};
PostModeMenu.update_sets_menu = function () {

View File

@ -33,6 +33,7 @@
@import "common/spoiler.scss";
@import "common/tables.scss";
@import "common/tags.scss";
@import "common/thumbnails.scss";
@import "common/user_styles.scss";
@import "common/voting.scss";

View File

@ -25,18 +25,22 @@ $base_font_family: Verdana, sans-serif;
$box-shadow-size: 2px 2px 5px;
@mixin preview-type-badge($text) {
background-color: themed("color-section");
border: 1px solid themed("color-text-white");
color: themed("color-text");
z-index: 5;
position: absolute;
z-index: 5;
left: 0;
top: 0.5rem;
text-align: center;
border-radius: $border-radius-full;
padding: 3px 5px;
padding: 2px 4px;
background-color: themed("color-section");
color: themed("color-text");
font-size: 60%;
font-weight: bold;
text-align: center;
border-radius: 0 $border-radius-half $border-radius-half 0;
border: 1px solid themed("color-text-muted");
border-left: 0;
content: $text;
}

View File

@ -191,14 +191,13 @@
#c-posts #a-index,
#c-popular,
#c-favorites {
.post-preview.blacklisted {
.thumbnail.blacklisted {
display: none !important;
}
}
#image-container.blacklisted,
.post-thumbnail.blacklisted,
.post-preview.blacklisted {
.post-thumbnail.blacklisted {
img,
video {
height: 0px !important;
@ -216,3 +215,14 @@
display: none;
}
}
article.thumbnail.blacklisted {
width: 100%;
& > a {
height: 100%;
background-image: url("blacklisted-preview.png");
background-size: cover;
background-position: center;
}
img { display: none; }
}

View File

@ -0,0 +1,198 @@
// Thumbnail dimensions
$thumb-image-size: 150px;
$thumb-border-width: 2px;
$thumb-mobile-cols: 3;
$thumb-gap: 8px;
// Switch to desktop view at this width
// Last value is the #page padding
$thumb-desktop-breakpoint: ($thumb-image-size * $thumb-mobile-cols) + (2 * $thumb-gap) + 8px;
// Posts container
// May or may not exist on some pages
section.posts-container {
display: grid;
grid-template-columns: repeat($thumb-mobile-cols, 1fr);
gap: 0.5rem;
@include window-larger-than($thumb-desktop-breakpoint) {
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}
h2.posts-container-header {
grid-column: -1 / 1;
}
}
// Thumbnail proper
article.thumbnail {
display: inline-flex;
flex-flow: column;
max-width: $thumb-image-size;
justify-self: center; // Center the thumbnail in the grid cell
position: relative; // Position badges correctly
overflow: hidden;
@include window-larger-than($thumb-desktop-breakpoint) {
width: min-content; // Force .desc to be the same width as the image
}
// Badges
&[data-tags~=animated] > a:before { @include preview-type-badge("ANIM"); }
&[data-file-ext=webm] > a:before { @include preview-type-badge("WEBM"); }
a { text-align: center; } // Fixes an issue with blacklisted pool thumbnails
img {
box-sizing: border-box; // Prevent the border from resizing the entire element
max-width: $thumb-image-size;
max-height: $thumb-image-size;
border-radius: $border-radius-half $border-radius-half 0 0;
pointer-events: none; // Should probably just fix the click events, huh
// Mobile view
width: 100%;
@include window-larger-than($thumb-desktop-breakpoint) {
width: unset;
}
}
.desc {
display: flex;
gap: 0.25em;
justify-content: center;
background-color: themed("color-section-lighten-5");
border-radius: 0 0 $border-radius-half $border-radius-half;
.rating { font-weight: 700; }
& > a { text-align: center; } // Pool names
}
// Color the rating letters
@each $name, $color in ("e": "red", "q": "yellow", "s": "green") {
&[data-rating=#{$name}] .desc .rating { color: palette("text-#{$color}"); }
}
// Post matches blacklist
&.filter-matches .desc {
background-color: var(--palette-background-red);
}
}
// Fix for when posts have no cropped versions
// Should be made irrelevant later, but this will do for now
@include window-smaller-than(800px) {
body[data-disable-cropped-thumbnails="false"] article.thumbnail {
width: 100%;
a { height: 100%; }
img {
height: 100%;
object-fit: cover;
@include window-larger-than($thumb-desktop-breakpoint) {
&.has-cropped-false { width: 150px; }
}
}
}
}
// I have absolutely no idea what this
// does or when it might be triggered.
article.thumbnail.current-post {
background-color: $post-preview-highlight-background;
}
// Nightmarish mess ahead
// Set thumbnail borders based on status
article.thumbnail {
&.has-children img {
border: 2px solid transparent;
border-color: $preview-has-children-color;
}
&.has-parent img {
border: 2px solid transparent;
border-color: $preview-has-parent-color;
}
&.has-children.has-parent img {
border: 2px solid transparent;
border-color: $preview-has-children-color $preview-has-parent-color $preview-has-parent-color $preview-has-children-color;
}
&.deleted img {
border: 2px solid transparent;
border-color: $preview-deleted-color;
}
&.has-children.deleted img {
border: 2px solid transparent;
border-color: $preview-has-children-color $preview-deleted-color $preview-deleted-color $preview-has-children-color;
}
&.has-parent.deleted img {
border: 2px solid transparent;
border-color: $preview-has-parent-color $preview-deleted-color $preview-deleted-color $preview-has-parent-color;
}
&.has-children.has-parent.deleted img {
border: 2px solid transparent;
border-color: $preview-has-children-color $preview-deleted-color $preview-deleted-color $preview-has-parent-color;
}
&.pending img,
&.flagged img {
border: 2px solid transparent;
border-color: $preview-pending-color;
}
&.has-children.pending img,
&.has-children.flagged img {
border: 2px solid transparent;
border-color: $preview-has-children-color $preview-pending-color $preview-pending-color $preview-has-children-color;
}
&.has-parent.pending img,
&.has-parent.flagged img {
border: 2px solid transparent;
border-color: $preview-has-parent-color $preview-pending-color $preview-pending-color $preview-has-parent-color;
}
&.has-children.has-parent.pending img,
&.has-children.has-parent.flagged img {
border: 2px solid transparent;
border-color: $preview-has-children-color $preview-pending-color $preview-pending-color $preview-has-parent-color;
}
}
// Flagged posts have red borders for approvers.
body[data-user-can-approve-posts="true"] article.thumbnail {
&.flagged img {
border: 2px solid transparent;
border-color: $preview-flagged-color;
}
&.has-children.flagged img {
border: 2px solid transparent;
border-color: $preview-has-children-color $preview-flagged-color $preview-flagged-color $preview-has-children-color;
}
&.has-parent.flagged img {
border: 2px solid transparent;
border-color: $preview-has-parent-color $preview-flagged-color $preview-flagged-color $preview-has-parent-color;
}
&.has-children.has-parent.flagged img {
border: 2px solid transparent;
border-color: $preview-has-children-color $preview-flagged-color $preview-flagged-color $preview-has-parent-color;
}
}

View File

@ -197,53 +197,6 @@
padding: 0;
}
#posts #posts-container {
width: 100%;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-evenly;
}
article.post-preview {
float: none;
margin: 0 0 10px 0;
text-align: center;
vertical-align: middle;
display: inline-block;
width: 31vw;
a {
margin: 0 auto;
}
img {
max-height: 150px;
max-width: 31vw;
}
.desc {
font-size: 100%;
}
}
.user-disable-cropped-false {
article.post-preview {
width: 31vw;
img {
height: auto;
max-height: none;
max-width: none;
width: 31vw;
&.has-cropped-false {
object-fit: cover;
}
}
}
}
div#options {
margin-top: 10px;
font-size: 24pt;

View File

@ -1,6 +1,6 @@
div#c-iqdb-queries {
div#a-show {
article.post-preview {
article.thumbnail {
width: 180px;
border: $iqdb-post-border solid 1px;

View File

@ -25,10 +25,6 @@ $modes: (
}
}
#page:not([data-mode-menu="view"]) article.post-preview img {
pointer-events: none;
}
#tag-script-ui {
display: flex;
margin-top: 0.5em;

View File

@ -3,167 +3,15 @@
width: 15em; // Match width to that of the
}
article.post-preview {
box-sizing: border-box;
height: auto;
width: fit-content;
margin: 0 10px 10px 0;
overflow: hidden;
text-align: center;
display: inline-block;
position: relative;
vertical-align: text-top;
a {
display: inline-block;
}
.desc {
background-color: themed("color-section-lighten-5");
font-size: 80%;
margin-bottom: 0;
}
&.filter-matches .desc {
background-color: var(--palette-background-red);
}
.post-score>span {
font-size: 0.8rem;
margin-left: 0.5em;
}
&.post-rating-explicit .post-score-rating {
color: palette("text-red");
}
&.post-rating-safe .post-score-rating {
color: palette("text-green");
}
&.post-rating-questionable .post-score-rating {
color: palette("text-yellow");
}
img {
box-sizing: border-box;
margin: auto;
max-height: 10rem; // Roughly 160px with 16pt font size
max-width: 10rem;
border-radius: $border-radius-half $border-radius-half 0 0;
}
&[data-tags~=animated] > a:before {
@include preview-type-badge('ANIM');
}
&[data-file-ext=webm] > a:before {
@include preview-type-badge('WEBM');
}
&[data-file-ext=mp4] > a:before {
@include preview-type-badge('MP4');
}
}
#edit-dialog textarea {
margin-bottom: 0.25em;
}
.post-preview {
&.post-status-has-children img {
border: 2px solid transparent;
border-color: $preview-has-children-color;
}
&.post-status-has-parent img {
border: 2px solid transparent;
border-color: $preview-has-parent-color;
}
&.post-status-has-children.post-status-has-parent img {
border: 2px solid transparent;
border-color: $preview-has-children-color $preview-has-parent-color $preview-has-parent-color $preview-has-children-color;
}
&.post-status-deleted img {
border: 2px solid transparent;
border-color: $preview-deleted-color;
}
&.post-status-has-children.post-status-deleted img {
border: 2px solid transparent;
border-color: $preview-has-children-color $preview-deleted-color $preview-deleted-color $preview-has-children-color;
}
&.post-status-has-parent.post-status-deleted img {
border: 2px solid transparent;
border-color: $preview-has-parent-color $preview-deleted-color $preview-deleted-color $preview-has-parent-color;
}
&.post-status-has-children.post-status-has-parent.post-status-deleted img {
border: 2px solid transparent;
border-color: $preview-has-children-color $preview-deleted-color $preview-deleted-color $preview-has-parent-color;
}
&.post-status-pending img,
&.post-status-flagged img {
border: 2px solid transparent;
border-color: $preview-pending-color;
}
&.post-status-has-children.post-status-pending img,
&.post-status-has-children.post-status-flagged img {
border: 2px solid transparent;
border-color: $preview-has-children-color $preview-pending-color $preview-pending-color $preview-has-children-color;
}
&.post-status-has-parent.post-status-pending img,
&.post-status-has-parent.post-status-flagged img {
border: 2px solid transparent;
border-color: $preview-has-parent-color $preview-pending-color $preview-pending-color $preview-has-parent-color;
}
&.post-status-has-children.post-status-has-parent.post-status-pending img,
&.post-status-has-children.post-status-has-parent.post-status-flagged img {
border: 2px solid transparent;
border-color: $preview-has-children-color $preview-pending-color $preview-pending-color $preview-has-parent-color;
}
}
/* Flagged posts have red borders for approvers. */
body[data-user-can-approve-posts="true"] .post-preview {
&.post-status-flagged img {
border: 2px solid transparent;
border-color: $preview-flagged-color;
}
&.post-status-has-children.post-status-flagged img {
border: 2px solid transparent;
border-color: $preview-has-children-color $preview-flagged-color $preview-flagged-color $preview-has-children-color;
}
&.post-status-has-parent.post-status-flagged img {
border: 2px solid transparent;
border-color: $preview-has-parent-color $preview-flagged-color $preview-flagged-color $preview-has-parent-color;
}
&.post-status-has-children.post-status-has-parent.post-status-flagged img {
border: 2px solid transparent;
border-color: $preview-has-children-color $preview-flagged-color $preview-flagged-color $preview-has-parent-color;
}
}
.post-preview.current-post {
background-color: $post-preview-highlight-background;
}
#has-parent-relationship-preview, #has-children-relationship-preview {
flex-direction: row;
flex-wrap: wrap;
article.post-preview {
article.thumbnail {
border: none;
margin: 0;
padding: 5px 5px 10px;

View File

@ -175,12 +175,6 @@ div#c-users {
border-top-left-radius: 0;
justify-content: flex-start;
}
article.post-preview {
flex: 0 0 154px;
margin: 0;
img { max-width: unset !important; }
}
}
}

View File

@ -1,95 +1,97 @@
# frozen_string_literal: true
class ModAction < ApplicationRecord
belongs_to :creator, :class_name => "User"
before_validation :initialize_creator, :on => :create
belongs_to :creator, class_name: "User"
before_validation :initialize_creator, on: :create
validates :creator_id, presence: true
KnownActions = [
:artist_page_rename,
:artist_page_lock,
:artist_page_unlock,
:artist_user_linked,
:artist_user_unlinked,
:avoid_posting_create,
:avoid_posting_update,
:avoid_posting_delete,
:avoid_posting_undelete,
:avoid_posting_destroy,
:blip_delete,
:blip_hide,
:blip_unhide,
:blip_update,
:comment_delete,
:comment_hide,
:comment_unhide,
:comment_update,
:forum_category_create,
:forum_category_delete,
:forum_category_update,
:forum_post_delete,
:forum_post_hide,
:forum_post_unhide,
:forum_post_update,
:forum_topic_delete,
:forum_topic_hide,
:forum_topic_unhide,
:forum_topic_lock,
:forum_topic_unlock,
:forum_topic_stick,
:forum_topic_unstick,
:forum_topic_update,
:help_create,
:help_delete,
:help_update,
:ip_ban_create,
:ip_ban_delete,
:mascot_create,
:mascot_update,
:mascot_delete,
:pool_delete,
:report_reason_create,
:report_reason_delete,
:report_reason_update,
:set_update,
:set_delete,
:set_change_visibility,
:tag_alias_create,
:tag_alias_update,
:tag_implication_create,
:tag_implication_update,
:ticket_claim,
:ticket_unclaim,
:ticket_update,
:upload_whitelist_create,
:upload_whitelist_update,
:upload_whitelist_delete,
:user_blacklist_changed,
:user_text_change,
:user_upload_limit_change,
:user_flags_change,
:user_level_change,
:user_name_change,
:user_delete,
:user_ban,
:user_ban_update,
:user_unban,
:user_feedback_create,
:user_feedback_update,
:user_feedback_delete,
:user_feedback_undelete,
:user_feedback_destroy,
:wiki_page_rename,
:wiki_page_delete,
:wiki_page_lock,
:wiki_page_unlock,
KnownActions = {
artist_page_rename: %i[old_name new_name],
artist_page_lock: %i[artist_page],
artist_page_unlock: %i[artist_page],
artist_user_linked: %i[artist_page user_id],
artist_user_unlinked: %i[artist_page user_id],
avoid_posting_create: %i[id artist_name],
avoid_posting_update: %i[id artist_name details old_details staff_notes old_staff_notes],
avoid_posting_delete: %i[id artist_name],
avoid_posting_undelete: %i[id artist_name],
avoid_posting_destroy: %i[id artist_name],
blip_delete: %i[blip_id user_id],
blip_hide: %i[blip_id user_id],
blip_unhide: %i[blip_id user_id],
blip_update: %i[blip_id user_id],
comment_delete: %i[comment_id user_id],
comment_hide: %i[comment_id user_id],
comment_unhide: %i[comment_id user_id],
comment_update: %i[comment_id user_id],
forum_category_create: %i[forum_category_id],
forum_category_delete: %i[forum_category_id],
forum_category_update: %i[forum_category_id],
forum_post_delete: %i[forum_post_id forum_topic_id user_id],
forum_post_hide: %i[forum_post_id forum_topic_id user_id],
forum_post_unhide: %i[forum_post_id forum_topic_id user_id],
forum_post_update: %i[forum_post_id forum_topic_id user_id],
forum_topic_delete: %i[forum_topic_id forum_topic_title user_id],
forum_topic_hide: %i[forum_topic_id forum_topic_title user_id],
forum_topic_unhide: %i[forum_topic_id forum_topic_title user_id],
forum_topic_lock: %i[forum_topic_id forum_topic_title user_id],
forum_topic_unlock: %i[forum_topic_id forum_topic_title user_id],
forum_topic_stick: %i[forum_topic_id forum_topic_title user_id],
forum_topic_unstick: %i[forum_topic_id forum_topic_title user_id],
forum_topic_update: [], # FIXME: this key is never used anywhere
help_create: %i[name wiki_page],
help_delete: %i[name wiki_page],
help_update: %i[name wiki_page],
ip_ban_create: %i[ip_addr reason],
ip_ban_delete: %i[ip_addr reason],
mascot_create: %i[id],
mascot_update: %i[id],
mascot_delete: %i[id],
pool_delete: %i[pool_id pool_name user_id],
report_reason_create: %i[reason],
report_reason_delete: %i[reason user_id],
report_reason_update: %i[reason reason_was description description_was],
set_update: %i[set_id user_id],
set_delete: %i[set_id user_id],
set_change_visibility: %i[set_id user_id is_public],
tag_alias_create: %i[alias_id alias_desc],
tag_alias_update: %i[alias_id alias_desc change_desc],
tag_implication_create: %i[implication_id implication_desc],
tag_implication_update: %i[implication_id implication_desc change_desc],
ticket_claim: %i[ticket_id],
ticket_unclaim: %i[ticket_id],
ticket_update: %i[ticket_id],
upload_whitelist_create: %i[pattern note hidden],
upload_whitelist_update: %i[pattern note old_pattern hidden],
upload_whitelist_delete: %i[pattern note hidden],
user_blacklist_changed: %i[user_id],
user_text_change: %i[user_id],
user_upload_limit_change: %i[user_id old_upload_limit new_upload_limit],
user_flags_change: %i[user_id added removed],
user_level_change: %i[user_id level level_was],
user_name_change: %i[user_id],
user_delete: %i[user_id],
user_ban: %i[user_id duration reason],
user_ban_update: %i[user_id ban_id expires_at expires_at_was reason reason_was],
user_unban: %i[user_id],
user_feedback_create: %i[user_id reason type record_id],
user_feedback_update: %i[user_id reason reason_was type type_was record_id],
user_feedback_delete: %i[user_id reason reason_was type type_was record_id],
user_feedback_undelete: %i[user_id reason reason_was type type_was record_id],
user_feedback_destroy: %i[user_id reason type record_id],
wiki_page_rename: %i[new_title old_title],
wiki_page_delete: %i[wiki_page wiki_page_id],
wiki_page_lock: %i[wiki_page],
wiki_page_unlock: %i[wiki_page],
:mass_update,
:nuke_tag,
mass_update: %i[antecedent consequent],
nuke_tag: %i[tag_name],
:takedown_delete,
:takedown_process,
]
takedown_delete: %i[takedown_id],
takedown_process: %i[takedown_id],
}.freeze
KnownActionKeys = KnownActions.keys.freeze
def self.search(params)
q = super
@ -97,16 +99,45 @@ class ModAction < ApplicationRecord
q = q.where_user(:creator_id, :creator, params)
if params[:action].present?
q = q.where('action = ?', params[:action])
q = q.where("action = ?", params[:action])
end
q.apply_basic_order(params)
end
def values
original_values = self[:values]
if CurrentUser.is_admin?
original_values
else
valid_keys = KnownActions[action.to_sym]&.map(&:to_s) || []
sanitized_values = original_values.slice(*valid_keys)
if %i[ip_ban_create ip_ban_delete].include?(action.to_sym)
sanitized_values = sanitized_values.slice([])
end
if %i[upload_whitelist_create upload_whitelist_update upload_whitelist_delete].include?(action.to_sym)
if sanitized_values["hidden"]
sanitized_values = sanitized_values.slice("hidden")
else
sanitized_values = sanitized_values.slice("hidden", "note")
end
end
sanitized_values
end
end
def hidden_attributes
super + [:values]
end
def method_attributes
super + [:values]
end
def self.log(cat = :other, details = {})
create(action: cat.to_s, values: details)
end

View File

@ -66,7 +66,7 @@ class UserFeedback < ApplicationRecord
end
def visible(user)
if user.is_moderator?
if user.is_staff?
all
else
active

View File

@ -22,8 +22,8 @@ class PostPresenter < Presenter
locals = {}
locals[:article_attrs] = {
"id" => "post_#{post.id}",
"class" => preview_class(post, **options).join(" ")
"id" => "post_#{post.id}",
"class" => preview_class(post, **options).join(" "),
}.merge(data_attributes(post))
locals[:link_target] = options[:link_target] || post
@ -54,7 +54,7 @@ class PostPresenter < Presenter
post.preview_file_url
end
locals[:alt_text] = post.tag_string
locals[:alt_text] = "post ##{post.id}"
locals[:has_cropped] = post.has_cropped?
@ -91,15 +91,15 @@ class PostPresenter < Presenter
end
def self.preview_class(post, pool: nil, size: nil, similarity: nil, **options)
klass = ["post-preview"]
klass << "post-status-pending" if post.is_pending?
klass << "post-status-flagged" if post.is_flagged?
klass << "post-status-deleted" if post.is_deleted?
klass << "post-status-has-parent" if post.parent_id
klass << "post-status-has-children" if post.has_visible_children?
klass << "post-rating-safe" if post.rating == "s"
klass << "post-rating-questionable" if post.rating == "q"
klass << "post-rating-explicit" if post.rating == "e"
klass = ["thumbnail"]
klass << "pending" if post.is_pending?
klass << "flagged" if post.is_flagged?
klass << "deleted" if post.is_deleted?
klass << "has-parent" if post.parent_id
klass << "has-children" if post.has_visible_children?
klass << "rating-safe" if post.rating == "s"
klass << "rating-questionable" if post.rating == "q"
klass << "rating-explicit" if post.rating == "e"
klass << "blacklistable" unless options[:no_blacklist]
klass
end

View File

@ -54,7 +54,8 @@ class UserPresenter
end
def uploads
Post.tag_match("user:#{user.name}").limit(8)
posts = Post.tag_match("user:#{user.name}").limit(8)
PostsDecorator.decorate_collection(posts)
end
def has_uploads?
@ -63,7 +64,8 @@ class UserPresenter
def favorites
ids = Favorite.where(user_id: user.id).order(created_at: :desc).limit(50).pluck(:post_id)[0..7]
Post.where(id: ids).sort_by { |post| ids.index(post.id) }
posts = Post.where(id: ids).sort_by { |post| ids.index(post.id) }
PostsDecorator.decorate_collection(posts)
end
def has_favorites?

View File

@ -37,9 +37,9 @@
<%= render "posts/partials/common/inline_blacklist" %>
<div style="margin: 1em 0;">
<section class="posts-container" style="margin: 1em 0;">
<%= @post_set.presenter.post_previews_html(self) %>
</div>
</section>
</div>
</div>
</div>

View File

@ -1,4 +1,4 @@
<% if comment.should_see?(CurrentUser.user) %>
<% if comment.should_see?(CurrentUser.user) || (current_page?(:controller => "comments", :action => "show") && CurrentUser.id == comment.creator_id) %>
<article class="comment comment-post-grid <%= "below-threshold" if comment.below_threshold? %>" data-post-id="<%= comment.post_id %>"
data-comment-id="<%= comment.id %>" data-score="<%= comment.score %>"
data-creator="<%= comment.creator&.name.downcase %>" data-is-sticky="<%= comment.is_sticky %>" data-creator-id="<%= comment.creator_id %>"

View File

@ -18,14 +18,12 @@
</aside>
<section id="content">
<h1>Favorites</h1>
<%= render "posts/partials/index/edit" %>
<div id="posts" class="user-disable-cropped-<%= Danbooru.config.enable_image_cropping? && CurrentUser.user.disable_cropped_thumbnails? %>">
<div id="posts-container">
<section class="posts-container">
<%= @favorite_set.presenter.post_previews_html(self) %>
</div>
</section>
<%= numbered_paginator(@favorite_set.posts) %>
</div>
</section>

View File

@ -65,13 +65,13 @@
</script>
<style id="blacklisted-hider">
.post-preview, #image-container, #c-comments .post, .post-thumbnail {
.thumbnail, #image-container, #c-comments .post, .post-thumbnail {
visibility: hidden !important;
}
</style>
<noscript>
<style>
.post-preview, #image-container, #c-comments .post, .post-thumbnail {
.thumbnail, #image-container, #c-comments .post, .post-thumbnail {
visibility: visible !important;
}
</style>

View File

@ -1,4 +1,4 @@
<%= form_search(path: mod_actions_path) do |f| %>
<%= f.user :creator %>
<%= f.input :action, label: "Action", collection: ModAction::KnownActions.map {|k| [k.to_s.capitalize.tr("_"," "), k.to_s]}, include_blank: true %>
<%= f.input :action, label: "Action", collection: ModAction::KnownActionKeys.map {|k| [k.to_s.capitalize.tr("_"," "), k.to_s]}, include_blank: true %>
<% end %>

View File

@ -6,7 +6,7 @@
<h2>Pools</h2>
<section>
<section class="posts-container">
<% if @pools.none? %>
<%= render "posts/blank" %>
<% else %>

View File

@ -12,7 +12,7 @@
<%= render "posts/partials/common/inline_blacklist" %>
<div id="posts" class="user-disable-cropped-<%= Danbooru.config.enable_image_cropping? && CurrentUser.user.disable_cropped_thumbnails? %>">
<div id="posts-container">
<section class="posts-container">
<% if @posts.none? %>
<%= render "posts/blank" %>
<% else %>

View File

@ -7,9 +7,9 @@
<%= render "posts/partials/common/inline_blacklist" %>
<div id="posts" class="user-disable-cropped-<%= Danbooru.config.enable_image_cropping? && CurrentUser.user.disable_cropped_thumbnails? %>">
<div id="posts-container">
<section class="posts-container">
<%= @post_set.presenter.post_previews_html(self, show_cropped: true) %>
</div>
</section>
</div>
</div>
</div>

View File

@ -1,5 +1,5 @@
<div id="posts" class="user-disable-cropped-<%= Danbooru.config.enable_image_cropping? && CurrentUser.user.disable_cropped_thumbnails? %>">
<div id="posts-container">
<section class="posts-container">
<% if @posts.empty? %>
<%= render "posts/blank" %>
<% else %>
@ -7,7 +7,7 @@
<%= post.preview_html(self, {tags: @post_set.public_tag_string, show_cropped: true, stats: CurrentUser.show_post_statistics?}) %>
<% end %>
<% end %>
</div>
</section>
<% if post_set.hidden_posts.present? %>
<div class="info hidden-posts-notice">

View File

@ -7,23 +7,21 @@
<% end -%>
<% end -%>
<% if stats -%>
<div class="desc">
<%= post_stats_section(post) %>
</div>
<%= post_stats_section(post) %>
<% end -%>
<% if pool -%>
<p class="desc">
<div class="desc">
<%= link_to pool.pretty_name.truncate(80), pool %>
</p>
</div>
<% end -%>
<% if similarity -%>
<p class="desc">
<div class="desc">
Similarity: <%= similarity %>
</p>
</div>
<% end -%>
<% if size -%>
<p class="desc">
<div class="desc">
<%= number_to_human_size(size) %> <%= file_ext.upcase %> (<%= width %>x<%= height %>)
</p>
</div>
<% end -%>
<% end -%>

View File

@ -2,7 +2,7 @@
<%= f.user :user %>
<%= f.user :creator %>
<%= f.input :body_matches, label: "Message" %>
<% if CurrentUser.is_moderator? %>
<% if CurrentUser.is_staff? %>
<%= f.input :deleted, label: "Deleted?", collection: [%w[Excluded excluded], %w[Included included], %w[Only only]], include_blank: true %>
<% end %>
<%= f.input :category, collection: %w[positive negative neutral], include_blank: true %>

View File

@ -2,12 +2,20 @@
<% if CurrentUser.is_moderator? %>
<% if @user_feedback %>
<%= subnav_link_to "New", new_user_feedback_path(user_feedback: { user_id: @user_feedback.user_id }) %>
<% elsif params[:search] && params[:search][:user_id] %>
<%= subnav_link_to "New", new_user_feedback_path(user_feedback: { user_id: params[:search][:user_id] }) %>
<% elsif params.dig(:search, :user_id) %>
<%= subnav_link_to "New", new_user_feedback_path(user_feedback: { user_id: params.dig(:search, :user_id) }) %>
<% else %>
<%= subnav_link_to "New", new_user_feedback_path %>
<% end %>
<% end %>
<%= subnav_link_to "Listing", user_feedbacks_path %>
<%= subnav_link_to "Search", search_user_feedbacks_path %>
<% if CurrentUser.is_moderator? %>
<li>|</li>
<% if @user_feedback %>
<%= subnav_link_to "Ban", new_ban_path(ban: { user_id: @user_feedback.user_id }) %>
<% elsif params.dig(:search, :user_id) %>
<%= subnav_link_to "Ban", new_ban_path(ban: { user_id: params.dig(:search, :user_id) }) %>
<% end %>
<% end %>
<% end %>

View File

@ -50,7 +50,7 @@
</tbody>
</table>
<% if CurrentUser.is_moderator? && params.dig(:search, :user_id).present? && params.dig(:search, :deleted).blank? %>
<% if CurrentUser.is_staff? && params.dig(:search, :user_id).present? && params.dig(:search, :deleted).blank? %>
<% count = UserFeedback.for_user(params.dig(:search, :user_id)).deleted.count %>
<% if count > 0 %>
<%= link_to("Show All (#{count})", { search: params[:search].permit!.merge(deleted: "included") }, class: "show-all-user-feedbacks-link button btn-neutral") %>

View File

@ -18,7 +18,7 @@
<div class="profile-sample-posts">
<% presenter.uploads.each do |post| %>
<%= PostPresenter.preview(post, :tags => "user:#{user.name}") %>
<%= post.preview_html(self, { tags: "user:#{user.name}", show_cropped: true, stats: CurrentUser.show_post_statistics? }) %>
<% end %>
</div>
@ -41,7 +41,7 @@
<div class="profile-sample-posts">
<% presenter.favorites.each do |post| %>
<%= PostPresenter.preview(post, :tags => "fav:#{user.name}") %>
<%= post.preview_html(self, { tags: "fav:#{user.name}", show_cropped: true, stats: CurrentUser.show_post_statistics? }) %>
<% end %>
</div>

View File

@ -89,7 +89,7 @@
<span>
<%= presenter.feedbacks %>
<%= link_to("List", user_feedbacks_path(search: { user_id: @user.id })) %>
<% if CurrentUser.is_moderator? && @user.feedback.count.zero? %>
<% if CurrentUser.is_moderator? && @user.feedback.active.count == 0 %>
| <%= link_to("Create", new_user_feedback_path(user_feedback: { user_id: @user.id, category: "neutral" })) %>
<% end %>
</span>