mirror of
https://github.com/discourse/discourse.git
synced 2024-12-16 07:01:00 +08:00
e0cc29d658
* FEATURE: themes and components split * two seperate methods to switch theme type * use strict equality operator
294 lines
7.8 KiB
Ruby
294 lines
7.8 KiB
Ruby
require_dependency 'upload_creator'
|
|
require 'base64'
|
|
|
|
class Admin::ThemesController < Admin::AdminController
|
|
|
|
skip_before_action :check_xhr, only: [:show, :preview]
|
|
|
|
def preview
|
|
@theme = Theme.find(params[:id])
|
|
redirect_to path("/?preview_theme_id=#{@theme.id}")
|
|
end
|
|
|
|
def upload_asset
|
|
path = params[:file].path
|
|
|
|
hijack do
|
|
File.open(path) do |file|
|
|
filename = params[:file]&.original_filename || File.basename(path)
|
|
upload = UploadCreator.new(file, filename, for_theme: true).create_for(current_user.id)
|
|
if upload.errors.count > 0
|
|
render_json_error upload
|
|
else
|
|
render json: { upload_id: upload.id }, status: :created
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
def generate_key_pair
|
|
require 'sshkey'
|
|
k = SSHKey.generate
|
|
|
|
render json: {
|
|
private_key: k.private_key,
|
|
public_key: k.ssh_public_key
|
|
}
|
|
end
|
|
|
|
def import
|
|
@theme = nil
|
|
if params[:theme]
|
|
json = JSON::parse(params[:theme].read)
|
|
theme = json['theme']
|
|
|
|
@theme = Theme.new(name: theme["name"], user_id: current_user.id)
|
|
theme["theme_fields"]&.each do |field|
|
|
|
|
if field["raw_upload"]
|
|
begin
|
|
tmp = Tempfile.new
|
|
tmp.binmode
|
|
file = Base64.decode64(field["raw_upload"])
|
|
tmp.write(file)
|
|
tmp.rewind
|
|
upload = UploadCreator.new(tmp, field["filename"]).create_for(current_user.id)
|
|
field["upload_id"] = upload.id
|
|
ensure
|
|
tmp.unlink
|
|
end
|
|
end
|
|
|
|
@theme.set_field(
|
|
target: field["target"],
|
|
name: field["name"],
|
|
value: field["value"],
|
|
type_id: field["type_id"],
|
|
upload_id: field["upload_id"]
|
|
)
|
|
end
|
|
|
|
if @theme.save
|
|
log_theme_change(nil, @theme)
|
|
render json: @theme, status: :created
|
|
else
|
|
render json: @theme.errors, status: :unprocessable_entity
|
|
end
|
|
elsif params[:remote]
|
|
begin
|
|
@theme = RemoteTheme.import_theme(params[:remote], current_user, private_key: params[:private_key])
|
|
render json: @theme, status: :created
|
|
rescue RuntimeError => e
|
|
Discourse.warn_exception(e, message: "Error importing theme")
|
|
render_json_error I18n.t('themes.error_importing')
|
|
end
|
|
elsif params[:bundle]
|
|
begin
|
|
@theme = RemoteTheme.update_tgz_theme(params[:bundle].path, user: current_user)
|
|
render json: @theme, status: :created
|
|
rescue RuntimeError
|
|
render_json_error I18n.t('themes.error_importing')
|
|
end
|
|
else
|
|
render json: @theme.errors, status: :unprocessable_entity
|
|
end
|
|
end
|
|
|
|
def index
|
|
@themes = Theme.order(:name).includes(:theme_fields, :remote_theme)
|
|
@color_schemes = ColorScheme.all.to_a
|
|
light = ColorScheme.new(name: I18n.t("color_schemes.light"))
|
|
@color_schemes.unshift(light)
|
|
|
|
payload = {
|
|
themes: ActiveModel::ArraySerializer.new(@themes, each_serializer: ThemeSerializer),
|
|
extras: {
|
|
color_schemes: ActiveModel::ArraySerializer.new(@color_schemes, each_serializer: ColorSchemeSerializer)
|
|
}
|
|
}
|
|
|
|
respond_to do |format|
|
|
format.json { render json: payload }
|
|
end
|
|
end
|
|
|
|
def create
|
|
@theme = Theme.new(name: theme_params[:name],
|
|
user_id: current_user.id,
|
|
user_selectable: theme_params[:user_selectable] || false,
|
|
color_scheme_id: theme_params[:color_scheme_id],
|
|
component: [true, "true"].include?(theme_params[:component]))
|
|
set_fields
|
|
|
|
respond_to do |format|
|
|
if @theme.save
|
|
update_default_theme
|
|
log_theme_change(nil, @theme)
|
|
format.json { render json: @theme, status: :created }
|
|
else
|
|
format.json { render json: @theme.errors, status: :unprocessable_entity }
|
|
end
|
|
end
|
|
end
|
|
|
|
def update
|
|
@theme = Theme.find(params[:id])
|
|
|
|
original_json = ThemeSerializer.new(@theme, root: false).to_json
|
|
|
|
[:name, :color_scheme_id, :user_selectable].each do |field|
|
|
if theme_params.key?(field)
|
|
@theme.send("#{field}=", theme_params[field])
|
|
end
|
|
end
|
|
|
|
if theme_params.key?(:child_theme_ids)
|
|
expected = theme_params[:child_theme_ids].map(&:to_i)
|
|
|
|
@theme.child_theme_relation.to_a.each do |child|
|
|
if expected.include?(child.child_theme_id)
|
|
expected.reject! { |id| id == child.child_theme_id }
|
|
else
|
|
child.destroy
|
|
end
|
|
end
|
|
|
|
Theme.where(id: expected).each do |theme|
|
|
@theme.add_child_theme!(theme)
|
|
end
|
|
end
|
|
|
|
set_fields
|
|
update_settings
|
|
handle_switch
|
|
|
|
save_remote = false
|
|
if params[:theme][:remote_check]
|
|
@theme.remote_theme.update_remote_version
|
|
save_remote = true
|
|
end
|
|
|
|
if params[:theme][:remote_update]
|
|
@theme.remote_theme.update_from_remote
|
|
save_remote = true
|
|
end
|
|
|
|
respond_to do |format|
|
|
if @theme.save
|
|
|
|
@theme.remote_theme.save! if save_remote
|
|
|
|
update_default_theme
|
|
|
|
log_theme_change(original_json, @theme)
|
|
format.json { render json: @theme, status: :ok }
|
|
else
|
|
format.json do
|
|
error = @theme.errors.full_messages.join(", ").presence
|
|
error = I18n.t("themes.bad_color_scheme") if @theme.errors[:color_scheme].present?
|
|
error ||= I18n.t("themes.other_error")
|
|
|
|
render json: { errors: [ error ] }, status: :unprocessable_entity
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
def destroy
|
|
@theme = Theme.find(params[:id])
|
|
StaffActionLogger.new(current_user).log_theme_destroy(@theme)
|
|
@theme.destroy
|
|
|
|
respond_to do |format|
|
|
format.json { head :no_content }
|
|
end
|
|
end
|
|
|
|
def show
|
|
@theme = Theme.find(params[:id])
|
|
|
|
respond_to do |format|
|
|
format.json do
|
|
check_xhr
|
|
render json: ThemeSerializer.new(@theme)
|
|
end
|
|
|
|
format.any(:html, :text) do
|
|
raise RenderEmpty.new if request.xhr?
|
|
|
|
response.headers['Content-Disposition'] = "attachment; filename=#{@theme.name.parameterize}.dcstyle.json"
|
|
response.sending_file = true
|
|
render json: ::ThemeWithEmbeddedUploadsSerializer.new(@theme, root: 'theme')
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
private
|
|
|
|
def update_default_theme
|
|
if theme_params.key?(:default)
|
|
is_default = theme_params[:default].to_s == "true"
|
|
if @theme.id == SiteSetting.default_theme_id && !is_default
|
|
Theme.clear_default!
|
|
elsif is_default
|
|
@theme.set_default!
|
|
end
|
|
end
|
|
end
|
|
|
|
def theme_params
|
|
@theme_params ||=
|
|
begin
|
|
# deep munge is a train wreck, work around it for now
|
|
params[:theme][:child_theme_ids] ||= [] if params[:theme].key?(:child_theme_ids)
|
|
|
|
params.require(:theme).permit(
|
|
:name,
|
|
:color_scheme_id,
|
|
:default,
|
|
:user_selectable,
|
|
:component,
|
|
settings: {},
|
|
theme_fields: [:name, :target, :value, :upload_id, :type_id],
|
|
child_theme_ids: []
|
|
)
|
|
end
|
|
end
|
|
|
|
def set_fields
|
|
return unless fields = theme_params[:theme_fields]
|
|
|
|
fields.each do |field|
|
|
@theme.set_field(
|
|
target: field[:target],
|
|
name: field[:name],
|
|
value: field[:value],
|
|
type_id: field[:type_id],
|
|
upload_id: field[:upload_id]
|
|
)
|
|
end
|
|
end
|
|
|
|
def update_settings
|
|
return unless target_settings = theme_params[:settings]
|
|
|
|
target_settings.each_pair do |setting_name, new_value|
|
|
@theme.update_setting(setting_name.to_sym, new_value)
|
|
end
|
|
end
|
|
|
|
def log_theme_change(old_record, new_record)
|
|
StaffActionLogger.new(current_user).log_theme_change(old_record, new_record)
|
|
end
|
|
|
|
def handle_switch
|
|
param = theme_params[:component]
|
|
if param.to_s == "false" && @theme.component?
|
|
@theme.switch_to_theme!
|
|
elsif param.to_s == "true" && !@theme.component?
|
|
@theme.switch_to_component!
|
|
end
|
|
end
|
|
end
|