[Home] Add dynamic mascots

This commit is contained in:
Earlopain 2022-10-15 21:40:40 +02:00
parent f126d4602c
commit b5fbc1e2eb
No known key found for this signature in database
GPG Key ID: 6CFB948E15246897
17 changed files with 359 additions and 33 deletions

View File

@ -0,0 +1,43 @@
class MascotsController < ApplicationController
respond_to :html, :json
before_action :admin_only, except: [:index]
def index
@mascots = Mascot.search(search_params).paginate(params[:page], limit: 75)
respond_with(@mascots)
end
def new
@mascot = Mascot.new
end
def create
@mascot = Mascot.create(mascot_params.merge(creator: CurrentUser.user))
ModAction.log(:mascot_create, { id: @mascot.id }) if @mascot.valid?
respond_with(@mascot, location: mascots_path)
end
def edit
@mascot = Mascot.find(params[:id])
end
def update
@mascot = Mascot.find(params[:id])
@mascot.update(mascot_params)
ModAction.log(:mascot_update, { id: @mascot.id }) if @mascot.valid?
respond_with(@mascot, location: mascots_path)
end
def destroy
@mascot = Mascot.find(params[:id])
@mascot.destroy
ModAction.log(:mascot_delete, { id: @mascot.id })
respond_with(@mascot)
end
private
def mascot_params
params.fetch(:mascot, {}).permit(%i[mascot_file display_name background_color artist_url artist_name active])
end
end

View File

@ -292,6 +292,14 @@ class ModActionDecorator < ApplicationDecorator
when "wiki_page_rename"
"Renamed wiki page ([[#{vals['old_title']}]] → [[#{vals['new_title']}]])"
### Mascots ###
when "mascot_create"
"Created mascot ##{vals['id']}"
when "mascot_update"
"Updated mascot ##{vals['id']}"
when "mascot_delete"
"Deleted mascot ##{vals['id']}"
when "bulk_revert"
"Processed bulk revert for #{vals['constraints']} by #{user}"

View File

@ -4,26 +4,24 @@ const Mascots = {
current: 0
};
function showMascot(cur) {
const mascots = window.mascots;
function showMascot(mascot) {
$('body').css("background-image", "url(" + mascot.background_url + ")");
$('body').css("background-color", mascot.background_color);
$('.mascotbox').css("background-image", "url(" + mascot.background_url + ")");
$('.mascotbox').css("background-color", mascot.background_color);
$('body').css("background-image", "url(" + mascots[cur][0] + ")");
$('body').css("background-color", mascots[cur][1]);
$('.mascotbox').css("background-image", "url(" + mascots[cur][0] + ")");
$('.mascotbox').css("background-color", mascots[cur][1]);
if (mascots[cur][2])
$('#mascot_artist').html("Mascot by " + mascots[cur][2]);
else
$('#mascot_artist').html("&nbsp;");
const artistLink = $("<span>").text("Mascot by ").append($("<a>").text(mascot.artist_name).attr("href", mascot.artist_url));
$("#mascot_artist").empty().append(artistLink);
}
function changeMascot() {
const mascots = window.mascots;
Mascots.current += 1;
Mascots.current = Mascots.current % mascots.length;
showMascot(Mascots.current);
const availableMascotIds = Object.keys(mascots);
const currentMascotIndex = availableMascotIds.indexOf(Mascots.current);
Mascots.current = availableMascotIds[(currentMascotIndex + 1) % availableMascotIds.length];
showMascot(mascots[Mascots.current]);
LS.put('mascot', Mascots.current);
}
@ -31,10 +29,13 @@ function changeMascot() {
function initMascots() {
$('#change-mascot').on('click', changeMascot);
const mascots = window.mascots;
Mascots.current = parseInt(LS.get("mascot"));
if (isNaN(Mascots.current) || Mascots.current < 0 || Mascots.current >= mascots.length)
Mascots.current = Math.floor(Math.random() * mascots.length);
showMascot(Mascots.current);
Mascots.current = LS.get("mascot");
if (!mascots[Mascots.current]) {
const availableMascotIds = Object.keys(mascots);
const mascotIndex = Math.floor(Math.random() * availableMascotIds.length);
Mascots.current = availableMascotIds[mascotIndex];
}
showMascot(mascots[Mascots.current]);
}
$(function () {

View File

@ -3,6 +3,7 @@ class StorageManager
DEFAULT_BASE_DIR = "#{Rails.root}/public/data"
IMAGE_TYPES = %i[preview large crop original]
MASCOT_PREFIX = "mascots"
attr_reader :base_url, :base_dir, :hierarchical, :large_image_prefix, :protected_prefix, :base_path, :replacement_prefix
@ -185,6 +186,24 @@ class StorageManager
"#{base_dir}/#{replacement_prefix}/#{subdir}#{file}"
end
def store_mascot(io, mascot)
store(io, mascot_path(mascot.md5, mascot.file_ext))
end
def mascot_path(md5, file_ext)
file = "#{md5}.#{file_ext}"
"#{base_dir}/#{MASCOT_PREFIX}/#{file}"
end
def mascot_url(mascot)
file = "#{mascot.md5}.#{mascot.file_ext}"
"#{base_url}#{base_path}/#{MASCOT_PREFIX}/#{file}"
end
def delete_mascot(md5, file_ext)
delete(mascot_path(md5, file_ext))
end
def subdir_for(md5)
hierarchical ? "#{md5[0..1]}/#{md5[2..3]}/" : ""
end

83
app/models/mascot.rb Normal file
View File

@ -0,0 +1,83 @@
class Mascot < ApplicationRecord
belongs_to_creator
attr_accessor :mascot_file
validates :display_name, :background_color, :artist_url, :artist_name, presence: true
validates :artist_url, format: { with: %r{\Ahttps?://}, message: "must start with http:// or https://" }
validates :mascot_file, presence: true, on: :create
validate :set_file_properties
validates :md5, uniqueness: true
validate if: :mascot_file do |mascot|
max_file_sizes = { "jpg" => 500.kilobytes, "png" => 500.kilobytes }
FileValidator.new(mascot, mascot_file.path).validate(max_file_sizes: max_file_sizes, max_width: 1_000, max_height: 1_000)
end
after_commit :invalidate_cache
after_save_commit :write_storage_file
after_destroy_commit :remove_storage_file
def set_file_properties
return if mascot_file.blank?
self.file_ext = file_header_to_file_ext(mascot_file.path)
self.md5 = Digest::MD5.file(mascot_file.path).hexdigest
end
def write_storage_file
return if mascot_file.blank?
Danbooru.config.storage_manager.delete_mascot(md5_previously_was, file_ext_previously_was)
Danbooru.config.storage_manager.store_mascot(mascot_file, self)
end
def self.active_for_browser
Cache.get("active_mascots", 1.day) do
mascots = Mascot.where(active: true).map do |mascot|
mascot.slice(:id, :background_color, :artist_url, :artist_name).merge(background_url: mascot.url_path)
end
mascots.index_by { |mascot| mascot["id"] }
end
end
def invalidate_cache
Cache.delete("active_mascots")
end
def remove_storage_file
Danbooru.config.storage_manager.delete_mascot(md5, file_ext)
end
def url_path
Danbooru.config.storage_manager.mascot_url(self)
end
def file_path
Danbooru.config.storage_manager.mascot_path(self)
end
concerning :ValidationMethods do
def dimensions
@dimensions ||= calculate_dimensions(mascot_file.path)
end
def image_width
dimensions[0]
end
def image_height
dimensions[1]
end
def file_size
@file_size ||= Danbooru.config.storage_manager.open(mascot_file.path).size
end
end
def self.search(params)
q = super
q.order("lower(artist_name)")
end
include FileMethods
end

View File

@ -39,6 +39,9 @@ class ModAction < ApplicationRecord
:help_update,
:ip_ban_create,
:ip_ban_delete,
:mascot_create,
:mascot_update,
:mascot_delete,
:pool_delete,
:report_reason_create,
:report_reason_delete,

View File

@ -0,0 +1,10 @@
<%= custom_form_for(mascot) do |f| %>
<%= error_messages_for "mascot" %>
<%= f.input :mascot_file, as: :file, input_html: { accept: "image/png,image/jpeg,.png,.jpg,.jpeg" } %>
<%= f.input :display_name %>
<%= f.input :background_color %>
<%= f.input :artist_url %>
<%= f.input :artist_name %>
<%= f.input :active %>
<%= f.submit %>
<% end %>

View File

@ -0,0 +1,8 @@
<% content_for(:secondary_links) do %>
<menu>
<%= subnav_link_to "Listing", mascots_path %>
<% if CurrentUser.user.is_admin? %>
<%= subnav_link_to "New", new_mascot_path %>
<% end %>
</menu>
<% end %>

View File

@ -0,0 +1,12 @@
<div class="c-mascots">
<div class="a-edit">
<h1>Edit Mascot</h1>
<%= render "form", mascot: @mascot %>
</div>
</div>
<%= render "secondary_links" %>
<% content_for(:page_title) do %>
Edit Mascot
<% end %>

View File

@ -0,0 +1,47 @@
<div id="c-mascots">
<div id="a-index">
<h1>Mascots</h1>
<table class="striped">
<thead>
<tr>
<th>Name</th>
<th>Background Color</th>
<th>Artist Name</th>
<th>Artist URL</th>
<th>Active</th>
<th>Created</th>
<% if CurrentUser.user.is_admin? %>
<th></th>
<% end %>
</tr>
</thead>
<tbody>
<% @mascots.each do |mascot| %>
<tr>
<td><%= link_to mascot.display_name, mascot.url_path %></td>
<td><%= mascot.background_color %></td>
<td><%= mascot.artist_name %></td>
<td><%= mascot.artist_url %></td>
<td><%= mascot.active %></td>
<td><%= compact_time mascot.created_at %></td>
<% if CurrentUser.user.is_admin? %>
<td>
<%= link_to "Edit", edit_mascot_path(mascot) %>
| <%= link_to "Delete", mascot_path(mascot), method: :delete, data: { confirm: "Are you sure you want to delete this mascot?" } %>
</td>
<% end %>
</tr>
<% end %>
</tbody>
</table>
<%= numbered_paginator(@mascots) %>
</div>
</div>
<%= render "secondary_links" %>
<% content_for(:page_title) do %>
Mascots
<% end %>

View File

@ -0,0 +1,12 @@
<div class="c-mascots">
<div class="a-new">
<h1>New Mascot</h1>
<%= render "form", mascot: @mascot %>
</div>
</div>
<%= render "secondary_links" %>
<% content_for(:page_title) do %>
New Mascot
<% end %>

View File

@ -87,7 +87,7 @@
<div id="c-static">
<div id="a-home">
<%= javascript_tag nonce: true do -%>
var mascots = <%= Danbooru.config.mascots.to_json.html_safe %>;
var mascots = <%= Mascot.active_for_browser.to_json.html_safe %>;
<% end -%>
<div id="searchbox" class='mascotbox'>

View File

@ -30,6 +30,7 @@
<ul>
<li><h1>Tools</h1></li>
<li><%= link_to("News Updates", news_updates_path) %></li>
<li><%= link_to("Mascots", mascots_path) %></li>
<li><%= link_to("Source Code", Danbooru.config.source_code_url) %></li>
<li><%= link_to("Keyboard Shortcuts", keyboard_shortcuts_path) %></li>
<li><%= link_to("API Documentation", help_page_path(id: "api")) %></li>

View File

@ -798,19 +798,6 @@ fart'
{zone: nil, revive_id: nil, checksum: nil}
end
def mascots
[
["https://static1.e621.net/data/mascot_bg/esix1.jpg", "#012e56", "<a href='http://www.furaffinity.net/user/keishinkae'>Keishinkae</a>"],
["https://static1.e621.net/data/mascot_bg/esix2.jpg", "#012e56", "<a href='http://www.furaffinity.net/user/keishinkae'>Keishinkae</a>"],
["https://static1.e621.net/data/mascot_bg/raptor1.jpg", "#012e56", "<a href='http://nowhereincoming.net/'>darkdoomer</a>"],
["https://static1.e621.net/data/mascot_bg/hexerade.jpg", "#002d55", "<a href='http://www.furaffinity.net/user/chizi'>chizi</a>"],
["https://static1.e621.net/data/mascot_bg/wiredhooves.jpg", "#012e56", "<a href='http://www.furaffinity.net/user/wiredhooves'>wiredhooves</a>"],
["https://static1.e621.net/data/mascot_bg/ecmajor.jpg", "#012e57", "<a href='http://www.horsecore.org/'>ECMajor</a>"],
["https://static1.e621.net/data/mascot_bg/evalionfix.jpg", "#012e57", "<a href='http://www.furaffinity.net/user/evalion'>evalion</a>"],
["https://static1.e621.net/data/mascot_bg/peacock.png", "#012e57", "<a href='http://www.furaffinity.net/user/ratte'>Ratte</a>"]
]
end
# Additional video samples will be generated in these dimensions if it makes sense to do so
# They will be available as additional scale options on applicable posts in the order they appear here
def video_rescales

View File

@ -341,6 +341,7 @@ Rails.application.routes.draw do
get :resend_confirmation
end
end
resources :mascots, only: [:index, :new, :create, :edit, :update, :destroy]
options "*all", to: "application#enable_cors"

View File

@ -0,0 +1,15 @@
class AddMascotTable < ActiveRecord::Migration[6.1]
def change
create_table :mascots do |t|
t.references :creator, foreign_key: { to_table: :users }, null: false
t.string :display_name, null: false
t.string :md5, index: { unique: true }, null: false
t.string :file_ext, null: false
t.string :background_color, null: false
t.string :artist_url, null: false
t.string :artist_name, null: false
t.boolean :active, default: true, null: false
t.timestamps
end
end
end

View File

@ -1034,6 +1034,44 @@ CREATE SEQUENCE public.janitor_trials_id_seq
ALTER SEQUENCE public.janitor_trials_id_seq OWNED BY public.janitor_trials.id;
--
-- Name: mascots; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE public.mascots (
id bigint NOT NULL,
creator_id bigint NOT NULL,
display_name character varying NOT NULL,
md5 character varying NOT NULL,
file_ext character varying NOT NULL,
background_color character varying NOT NULL,
artist_url character varying NOT NULL,
artist_name character varying NOT NULL,
active boolean DEFAULT true NOT NULL,
created_at timestamp(6) without time zone NOT NULL,
updated_at timestamp(6) without time zone NOT NULL
);
--
-- Name: mascots_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE public.mascots_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: mascots_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE public.mascots_id_seq OWNED BY public.mascots.id;
--
-- Name: mod_actions_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
@ -2623,6 +2661,13 @@ ALTER TABLE ONLY public.ip_bans ALTER COLUMN id SET DEFAULT nextval('public.ip_b
ALTER TABLE ONLY public.janitor_trials ALTER COLUMN id SET DEFAULT nextval('public.janitor_trials_id_seq'::regclass);
--
-- Name: mascots id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.mascots ALTER COLUMN id SET DEFAULT nextval('public.mascots_id_seq'::regclass);
--
-- Name: news_updates id; Type: DEFAULT; Schema: public; Owner: -
--
@ -3076,6 +3121,14 @@ ALTER TABLE ONLY public.janitor_trials
ADD CONSTRAINT janitor_trials_pkey PRIMARY KEY (id);
--
-- Name: mascots mascots_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.mascots
ADD CONSTRAINT mascots_pkey PRIMARY KEY (id);
--
-- Name: mod_actions mod_actions_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
@ -3765,6 +3818,20 @@ CREATE UNIQUE INDEX index_ip_bans_on_ip_addr ON public.ip_bans USING btree (ip_a
CREATE INDEX index_janitor_trials_on_user_id ON public.janitor_trials USING btree (user_id);
--
-- Name: index_mascots_on_creator_id; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX index_mascots_on_creator_id ON public.mascots USING btree (creator_id);
--
-- Name: index_mascots_on_md5; Type: INDEX; Schema: public; Owner: -
--
CREATE UNIQUE INDEX index_mascots_on_md5 ON public.mascots USING btree (md5);
--
-- Name: index_mod_actions_on_action; Type: INDEX; Schema: public; Owner: -
--
@ -4452,6 +4519,14 @@ ALTER TABLE ONLY public.staff_audit_logs
ADD CONSTRAINT fk_rails_02329e5ef9 FOREIGN KEY (user_id) REFERENCES public.users(id);
--
-- Name: mascots fk_rails_9901e810fa; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.mascots
ADD CONSTRAINT fk_rails_9901e810fa FOREIGN KEY (creator_id) REFERENCES public.users(id);
--
-- Name: favorites fk_rails_a7668ef613; Type: FK CONSTRAINT; Schema: public; Owner: -
--
@ -4734,6 +4809,7 @@ INSERT INTO "schema_migrations" (version) VALUES
('20220316162257'),
('20220516103329'),
('20220710133556'),
('20220810131625');
('20220810131625'),
('20221014085948');