forked from e621ng/e621ng
[Home] Add dynamic mascots
This commit is contained in:
parent
f126d4602c
commit
b5fbc1e2eb
43
app/controllers/mascots_controller.rb
Normal file
43
app/controllers/mascots_controller.rb
Normal 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
|
@ -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}"
|
||||
|
||||
|
@ -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(" ");
|
||||
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 () {
|
||||
|
@ -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
83
app/models/mascot.rb
Normal 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
|
@ -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,
|
||||
|
10
app/views/mascots/_form.html.erb
Normal file
10
app/views/mascots/_form.html.erb
Normal 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 %>
|
8
app/views/mascots/_secondary_links.html.erb
Normal file
8
app/views/mascots/_secondary_links.html.erb
Normal 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 %>
|
12
app/views/mascots/edit.html.erb
Normal file
12
app/views/mascots/edit.html.erb
Normal 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 %>
|
47
app/views/mascots/index.html.erb
Normal file
47
app/views/mascots/index.html.erb
Normal 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 %>
|
12
app/views/mascots/new.html.erb
Normal file
12
app/views/mascots/new.html.erb
Normal 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 %>
|
@ -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'>
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
||||
|
15
db/migrate/20221014085948_add_mascot_table.rb
Normal file
15
db/migrate/20221014085948_add_mascot_table.rb
Normal 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
|
@ -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');
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user