diff --git a/app/controllers/auth/sessions_controller.rb b/app/controllers/auth/sessions_controller.rb index a2fed644fe..c28c0eab8b 100644 --- a/app/controllers/auth/sessions_controller.rb +++ b/app/controllers/auth/sessions_controller.rb @@ -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 diff --git a/app/controllers/custom_css_controller.rb b/app/controllers/custom_css_controller.rb index eb6417698a..f0ff6f8ca7 100644 --- a/app/controllers/custom_css_controller.rb +++ b/app/controllers/custom_css_controller.rb @@ -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 diff --git a/app/controllers/settings/preferences/base_controller.rb b/app/controllers/settings/preferences/base_controller.rb index ce6e2bba44..d3e62fb5d9 100644 --- a/app/controllers/settings/preferences/base_controller.rb +++ b/app/controllers/settings/preferences/base_controller.rb @@ -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 diff --git a/app/controllers/settings/preferences/custom_css_controller.rb b/app/controllers/settings/preferences/custom_css_controller.rb new file mode 100644 index 0000000000..6a7369ec49 --- /dev/null +++ b/app/controllers/settings/preferences/custom_css_controller.rb @@ -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 diff --git a/app/controllers/user_custom_css_controller.rb b/app/controllers/user_custom_css_controller.rb new file mode 100644 index 0000000000..2535e07c03 --- /dev/null +++ b/app/controllers/user_custom_css_controller.rb @@ -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 diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index f37a51a864..7779926bae 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -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 diff --git a/app/models/concerns/user/has_settings.rb b/app/models/concerns/user/has_settings.rb index 63e585ec5e..655e589aae 100644 --- a/app/models/concerns/user/has_settings.rb +++ b/app/models/concerns/user/has_settings.rb @@ -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 diff --git a/app/models/custom_css.rb b/app/models/custom_css.rb new file mode 100644 index 0000000000..470395a96e --- /dev/null +++ b/app/models/custom_css.rb @@ -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 diff --git a/app/models/user.rb b/app/models/user.rb index 2fd19550ce..eab5f6fd52 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -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 diff --git a/app/models/user_settings.rb b/app/models/user_settings.rb index 2d962cd87c..0a995be81a 100644 --- a/app/models/user_settings.rb +++ b/app/models/user_settings.rb @@ -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 diff --git a/app/models/user_settings/setting.rb b/app/models/user_settings/setting.rb index 0aab0da689..9b1eb2e1ef 100644 --- a/app/models/user_settings/setting.rb +++ b/app/models/user_settings/setting.rb @@ -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 diff --git a/app/views/auth/sessions/new.html.haml b/app/views/auth/sessions/new.html.haml index 89903581a2..fb64bab781 100644 --- a/app/views/auth/sessions/new.html.haml +++ b/app/views/auth/sessions/new.html.haml @@ -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 diff --git a/app/views/auth/shared/_links.html.haml b/app/views/auth/shared/_links.html.haml index 757ef0a090..bd2245e111 100644 --- a/app/views/auth/shared/_links.html.haml +++ b/app/views/auth/shared/_links.html.haml @@ -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 diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index a6b34c8a36..51a7c3ebf1 100755 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -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 } diff --git a/app/views/settings/preferences/custom_css/show.html.haml b/app/views/settings/preferences/custom_css/show.html.haml new file mode 100644 index 0000000000..03ee400093 --- /dev/null +++ b/app/views/settings/preferences/custom_css/show.html.haml @@ -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 diff --git a/app/views/user_custom_css/show.css.erb b/app/views/user_custom_css/show.css.erb new file mode 100644 index 0000000000..97ca495248 --- /dev/null +++ b/app/views/user_custom_css/show.css.erb @@ -0,0 +1 @@ +<%= raw user_custom_css %> diff --git a/config/locales/en.yml b/config/locales/en.yml index 11d55dd1eb..20f45bebc9 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -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. @@ -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 @@ -1906,6 +1908,7 @@ en: too_few_options: must have more than one item too_many_options: can't contain more than %{max} items preferences: + custom_css: Custom css does_not_search: The full-text search feature is not available on this server. Instead, your posts will be searched according to this setting on other kmyblue servers. dtl: Deep timeline dtl_hint: 'You can join deep timeline with #%{tag} tag. Following settings make convenient to use deep timeline.' diff --git a/config/locales/ja.yml b/config/locales/ja.yml index ebe7c3fe73..37944a42ac 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -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: パスワードリセットトークンが正しくないか期限切れです。もう一度リクエストしてください。 @@ -1384,6 +1385,7 @@ ja: view_strikes: 過去のストライクを表示 too_fast: フォームの送信が速すぎます。もう一度やり直してください。 use_security_key: セキュリティキーを使用 + with_login_options: カスタムCSSを無効化しますか? challenge: confirm: 続ける hint_html: 以後1時間はパスワードの再入力を求めません @@ -1782,6 +1784,7 @@ ja: too_few_options: は複数必要です too_many_options: は%{max}個までです preferences: + custom_css: カスタムCSS does_not_search: このサーバーでは全文検索機能を利用できません。代わりに、他のkmyblueサーバーであなたの投稿がこの設定に従って検索されます。 dtl: ディープタイムライン dtl_hint: "#%{tag} ハッシュタグに参加することで、ディープタイムラインに投稿できます。ここではディープタイムラインを利用しやすくするための設定ができます。" diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index 51f7c0ac8a..8a89b61a5e 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -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と同じ挙動になります @@ -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 @@ -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 diff --git a/config/locales/simple_form.ja.yml b/config/locales/simple_form.ja.yml index d5156b764e..16b44eba98 100644 --- a/config/locales/simple_form.ja.yml +++ b/config/locales/simple_form.ja.yml @@ -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と同じ挙動になります @@ -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の公開範囲 @@ -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: 重大性 diff --git a/config/navigation.rb b/config/navigation.rb index 9ace656bbc..87d6d7dbec 100644 --- a/config/navigation.rb +++ b/config/navigation.rb @@ -18,6 +18,7 @@ SimpleNavigation::Configuration.run do |navigation| 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 diff --git a/config/routes.rb b/config/routes.rb index 800118caba..00842a6cce 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -87,6 +87,7 @@ Rails.application.routes.draw do get 'manifest', to: 'manifests#show', defaults: { format: 'json' } get 'intent', to: 'intents#show' get 'custom.css', to: 'custom_css#show', as: :custom_css + get 'user_custom.css', to: 'user_custom_css#show', as: :user_custom_css get 'remote_interaction_helper', to: 'remote_interaction_helper#index' diff --git a/config/routes/settings.rb b/config/routes/settings.rb index 6018415fc1..ff5ac33bc7 100644 --- a/config/routes/settings.rb +++ b/config/routes/settings.rb @@ -11,6 +11,7 @@ namespace :settings do 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 diff --git a/db/migrate/20240828123604_create_custom_csses.rb b/db/migrate/20240828123604_create_custom_csses.rb new file mode 100644 index 0000000000..5455eb2b61 --- /dev/null +++ b/db/migrate/20240828123604_create_custom_csses.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class CreateCustomCsses < ActiveRecord::Migration[7.1] + def change + create_table :custom_csses do |t| + t.belongs_to :user, foreign_key: { on_delete: :cascade }, null: false + t.string :css, null: false, default: '' + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 0ddb1d4709..5e21d3b1dd 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2024_08_08_125420) do +ActiveRecord::Schema[7.1].define(version: 2024_08_28_123604) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -479,6 +479,14 @@ ActiveRecord::Schema[7.1].define(version: 2024_08_08_125420) do t.index ["uri"], name: "index_conversations_on_uri", unique: true, opclass: :text_pattern_ops, where: "(uri IS NOT NULL)" end + create_table "custom_csses", force: :cascade do |t| + t.bigint "user_id", null: false + t.string "css", default: "", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["user_id"], name: "index_custom_csses_on_user_id" + end + create_table "custom_emoji_categories", force: :cascade do |t| t.string "name" t.datetime "created_at", precision: nil, null: false @@ -1678,6 +1686,7 @@ ActiveRecord::Schema[7.1].define(version: 2024_08_08_125420) do add_foreign_key "circles", "accounts", on_delete: :cascade add_foreign_key "conversation_mutes", "accounts", name: "fk_225b4212bb", on_delete: :cascade add_foreign_key "conversation_mutes", "conversations", on_delete: :cascade + add_foreign_key "custom_csses", "users", on_delete: :cascade add_foreign_key "custom_filter_keywords", "custom_filters", on_delete: :cascade add_foreign_key "custom_filter_statuses", "custom_filters", on_delete: :cascade add_foreign_key "custom_filter_statuses", "statuses", on_delete: :cascade diff --git a/lib/tasks/dangerous.rake b/lib/tasks/dangerous.rake index c2ca637645..fcc2a7b52c 100644 --- a/lib/tasks/dangerous.rake +++ b/lib/tasks/dangerous.rake @@ -14,6 +14,7 @@ namespace :dangerous do end target_migrations = %w( + 20240828123604 20240709063700 20240426233435 20240426000034 @@ -113,6 +114,7 @@ namespace :dangerous do circles circle_accounts circle_statuses + custom_csses emoji_reactions friend_domains instance_infos diff --git a/spec/controllers/auth/sessions_controller_spec.rb b/spec/controllers/auth/sessions_controller_spec.rb index 9a94e5e1a1..5b39f97ae0 100644 --- a/spec/controllers/auth/sessions_controller_spec.rb +++ b/spec/controllers/auth/sessions_controller_spec.rb @@ -203,6 +203,45 @@ RSpec.describe Auth::SessionsController do 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 diff --git a/spec/requests/user_custom_css_spec.rb b/spec/requests/user_custom_css_spec.rb new file mode 100644 index 0000000000..a36afc0e6e --- /dev/null +++ b/spec/requests/user_custom_css_spec.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe 'User custom CSS' do + let(:user) { Fabricate(:user) } + let(:custom_css) { '* { display: none !important; }' } + + describe 'GET /user_custom.css' do + context 'without sign in' do + it 'returns 422' do + get '/user_custom.css' + + expect(response).to have_http_status(401) + end + end + + context 'with sign in but custom css is not enabled' do + before do + user.update!(custom_css_text: custom_css) + sign_in user + end + + it 'returns custom css' do + get '/user_custom.css' + + expect(response).to have_http_status(200) + expect(response.content_type).to include 'text/css' + expect(response.body.strip).to eq custom_css + end + end + + context 'with sign in and custom css is enabled' do + before do + user.update!(custom_css_text: custom_css, settings: { 'web.use_custom_css': true }) + sign_in user + end + + it 'returns custom css' do + get '/user_custom.css' + + expect(response).to have_http_status(200) + expect(response.content_type).to include 'text/css' + expect(response.body.strip).to eq custom_css + end + end + end +end