2
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2025-10-03 17:21:20 +08:00
This commit is contained in:
OsamaSayegh 2025-10-02 07:48:57 +03:00
parent 1cd420e115
commit a06e40fd05
No known key found for this signature in database
GPG key ID: 060E5AC82223685F
11 changed files with 235 additions and 146 deletions

View file

@ -31,11 +31,7 @@ class Admin::ColorSchemesController < Admin::AdminController
end
end

diverge_from_remote =
update_params[:colors].present? && @color_scheme.theme_id.present? &&
@color_scheme.base_scheme_id.blank?

color_scheme = ColorSchemeRevisor.revise(@color_scheme, update_params, diverge_from_remote:)
color_scheme = ColorSchemeRevisor.revise(@color_scheme, update_params)
update_theme_default_scheme!
if color_scheme.valid?
render json: color_scheme, root: false

View file

@ -349,6 +349,7 @@ class ColorScheme < ActiveRecord::Base
belongs_to :theme
belongs_to :base_scheme, -> { unscope(where: :remote_copy) }, class_name: "ColorScheme"

validate :no_edits_for_remote_copies, on: :update
validates_associated :color_scheme_colors

BASE_COLORS_FILE = "#{Rails.root}/app/assets/stylesheets/common/foundation/colors.scss"
@ -588,15 +589,19 @@ class ColorScheme < ActiveRecord::Base
DistributedMutex.synchronize("color_scheme_fork_#{self.id}") do
self.reload
if self.base_scheme_id.blank?
new_scheme.save!
self.base_scheme_id = new_scheme.id
self.save!
self.transaction do
new_scheme.save!
self.base_scheme_id = new_scheme.id
self.save!
end
end
end

self
end

private

def destroy_remote_original
return if theme_id.blank?
return if base_scheme_id.blank? || base_scheme_id < 0
@ -606,6 +611,16 @@ class ColorScheme < ActiveRecord::Base
.where(theme_id: theme_id, id: base_scheme_id, remote_copy: true)
.destroy_all
end

def no_edits_for_remote_copies
if remote_copy &&
(
will_save_change_to_base_scheme_id? || will_save_change_to_user_selectable? ||
will_save_change_to_remote_copy? || will_save_change_to_theme_id?
)
errors.add(:base, I18n.t("color_schemes.errors.cannot_edit_remote_copies"))
end
end
end

# == Schema Information

View file

@ -5,13 +5,22 @@ class ColorSchemeColor < ActiveRecord::Base
"dark_hex", # TODO: Remove when 20250821155127_drop_dark_hex_from_color_scheme_color has been promoted to pre-deploy
]

belongs_to :color_scheme
belongs_to :color_scheme, -> { unscope(where: :remote_copy) }

validates :hex, format: { with: /\A([0-9a-fA-F]{3}|[0-9a-fA-F]{6})\z/ }
validate :no_edits_for_remote_copies, on: :update

def hex_with_hash
"##{hex}"
end

private

def no_edits_for_remote_copies
if color_scheme&.remote_copy && will_save_change_to_hex?
errors.add(:base, I18n.t("color_schemes.errors.cannot_edit_remote_copies"))
end
end
end

# == Schema Information

View file

@ -420,7 +420,11 @@ class RemoteTheme < ActiveRecord::Base
existing_schemes = ColorScheme.unscoped.where(theme_id: theme.id)

missing_scheme_names =
Hash[*existing_schemes.reject(&:remote_copy).map { |cs| [cs.name, cs] }.flatten]
existing_schemes.reduce({}) do |hash, cs|
hash[cs.name] = cs if !cs.remote_copy
hash
end

ordered_schemes = []

schemes&.each do |name, colors|

View file

@ -6,11 +6,11 @@ class ColorSchemeRevisor
@params = params
end

def self.revise(color_scheme, params, diverge_from_remote: false)
self.new(color_scheme, params).revise(diverge_from_remote:)
def self.revise(color_scheme, params)
self.new(color_scheme, params).revise
end

def revise(diverge_from_remote: false)
def revise
ColorScheme.transaction do
@color_scheme.name = @params[:name] if @params.has_key?(:name)
@color_scheme.user_selectable = @params[:user_selectable] if @params.has_key?(
@ -20,7 +20,10 @@ class ColorSchemeRevisor
@color_scheme.base_scheme_id = @params[:base_scheme_id] if @params.has_key?(:base_scheme_id)
has_colors = @params[:colors]

@color_scheme.diverge_from_remote if diverge_from_remote
if @params[:colors].present? && @color_scheme.theme_id.present? &&
@color_scheme.base_scheme_id.blank?
@color_scheme.diverge_from_remote
end

if has_colors
@params[:colors].each do |c|

View file

@ -4805,6 +4805,8 @@ en:
latte_theme_name: "Latte"
summer_theme_name: "Summer"
dark_rose_theme_name: "Dark Rose"
errors:
cannot_edit_remote_copies: "Edits to remote copies of color palettes are not allowed."

edit_this_page: "Edit this page"


View file

@ -0,0 +1,152 @@
# frozen_string_literal: true

RSpec.describe "Remote theme update" do
let :about_json do
<<~JSON
{
"name": "awesome theme",
"about_url": "https://www.site.com/about",
"license_url": "https://www.site.com/license",
"theme_version": "1.0",
"minimum_discourse_version": "1.0.0",
"assets": {
"font": "assets/font.woff2"
},
"color_schemes": {
"Amazing": {
"love": "FAFAFA",
"tertiary-low": "FFFFFF"
}
}
}
JSON
end

let :initial_repo do
setup_git_repo("about.json" => about_json)
end

let :initial_repo_url do
MockGitImporter.register("https://example.com/initial_repo.git", initial_repo)
end

after { `rm -fr #{initial_repo}` }

around(:each) { |group| MockGitImporter.with_mock { group.run } }

it "updates the base schemes for schemes that have diverged colors" do
add_to_git_repo(
initial_repo,
"about.json" =>
JSON
.parse(about_json)
.merge(
color_schemes: {
scheme1: {
love: "FFFFFF",
tertiary_low: "000000",
},
scheme2: {
love: "AACCDD",
tertiary_low: "99AAFF",
},
},
)
.to_json,
)

theme = RemoteTheme.import_theme(initial_repo_url)
scheme1 = theme.color_schemes.find_by(name: "scheme1")
scheme2 = theme.color_schemes.find_by(name: "scheme2")
expect(scheme1.base_scheme_id).to eq(nil)
expect(scheme2.base_scheme_id).to eq(nil)

ColorSchemeRevisor.revise(scheme1, { colors: [{ name: "love", hex: "111111" }] })

scheme1_base_scheme_id = scheme1.reload.base_scheme_id
expect(scheme1_base_scheme_id).to be_present
expect(scheme1.colors.find_by(name: "love").hex).to eq("111111")
expect(
ColorScheme.unscoped.find(scheme1_base_scheme_id).colors.find_by(name: "love").hex,
).to eq("ffffff")
expect(scheme2.base_scheme_id).to eq(nil)

add_to_git_repo(
initial_repo,
"about.json" =>
JSON
.parse(about_json)
.merge(
color_schemes: {
scheme1: {
love: "EEEEEE",
tertiary_low: "000000",
},
scheme2: {
love: "AACCDD",
tertiary_low: "99AAFF",
},
},
)
.to_json,
)
theme.remote_theme.update_from_remote

scheme1.reload
expect(scheme1.base_scheme_id).to eq(scheme1_base_scheme_id)
expect(scheme1.colors.find_by(name: "love").hex).to eq("111111")
expect(
ColorScheme.unscoped.find(scheme1_base_scheme_id).colors.find_by(name: "love").hex,
).to eq("eeeeee")
expect(scheme2.base_scheme_id).to eq(nil)
end

it "deletes color schemes that haven't been modified and deletes the bases of schemes that have been modified" do
add_to_git_repo(
initial_repo,
"about.json" =>
JSON
.parse(about_json)
.merge(
color_schemes: {
scheme1: {
love: "FFFFFF",
tertiary_low: "000000",
},
scheme2: {
love: "AACCDD",
tertiary_low: "99AAFF",
},
},
)
.to_json,
)

theme = RemoteTheme.import_theme(initial_repo_url)

scheme1 = theme.color_schemes.find_by(name: "scheme1")
scheme2 = theme.color_schemes.find_by(name: "scheme2")

expect do
ColorSchemeRevisor.revise(scheme1, { colors: [{ name: "love", hex: "111111" }] })
end.to change { ColorScheme.unscoped.count }.by(1)

expect(scheme1.reload.base_scheme_id).to be_present
expect(scheme2.reload.base_scheme_id).to be_blank

scheme1_base_scheme_id = scheme1.base_scheme_id

add_to_git_repo(
initial_repo,
"about.json" => JSON.parse(about_json).merge(color_schemes: {}).to_json,
)

expect do theme.remote_theme.update_from_remote end.to change { ColorScheme.unscoped.count }.by(
-2,
)

expect(ColorScheme.unscoped.exists?(id: scheme1_base_scheme_id)).to eq(false)
expect(ColorScheme.unscoped.exists?(id: scheme1.id)).to eq(true)
expect(ColorScheme.unscoped.exists?(id: scheme2.id)).to eq(false)
end
end

View file

@ -26,4 +26,24 @@ RSpec.describe ColorSchemeColor do
"555 666",
].each { |hex| test_invalid_hex(hex) }
end

describe "#no_edits_for_remote_copies" do
it "prevents editing colors of remote copies" do
remote_copy =
Fabricate(
:color_scheme,
remote_copy: true,
color_scheme_colors: [
Fabricate(:color_scheme_color, name: "primary", hex: "998877"),
Fabricate(:color_scheme_color, name: "secondary", hex: "553322"),
],
)
color = remote_copy.color_scheme_colors.first
color.hex = "111111"
expect(color.valid?).to eq(false)
expect(color.errors.full_messages).to include(
I18n.t("color_schemes.errors.cannot_edit_remote_copies"),
)
end
end
end

View file

@ -214,9 +214,10 @@ RSpec.describe ColorScheme do
expect(original_scheme.base_scheme.colors.map { |c| [c.name, c.hex] }.sort_by(&:first)).to eq(
original_scheme.colors.map { |c| [c.name, c.hex] }.sort_by(&:first),
)
expect(original_scheme.base_scheme.theme.id).to eq(theme.id)
expect(original_scheme.base_scheme.theme_id).to eq(theme.id)
expect(original_scheme.base_scheme.user_selectable).to eq(false)
expect(original_scheme.base_scheme.remote_copy).to eq(true)
expect(original_scheme.base_scheme.via_wizard).to eq(false)
end
end

@ -239,4 +240,21 @@ RSpec.describe ColorScheme do
expect(ColorScheme.unscoped.exists?(id: original_scheme.base_scheme_id)).to eq(false)
end
end

describe "#no_edits_for_remote_copies" do
it "prevents editing remote copies of color schemes" do
remote_copy =
Fabricate(
:color_scheme,
remote_copy: true,
user_selectable: false,
theme_id: Fabricate(:theme).id,
)
remote_copy.user_selectable = true
expect(remote_copy.valid?).to eq(false)
expect(remote_copy.errors.full_messages).to include(
I18n.t("color_schemes.errors.cannot_edit_remote_copies"),
)
end
end
end

View file

@ -468,136 +468,6 @@ RSpec.describe RemoteTheme do
end
end

describe "color schemes" do
it "updates the base schemes for schemes that have diverged colors" do
add_to_git_repo(
initial_repo,
"about.json" =>
JSON
.parse(about_json)
.merge(
color_schemes: {
scheme1: {
love: "FFFFFF",
tertiary_low: "000000",
},
scheme2: {
love: "AACCDD",
tertiary_low: "99AAFF",
},
},
)
.to_json,
)

theme = RemoteTheme.import_theme(initial_repo_url)
scheme1 = theme.color_schemes.find_by(name: "scheme1")
scheme2 = theme.color_schemes.find_by(name: "scheme1")
expect(scheme1.base_scheme_id).to eq(nil)
expect(scheme2.base_scheme_id).to eq(nil)

expect do
ColorSchemeRevisor.revise(
scheme1,
{ colors: [{ name: "love", hex: "111111" }] },
diverge_from_remote: true,
)
end.to change { ColorScheme.unscoped.count }.by(1)

scheme1_base_scheme_id = scheme1.reload.base_scheme_id
expect(scheme1_base_scheme_id).to be_present
expect(scheme1.colors.find_by(name: "love").hex).to eq("111111")
expect(
ColorScheme.unscoped.find(scheme1_base_scheme_id).colors.find_by(name: "love").hex,
).to eq("ffffff")
expect(scheme2.base_scheme_id).to eq(nil)

add_to_git_repo(
initial_repo,
"about.json" =>
JSON
.parse(about_json)
.merge(
color_schemes: {
scheme1: {
love: "EEEEEE",
tertiary_low: "000000",
},
scheme2: {
love: "AACCDD",
tertiary_low: "99AAFF",
},
},
)
.to_json,
)
theme.remote_theme.update_from_remote

scheme1.reload
expect(scheme1.base_scheme_id).to eq(scheme1_base_scheme_id)
expect(scheme1.colors.find_by(name: "love").hex).to eq("111111")
expect(
ColorScheme.unscoped.find(scheme1_base_scheme_id).colors.find_by(name: "love").hex,
).to eq("eeeeee")
expect(scheme2.base_scheme_id).to eq(nil)
end

it "deletes color schemes that haven't been modified and deletes the bases of schemes that have been modified" do
add_to_git_repo(
initial_repo,
"about.json" =>
JSON
.parse(about_json)
.merge(
color_schemes: {
scheme1: {
love: "FFFFFF",
tertiary_low: "000000",
},
scheme2: {
love: "AACCDD",
tertiary_low: "99AAFF",
},
},
)
.to_json,
)

theme = RemoteTheme.import_theme(initial_repo_url)

expect(theme.color_schemes.pluck(:name)).to contain_exactly("scheme1", "scheme2")

scheme1 = theme.color_schemes.find_by(name: "scheme1")
scheme2 = theme.color_schemes.find_by(name: "scheme2")

expect do
ColorSchemeRevisor.revise(
scheme1,
{ colors: [{ name: "love", hex: "111111" }] },
diverge_from_remote: true,
)
end.to change { ColorScheme.unscoped.count }.by(1)

expect(scheme1.reload.base_scheme_id).to be_present
expect(scheme2.reload.base_scheme_id).to be_blank

scheme1_base_scheme_id = scheme1.base_scheme_id

add_to_git_repo(
initial_repo,
"about.json" => JSON.parse(about_json).merge(color_schemes: {}).to_json,
)

expect do theme.remote_theme.update_from_remote end.to change {
ColorScheme.unscoped.count
}.by(-2)

expect(ColorScheme.unscoped.exists?(id: scheme1_base_scheme_id)).to eq(false)
expect(ColorScheme.unscoped.exists?(id: scheme1.id)).to eq(true)
expect(ColorScheme.unscoped.exists?(id: scheme2.id)).to eq(false)
end
end

describe "theme site settings" do
it "creates theme site settings defined in about.json" do
add_to_git_repo(

View file

@ -201,7 +201,7 @@ RSpec.describe Admin::ColorSchemesController do
colors: [{ name: "primary", hex: "7711EE" }],
},
}
end.to change { ColorScheme.unscoped.count }.by(1)
end.to change { ColorScheme.unscoped.where(remote_copy: true).count }.by(1)
expect(response.status).to eq(200)
expect(response.parsed_body["colors"].find { |c| c["name"] == "primary" }["hex"]).to eq(
"7711EE",