Add: #406 ユーザーのカスタムCSS (#825)

* Add: #406 ユーザーのカスタムCSS

* Fix lint

* Fix lint

* カスタムCSSの保存先を変更

* キャッシュを考慮して別URLに変更
This commit is contained in:
KMY(雪あすか) 2024-08-29 07:55:01 +09:00 committed by GitHub
parent 5ffd7593f1
commit 665c632d66
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 283 additions and 4 deletions

View file

@ -78,7 +78,11 @@ class Auth::SessionsController < Devise::SessionsController
end
def user_params
params.require(:user).permit(:email, :password, :otp_attempt, credential: {})
params.require(:user).permit(:email, :password, :otp_attempt, :disable_css, credential: {})
end
def login_page_params
params.permit(:with_options)
end
def after_sign_in_path_for(resource)
@ -113,6 +117,11 @@ class Auth::SessionsController < Devise::SessionsController
truthy_param?(:continue)
end
def with_login_options?
login_page_params[:with_options] == '1'
end
helper_method :with_login_options?
def restart_session
clear_attempt_from_session
redirect_to new_user_session_path, alert: I18n.t('devise.failure.timeout')
@ -151,6 +160,8 @@ class Auth::SessionsController < Devise::SessionsController
sign_in(user)
flash.delete(:notice)
disable_custom_css!(user) if disable_custom_css?
LoginActivity.create(
user: user,
success: true,
@ -162,6 +173,15 @@ class Auth::SessionsController < Devise::SessionsController
UserMailer.suspicious_sign_in(user, request.remote_ip, request.user_agent, Time.now.utc).deliver_later! if @login_is_suspicious
end
def disable_custom_css?
user_params[:disable_css].present? && user_params[:disable_css] != '0'
end
def disable_custom_css!(user)
user.settings['web.use_custom_css'] = false
user.save!
end
def suspicious_sign_in?(user)
SuspiciousSignInDetector.new(user).suspicious?(request)
end

View file

@ -13,7 +13,17 @@ class CustomCssController < ActionController::Base # rubocop:disable Rails/Appli
def custom_css_styles
Setting.custom_css
end
helper_method :custom_css_styles
def user_custom_css?
return false if current_user.nil?
current_user.setting_use_custom_css && current_user.custom_css_text.present?
end
def user_custom_css
current_user.custom_css_text
end
helper_method :custom_css_styles, :user_custom_css?, :user_custom_css
def set_user_roles
@user_roles = UserRole.providing_styles

View file

@ -25,7 +25,7 @@ class Settings::Preferences::BaseController < Settings::BaseController
end
def original_user_params
params.require(:user).permit(:locale, :time_zone, chosen_languages: [], settings_attributes: UserSettings.keys)
params.require(:user).permit(:locale, :time_zone, :custom_css_text, chosen_languages: [], settings_attributes: UserSettings.keys)
end
def disabled_visibilities_params

View file

@ -0,0 +1,9 @@
# frozen_string_literal: true
class Settings::Preferences::CustomCssController < Settings::Preferences::BaseController
private
def after_update_redirect_path
settings_preferences_custom_css_path
end
end

View file

@ -0,0 +1,16 @@
# frozen_string_literal: true
class UserCustomCssController < ActionController::Base # rubocop:disable Rails/ApplicationController
before_action :authenticate_user!
def show
render content_type: 'text/css'
end
private
def user_custom_css
current_user.custom_css_text
end
helper_method :user_custom_css
end

View file

@ -252,6 +252,18 @@ module ApplicationHelper
full_asset_url(instance_presenter.mascot&.file&.url || frontend_asset_path('images/elephant_ui_plane.svg'))
end
def user_custom_css?
return false if current_account&.user.nil?
current_account.user.setting_use_custom_css && current_account.user.custom_css_text.present?
end
def user_custom_css_version
return '0' if current_account&.user&.custom_css.nil?
current_account&.user&.custom_css&.updated_at.to_s
end
private
def storage_host_var

View file

@ -283,6 +283,10 @@ module User::HasSettings
settings['web.hide_favourite_menu']
end
def setting_use_custom_css
settings['web.use_custom_css']
end
def allows_report_emails?
settings['notification_emails.report']
end

16
app/models/custom_css.rb Normal file
View file

@ -0,0 +1,16 @@
# frozen_string_literal: true
#
# == Schema Information
#
# Table name: custom_csses
#
# id :bigint(8) not null, primary key
# user_id :bigint(8) not null
# css :string default(""), not null
# created_at :datetime not null
# updated_at :datetime not null
#
class CustomCss < ApplicationRecord
belongs_to :user
end

View file

@ -101,6 +101,8 @@ class User < ApplicationRecord
accepts_nested_attributes_for :invite_request, reject_if: ->(attributes) { attributes['text'].blank? && !Setting.require_invite_text }
validates :invite_request, presence: true, on: :create, if: :invite_text_required?
has_one :custom_css, inverse_of: :user, dependent: :destroy
validates :email, presence: true, email_address: true
validates_with UserEmailValidator, if: -> { ENV['EMAIL_DOMAIN_LISTS_APPLY_AFTER_CONFIRMATION'] == 'true' || !confirmed? }
@ -227,6 +229,22 @@ class User < ApplicationRecord
prepare_returning_user!
end
def disable_css
false
end
def custom_css_text
custom_css&.css.to_s
end
def custom_css_text=(val)
if custom_css.present?
custom_css.update!(css: val)
else
CustomCss.create!(user: self, css: val)
end
end
def pending?
!approved?
end

View file

@ -55,6 +55,7 @@ class UserSettings
setting :use_blurhash, default: true
setting :use_pending_items, default: false
setting :use_system_font, default: false
setting :use_custom_css, default: false
setting :content_font_size, default: 'medium', in: %w(medium large x_large xx_large)
setting :bookmark_category_needed, default: false
setting :disable_swiping, default: false

View file

@ -63,6 +63,8 @@ class UserSettings::Setting
case default_value
when TrueClass, FalseClass
ActiveModel::Type::Boolean.new
when Integer
ActiveModel::Type::Integer.new
else
ActiveModel::Type::String.new
end

View file

@ -30,6 +30,15 @@
label: t('simple_form.labels.defaults.password'),
wrapper: :with_label
- if with_login_options?
.fields-group
= f.input :disable_css,
as: :boolean,
hint: false,
input_html: { 'aria-label': t('auth.disable_custom_css') },
label: t('auth.disable_custom_css'),
wrapper: :with_label
.actions
= f.button :button, t('auth.login'), type: :submit

View file

@ -11,6 +11,9 @@
- if controller_name != 'passwords' && controller_name != 'registrations'
%li= link_to t('auth.forgot_password'), new_user_password_path
- if controller_name != 'passwords' && controller_name != 'registrations' && params[:with_options].nil?
%li= link_to t('auth.with_login_options'), new_user_session_path(with_options: '1')
- if controller_name != 'confirmations' && (!user_signed_in? || !current_user.confirmed? || current_user.unconfirmed_email.present?)
%li= link_to t('auth.didnt_get_confirmation'), new_user_confirmation_path

View file

@ -36,6 +36,9 @@
= stylesheet_link_tag custom_css_path, skip_pipeline: true, host: root_url, media: 'all'
- if user_custom_css?
= stylesheet_link_tag user_custom_css_path({ version: user_custom_css_version }), skip_pipeline: true, host: root_url, media: 'all'
= yield :header_tags
%body{ class: body_classes }

View file

@ -0,0 +1,30 @@
- content_for :page_title do
= t('simple_form.labels.form_admin_settings.custom_css')
- content_for :heading_actions do
= button_tag t('generic.save_changes'), class: 'button', form: 'edit_preferences'
= simple_form_for current_user, url: settings_preferences_custom_css_path, html: { method: :put, id: 'edit_preferences' } do |f|
= render 'shared/error_messages', object: current_user
= f.simple_fields_for :settings, current_user.settings do |ff|
.fields-group
= ff.input :'web.use_custom_css',
hint: false,
label: I18n.t('simple_form.labels.defaults.setting_use_custom_css'),
kmyblue: true,
wrapper: :with_label
.fields-group
= f.input :custom_css_text,
as: :text,
hint: false,
input_html: { rows: 12 },
label: I18n.t('simple_form.labels.defaults.setting_custom_css'),
kmyblue: true,
wrapper: :with_label
%p.hint= t 'simple_form.hints.defaults.setting_custom_css_lead'
.actions
= f.button :button, t('generic.save_changes'), type: :submit

View file

@ -0,0 +1 @@
<%= raw user_custom_css %>