Skip to content

Commit

Permalink
Add: #406 ユーザーのカスタムCSS
Browse files Browse the repository at this point in the history
  • Loading branch information
kmycode committed Aug 28, 2024
1 parent a7d9fd1 commit 1bfebb8
Show file tree
Hide file tree
Showing 20 changed files with 171 additions and 3 deletions.
23 changes: 22 additions & 1 deletion app/controllers/auth/sessions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,11 @@ def find_user_from_params
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)
Expand Down Expand Up @@ -113,6 +117,11 @@ def continue_after?
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')
Expand Down Expand Up @@ -151,6 +160,8 @@ def on_authentication_success(user, security_measure)
sign_in(user)
flash.delete(:notice)

disable_custom_css!(user) if disable_custom_css?

LoginActivity.create(
user: user,
success: true,
Expand All @@ -162,6 +173,16 @@ def on_authentication_success(user, security_measure)
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.settings['web.custom_css_version'] += 1
user.save!
end

def suspicious_sign_in?(user)
SuspiciousSignInDetector.new(user).suspicious?(request)
end
Expand Down
12 changes: 11 additions & 1 deletion app/controllers/custom_css_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,17 @@ def show
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.setting_custom_css.present?
end

def user_custom_css
current_user.setting_custom_css
end
helper_method :custom_css_styles, :user_custom_css?, :user_custom_css

def set_user_roles
@user_roles = UserRole.providing_styles
Expand Down
9 changes: 9 additions & 0 deletions app/controllers/settings/preferences/custom_css_controller.rb
Original file line number Diff line number Diff line change
@@ -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
6 changes: 6 additions & 0 deletions app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,12 @@ def mascot_url
full_asset_url(instance_presenter.mascot&.file&.url || frontend_asset_path('images/elephant_ui_plane.svg'))
end

def user_custom_css_version
return '0' if current_account&.user.nil?

current_account&.user&.setting_custom_css_version.to_s
end

private

def storage_host_var
Expand Down
12 changes: 12 additions & 0 deletions app/models/concerns/user/has_settings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,18 @@ def setting_hide_favourite_menu
settings['web.hide_favourite_menu']
end

def setting_use_custom_css
settings['web.use_custom_css']
end

def setting_custom_css
settings['web.custom_css']
end

def setting_custom_css_version
settings['web.custom_css_version']
end

def allows_report_emails?
settings['notification_emails.report']
end
Expand Down
4 changes: 4 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,10 @@ def update_sign_in!(new_sign_in: false)
prepare_returning_user!
end

def disable_css
false
end

def pending?
!approved?
end
Expand Down
3 changes: 3 additions & 0 deletions app/models/user_settings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ class KeyError < Error; end
setting :use_blurhash, default: true
setting :use_pending_items, default: false
setting :use_system_font, default: false
setting :use_custom_css, default: false
setting :custom_css, default: ''
setting :custom_css_version, default: 0
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
Expand Down
2 changes: 2 additions & 0 deletions app/models/user_settings/setting.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ def type
case default_value
when TrueClass, FalseClass
ActiveModel::Type::Boolean.new
when Integer
ActiveModel::Type::Integer.new
else
ActiveModel::Type::String.new
end
Expand Down
9 changes: 9 additions & 0 deletions app/views/auth/sessions/new.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
3 changes: 3 additions & 0 deletions app/views/auth/shared/_links.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 4 additions & 0 deletions app/views/custom_css/show.css.erb
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@
}

<%- end %>
<%- if user_custom_css? %>
<%= raw user_custom_css %>
<%- end %>
2 changes: 1 addition & 1 deletion app/views/layouts/application.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
= csrf_meta_tags unless skip_csrf_meta_tags?
%meta{ name: 'style-nonce', content: request.content_security_policy_nonce }

= stylesheet_link_tag custom_css_path, skip_pipeline: true, host: root_url, media: 'all'
= stylesheet_link_tag custom_css_path({ version: user_custom_css_version }), skip_pipeline: true, host: root_url, media: 'all'

= yield :header_tags

Expand Down
34 changes: 34 additions & 0 deletions app/views/settings/preferences/custom_css/show.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
- 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|
= ff.input :'web.custom_css_version',
as: :hidden,
input_html: { value: current_user.setting_custom_css_version + 1 }

.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
= ff.input :'web.custom_css',
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
2 changes: 2 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1418,6 +1418,7 @@ en:
prefix_sign_up: Sign up on Mastodon today!
suffix: With an account, you will be able to follow people, post updates and exchange messages with users from any Mastodon server and more!
didnt_get_confirmation: Didn't receive a confirmation link?
disable_custom_css: Disable custom CSS
dont_have_your_security_key: Don't have your security key?
forgot_password: Forgot your password?
invalid_reset_password_token: Password reset token is invalid or expired. Please request a new one.
Expand Down Expand Up @@ -1478,6 +1479,7 @@ en:
view_strikes: View past strikes against your account
too_fast: Form submitted too fast, try again.
use_security_key: Use security key
with_login_options: Will you disable your custom css?
bookmark_categories:
errors:
limit: Bookmark category limit
Expand Down
2 changes: 2 additions & 0 deletions config/locales/ja.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1329,6 +1329,7 @@ ja:
prefix_sign_up: 今すぐMastodonを始めよう!
suffix: アカウントがあれば、どんなMastodon互換サーバーのユーザーでもフォローしたりメッセージをやり取りできるようになります!
didnt_get_confirmation: 確認メールを受信できない場合は
disable_custom_css: カスタムCSSを無効化する
dont_have_your_security_key: セキュリティキーを持っていませんか?
forgot_password: パスワードをお忘れですか?
invalid_reset_password_token: パスワードリセットトークンが正しくないか期限切れです。もう一度リクエストしてください。
Expand Down Expand Up @@ -1384,6 +1385,7 @@ ja:
view_strikes: 過去のストライクを表示
too_fast: フォームの送信が速すぎます。もう一度やり直してください。
use_security_key: セキュリティキーを使用
with_login_options: カスタムCSSを無効化しますか?
challenge:
confirm: 続ける
hint_html: 以後1時間はパスワードの再入力を求めません
Expand Down
3 changes: 3 additions & 0 deletions config/locales/simple_form.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ en:
setting_allow_quote: Subdued quotes are allowed regardless of this setting; you can quote freely from any source except kmyblue!
setting_always_send_emails: Normally e-mail notifications won't be sent when you are actively using Mastodon
setting_bookmark_category_needed: When removing from all category, unbookmarked automatically
setting_custom_css_lead: "Be sure to remember: In the unlikely event that you make a mistake in entering your custom CSS and the screen does not display properly, you can disable your custom CSS from the link at the bottom of the sign-in screen. Open the sign-in screen in private mode of your browser, for example, and disable it."
setting_default_searchability: On kmyblue and Fedibird, the search is based on the search permission setting; on Misskey, all public, local public, and non-public posts are searched regardless of this setting; on Mastodon and Firefish, instead of search permission, the "Make public posts freely searchable on other servers" setting in the profile settings is applied. In Mastodon and Firefish, the "Make public posts freely searchable on other servers" setting in the profile settings is applied instead of the search permission.
setting_default_sensitive: Sensitive media is hidden by default and can be revealed with a click
setting_disallow_unlisted_public_searchability: この設定を有効にすると、非収載投稿と検索範囲「誰でも」は両立できず不特定多数からの検索が不可になります。Fedibirdと同じ挙動になります
Expand Down Expand Up @@ -263,6 +264,7 @@ en:
medium: Default
x_large: Large large
xx_large: Large large large
setting_custom_css: Custom CSS
setting_default_language: Posting language
setting_default_privacy: Posting privacy
setting_default_reblog_privacy: Reblogging privacy
Expand Down Expand Up @@ -325,6 +327,7 @@ en:
setting_trends: Show today's trends
setting_unfollow_modal: Show confirmation dialog before unfollowing someone
setting_use_blurhash: Show colorful gradients for hidden media
setting_use_custom_css: Enable custom CSS
setting_use_pending_items: Slow mode
setting_use_public_index: Include permitted accounts post to results of search
severity: Severity
Expand Down
3 changes: 3 additions & 0 deletions config/locales/simple_form.ja.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ ja:
setting_allow_quote: ひかえめな引用はこの設定に関わらず可能です。kmyblue以外からは自由に引用できます
setting_always_send_emails: 通常、Mastodon からメール通知は行われません。
setting_bookmark_category_needed: すべてのカテゴリから削除したとき、ブックマークが自動で外れるようになります
setting_custom_css_lead: "必ず覚えてください: 万が一カスタムCSSの入力を誤り、画面が正常に表示されなくなった場合は、サインイン画面の下にあるリンクよりカスタムCSSを無効化することができます。ブラウザのプライベートモードなどでサインイン画面を開き、無効化してください。"
setting_default_searchability: kmyblue・Fedibirdでは検索許可設定に基づき検索されます。Misskeyでは当設定に関係なく、全ての公開・ローカル公開・非収載投稿が検索されます。Mastodon・Firefishでは検索許可の代わりにプロフィール設定の「公開投稿を他のサーバーで自由に検索できるようにする」設定が適用されます
setting_default_sensitive: 閲覧注意状態のメディアはデフォルトでは内容が伏せられ、クリックして初めて閲覧できるようになります
setting_disallow_unlisted_public_searchability: この設定を有効にすると、非収載投稿と検索範囲「誰でも」は両立できず不特定多数からの検索が不可になります。Fedibirdと同じ挙動になります
Expand Down Expand Up @@ -263,6 +264,7 @@ ja:
medium: デフォルト
x_large: 大きい大きい
xx_large: 大きい大きい大きい
setting_custom_css: カスタムCSS
setting_default_language: 投稿する言語
setting_default_privacy: 投稿の公開範囲
setting_default_reblog_privacy: BTの公開範囲
Expand Down Expand Up @@ -325,6 +327,7 @@ ja:
setting_trends: 本日のトレンドタグを表示する
setting_unfollow_modal: フォローを解除する前に確認ダイアログを表示する
setting_use_blurhash: 非表示のメディアを色付きのぼかしで表示する
setting_use_custom_css: カスタムCSSを有効にする
setting_use_pending_items: 手動更新モード
setting_use_public_index: Mastodonの標準設定によって検索が許可されたアカウントの公開投稿を検索結果に含める
severity: 重大性
Expand Down
1 change: 1 addition & 0 deletions config/navigation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
s.item :appearance, safe_join([material_symbol('computer'), t('settings.appearance')]), settings_preferences_appearance_path
s.item :notifications, safe_join([material_symbol('mail'), t('settings.notifications')]), settings_preferences_notifications_path
s.item :reaching, safe_join([material_symbol('search'), t('preferences.reaching')]), settings_preferences_reaching_path
s.item :custom_css, safe_join([material_symbol('inbox'), t('preferences.custom_css')]), settings_preferences_custom_css_path
s.item :other, safe_join([material_symbol('settings'), t('preferences.other')]), settings_preferences_other_path
end

Expand Down
1 change: 1 addition & 0 deletions config/routes/settings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
resource :appearance, only: [:show, :update], controller: :appearance
resource :notifications, only: [:show, :update]
resource :reaching, only: [:show, :update], controller: :reaching
resource :custom_css, only: [:show, :update], controller: :custom_css
resource :other, only: [:show, :update], controller: :other
end

Expand Down
39 changes: 39 additions & 0 deletions spec/controllers/auth/sessions_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,45 @@
end
end
end

context 'with custom css' do
let(:params) { {} }

before do
user.settings['web.use_custom_css'] = true
user.save!

post :create, params: { user: { email: user.email, password: user.password }.merge(params) }
end

context 'when does not reset custom css' do
let(:params) { { disable_css: '0' } }

it 'custom css is enabled' do
expect(response).to redirect_to(root_path)
expect(controller.current_user).to eq user
expect(user.reload.setting_use_custom_css).to be true
end
end

context 'when reset custom css' do
let(:params) { { disable_css: '1' } }

it 'custom css is disabled' do
expect(response).to redirect_to(root_path)
expect(controller.current_user).to eq user
expect(user.reload.setting_use_custom_css).to be false
end
end

context 'when does not specify about custom css' do
it 'custom css is enabled' do
expect(response).to redirect_to(root_path)
expect(controller.current_user).to eq user
expect(user.reload.setting_use_custom_css).to be true
end
end
end
end

context 'when using two-factor authentication' do
Expand Down

0 comments on commit 1bfebb8

Please sign in to comment.