FEATURE: Upload Site Settings. (#6573)

This commit is contained in:
Guo Xiang Tan 2018-11-14 15:03:02 +08:00 committed by GitHub
parent 17bc82765b
commit 44391ee8ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
59 changed files with 892 additions and 244 deletions

View File

@ -0,0 +1,6 @@
import ImageUploader from "discourse/components/image-uploader";
export default ImageUploader.extend({
layoutName: "components/image-uploader",
uploadUrlParams: "&for_site_setting=true"
});

View File

@ -12,7 +12,8 @@ const CUSTOM_TYPES = [
"category",
"uploaded_image_list",
"compact_list",
"secret_list"
"secret_list",
"upload"
];
export default Ember.Mixin.create({

View File

@ -0,0 +1,2 @@
{{site-settings-image-uploader imageUrl=value type="site_setting"}}
<div class='desc'>{{{unbound setting.description}}}</div>

View File

@ -62,7 +62,7 @@ const Discourse = Ember.Application.extend({
@observes("notifyCount")
faviconChanged() {
if (Discourse.User.currentProp("dynamic_favicon")) {
let url = Discourse.SiteSettings.favicon_url;
let url = Discourse.SiteSettings.site_favicon_url;
if (/^http/.test(url)) {
url = Discourse.getURL("/favicon/proxied?" + encodeURIComponent(url));
}

View File

@ -70,8 +70,8 @@ function confirmNotification() {
{
body: I18n.t("notifications.popup.confirm_body"),
icon:
Discourse.SiteSettings.logo_small_url ||
Discourse.SiteSettings.logo_url,
Discourse.SiteSettings.site_logo_small_url ||
Discourse.SiteSettings.site_logo_url,
tag: "confirm-subscription"
}
);
@ -150,8 +150,11 @@ function onNotification(data) {
});
const notificationBody = data.excerpt;
const notificationIcon =
Discourse.SiteSettings.logo_small_url || Discourse.SiteSettings.logo_url;
Discourse.SiteSettings.site_logo_small_url ||
Discourse.SiteSettings.site_logo_url;
const notificationTag =
"discourse-notification-" +
Discourse.SiteSettings.title +

View File

@ -22,10 +22,13 @@ export default Em.Mixin.create({
".json?client_id=" +
(this.messageBus && this.messageBus.clientId) +
"&authenticity_token=" +
encodeURIComponent(Discourse.Session.currentProp("csrfToken"))
encodeURIComponent(Discourse.Session.currentProp("csrfToken")) +
this.uploadUrlParams
);
},
uploadUrlParams: "",
uploadOptions() {
return {};
},

View File

@ -20,14 +20,14 @@ export default createWidget("home-logo", {
const { siteSettings } = this;
const mobileView = this.site.mobileView;
const mobileLogoUrl = siteSettings.mobile_logo_url || "";
const mobileLogoUrl = siteSettings.site_mobile_logo_url || "";
const showMobileLogo = mobileView && mobileLogoUrl.length > 0;
const logoUrl = siteSettings.logo_url || "";
const logoUrl = siteSettings.site_logo_url || "";
const title = siteSettings.title;
if (!mobileView && this.attrs.minimized) {
const logoSmallUrl = siteSettings.logo_small_url || "";
const logoSmallUrl = siteSettings.site_logo_small_url || "";
if (logoSmallUrl.length) {
return h("img#site-logo.logo-small", {
key: "logo-small",

View File

@ -37,6 +37,10 @@
width: 100% !important; // !important overrides hard-coded mobile width of 68px
}
}
.uploaded-image-preview {
background-size: auto;
background-repeat: no-repeat;
}
}
.setting-controls {
float: left;

View File

@ -13,6 +13,11 @@ class Admin::SiteSettingsController < Admin::AdminController
value = params[id]
value.strip! if value.is_a?(String)
raise_access_hidden_setting(id)
if SiteSetting.type_supervisor.get_type(id) == :upload
value = Upload.get_from_url(value) || ''
end
SiteSetting.set_and_log(id, value, current_user)
render body: nil
end

View File

@ -13,10 +13,14 @@ class MetadataController < ApplicationController
private
def default_manifest
logo = SiteSetting.large_icon_url.presence || SiteSetting.logo_small_url.presence || SiteSetting.apple_touch_icon_url.presence
logo = SiteSetting.site_large_icon_url.presence ||
SiteSetting.site_logo_small_url.presence ||
SiteSetting.site_apple_touch_icon_url.presence
if !logo
logo = path('/images/d-logo-sketch-small.png')
logo = '/images/d-logo-sketch-small.png'
end
file_info = get_file_info(logo)
display = Regexp.new(SiteSetting.pwa_display_browser_regex).match(request.user_agent) ? 'browser' : 'standalone'
@ -30,7 +34,7 @@ class MetadataController < ApplicationController
theme_color: "##{ColorScheme.hex_for_name('header_background', view_context.scheme_id)}",
icons: [
{
src: logo,
src: UrlHelper.absolute(logo),
sizes: file_info[:size],
type: file_info[:type]
}

View File

@ -37,16 +37,19 @@ class SiteController < ApplicationController
def basic_info
results = {
logo_url: UrlHelper.absolute(SiteSetting.logo_url),
logo_small_url: UrlHelper.absolute(SiteSetting.logo_small_url),
apple_touch_icon_url: UrlHelper.absolute(SiteSetting.apple_touch_icon_url),
favicon_url: UrlHelper.absolute(SiteSetting.favicon_url),
logo_url: UrlHelper.absolute(SiteSetting.site_logo_url),
logo_small_url: UrlHelper.absolute(SiteSetting.site_logo_small_url),
apple_touch_icon_url: UrlHelper.absolute(SiteSetting.site_apple_touch_icon_url),
favicon_url: UrlHelper.absolute(SiteSetting.site_favicon_url),
title: SiteSetting.title,
description: SiteSetting.site_description,
header_primary_color: ColorScheme.hex_for_name('header_primary') || '333333',
header_background_color: ColorScheme.hex_for_name('header_background') || 'ffffff'
}
results[:mobile_logo_url] = SiteSetting.mobile_logo_url.presence
if mobile_logo_url = SiteSetting.site_mobile_logo_url.presence
results[:mobile_logo_url] = UrlHelper.absolute(mobile_logo_url)
end
DiscourseHub.stats_fetched_at = Time.zone.now if request.user_agent == "Discourse Hub"

View File

@ -108,10 +108,10 @@ class StaticController < ApplicationController
is_asset_path
hijack do
data = DistributedMemoizer.memoize(FAVICON + SiteSetting.favicon_url, 60 * 30) do
data = DistributedMemoizer.memoize(FAVICON + SiteSetting.site_favicon_url, 60 * 30) do
begin
file = FileHelper.download(
SiteSetting.favicon_url,
UrlHelper.absolute(SiteSetting.site_favicon_url),
max_file_size: 50.kilobytes,
tmp_file_name: FAVICON,
follow_redirect: true
@ -122,7 +122,7 @@ class StaticController < ApplicationController
data
rescue => e
AdminDashboardData.add_problem_message('dashboard.bad_favicon_url', 1800)
Rails.logger.debug("Invalid favicon_url #{SiteSetting.favicon_url}: #{e}\n#{e.backtrace}")
Rails.logger.debug("Invalid favicon_url #{SiteSetting.site_favicon_url}: #{e}\n#{e.backtrace}")
""
end
end

View File

@ -21,6 +21,7 @@ class UploadsController < ApplicationController
file = params[:file] || params[:files]&.first
pasted = params[:pasted] == "true"
for_private_message = params[:for_private_message] == "true"
for_site_setting = params[:for_site_setting] == "true"
is_api = is_api?
retain_hours = params[:retain_hours].to_i
@ -34,6 +35,7 @@ class UploadsController < ApplicationController
url: url,
type: type,
for_private_message: for_private_message,
for_site_setting: for_site_setting,
pasted: pasted,
is_api: is_api,
retain_hours: retain_hours
@ -93,7 +95,16 @@ class UploadsController < ApplicationController
serialized ||= (data || {}).as_json
end
def self.create_upload(current_user:, file:, url:, type:, for_private_message:, pasted:, is_api:, retain_hours:)
def self.create_upload(current_user:,
file:,
url:,
type:,
for_private_message:,
for_site_setting:,
pasted:,
is_api:,
retain_hours:)
if file.nil?
if url.present? && is_api
maximum_upload_size = [SiteSetting.max_image_size_kb, SiteSetting.max_attachment_size_kb].max.kilobytes
@ -114,6 +125,7 @@ class UploadsController < ApplicationController
opts = {
type: type,
for_private_message: for_private_message,
for_site_setting: for_site_setting,
pasted: pasted,
}

View File

@ -192,11 +192,16 @@ module ApplicationHelper
opts ||= {}
opts[:url] ||= "#{Discourse.base_url_no_prefix}#{request.fullpath}"
if opts[:image].blank? && (SiteSetting.default_opengraph_image_url.present? || SiteSetting.twitter_summary_large_image_url.present?)
opts[:twitter_summary_large_image] = SiteSetting.twitter_summary_large_image_url if SiteSetting.twitter_summary_large_image_url.present?
opts[:image] = SiteSetting.default_opengraph_image_url.present? ? SiteSetting.default_opengraph_image_url : SiteSetting.twitter_summary_large_image_url
elsif opts[:image].blank? && SiteSetting.apple_touch_icon_url.present?
opts[:image] = SiteSetting.apple_touch_icon_url
twitter_summary_large_image_url =
SiteSetting.site_twitter_summary_large_image_url
opengraph_image_url = SiteSetting.opengraph_image_url
if opts[:image].blank? && (opengraph_image_url.present? || twitter_summary_large_image_url.present?)
opts[:twitter_summary_large_image] = twitter_summary_large_image_url if twitter_summary_large_image_url.present?
opts[:image] = opengraph_image_url.present? ? opengraph_image_url : twitter_summary_large_image_url
elsif opts[:image].blank? && SiteSetting.site_apple_touch_icon_url.present?
opts[:image] = SiteSetting.site_apple_touch_icon_url
end
# Use the correct scheme for open graph image
@ -271,7 +276,7 @@ module ApplicationHelper
end
def application_logo_url
@application_logo_url ||= (mobile_view? && SiteSetting.mobile_logo_url).presence || SiteSetting.logo_url
@application_logo_url ||= (mobile_view? && SiteSetting.site_mobile_logo_url).presence || SiteSetting.site_logo_url
end
def login_path

View File

@ -18,13 +18,14 @@ module UserNotificationsHelper
end
def logo_url
logo_url = SiteSetting.digest_logo_url
logo_url = SiteSetting.logo_url if logo_url.blank? || logo_url =~ /\.svg$/i
logo_url = SiteSetting.site_digest_logo_url
logo_url = SiteSetting.site_logo_url if logo_url.blank? || logo_url =~ /\.svg$/i
return nil if logo_url.blank? || logo_url =~ /\.svg$/i
if logo_url !~ /http(s)?\:\/\//
logo_url = "#{Discourse.base_url}#{logo_url}"
end
logo_url
end

View File

@ -46,6 +46,11 @@ module Jobs
result = Upload.where("uploads.retain_hours IS NULL OR uploads.created_at < current_timestamp - interval '1 hour' * uploads.retain_hours")
.where("uploads.created_at < ?", grace_period.hour.ago)
.joins(<<~SQL)
LEFT JOIN site_settings ss
ON ss.value::integer = uploads.id
AND ss.data_type = #{SiteSettings::TypeSupervisor.types[:upload].to_i}
SQL
.joins("LEFT JOIN post_uploads pu ON pu.upload_id = uploads.id")
.joins("LEFT JOIN users u ON u.uploaded_avatar_id = uploads.id")
.joins("LEFT JOIN user_avatars ua ON ua.gravatar_upload_id = uploads.id OR ua.custom_upload_id = uploads.id")
@ -62,6 +67,7 @@ module Jobs
.where("ce.upload_id IS NULL")
.where("tf.upload_id IS NULL")
.where("ue.upload_id IS NULL")
.where("ss.value IS NULL")
result = result.where("uploads.url NOT IN (?)", ignore_urls) if ignore_urls.present?

View File

@ -168,6 +168,55 @@ class SiteSetting < ActiveRecord::Base
SiteSetting::Upload
end
%i{
site_logo_url
site_logo_small_url
site_mobile_logo_url
site_favicon_url
}.each { |client_setting| client_settings << client_setting }
def self.site_logo_url
self.logo&.url || self.logo_url
end
def self.site_logo_small_url
self.logo_small&.url || self.logo_small_url
end
def self.site_digest_logo_url
self.digest_logo&.url || self.digest_logo_url
end
def self.site_mobile_logo_url
self.mobile_logo&.url || self.mobile_logo_url
end
def self.site_large_icon_url
self.large_icon&.url || self.large_icon_url
end
def self.site_favicon_url
self.favicon&.url || self.favicon_url
end
def self.site_apple_touch_icon_url
self.apple_touch_icon&.url || self.apple_touch_icon_url
end
def self.opengraph_image_url
self.opengraph_image&.url || self.default_opengraph_image_url
end
def self.site_twitter_summary_large_image_url
self.twitter_summary_large_image&.url ||
self.twitter_summary_large_image_url
end
def self.site_push_notifications_icon_url
SiteSetting.push_notifications_icon&.url ||
SiteSetting.push_notifications_icon_url
end
def self.shared_drafts_enabled?
c = SiteSetting.shared_drafts_category
c.present? && c.to_i != SiteSetting.uncategorized_category_id.to_i

View File

@ -21,6 +21,7 @@ class Upload < ActiveRecord::Base
attr_accessor :for_theme
attr_accessor :for_private_message
attr_accessor :for_export
attr_accessor :for_site_setting
validates_presence_of :filesize
validates_presence_of :original_filename
@ -33,6 +34,10 @@ class Upload < ActiveRecord::Base
UserAvatar.where(custom_upload_id: self.id).update_all(custom_upload_id: nil)
end
def to_s
self.url
end
def thumbnail(width = self.thumbnail_width, height = self.thumbnail_height)
optimized_images.find_by(width: width, height: height)
end

View File

@ -65,9 +65,11 @@ class PushNotificationPusher
protected
def self.get_badge
return !SiteSetting.push_notifications_icon_url.blank? ?
SiteSetting.push_notifications_icon_url :
ActionController::Base.helpers.image_url("push-notifications/discourse.png")
if SiteSetting.site_push_notifications_icon_url.present?
SiteSetting.site_push_notifications_icon_url
else
ActionController::Base.helpers.image_url("push-notifications/discourse.png")
end
end
def self.send_notification(user, subscription, message)

View File

@ -40,7 +40,7 @@
<% if @topic_view.topic.posts_count > 0 %>
<footer class="clearfix">
<%= link_to(image_tag(SiteSetting.logo_url, class: 'logo'), Discourse.base_url, target: '_blank') %>
<%= link_to(image_tag(SiteSetting.site_logo_url, class: 'logo'), Discourse.base_url, target: '_blank') %>
<%= link_to(I18n.t('embed.continue'), @topic_view.posts.last.full_url, class: 'button', target: '_blank') %>
<%- if @posts_left > 0 %>
<span class='replies'><%= I18n.t('embed.more_replies', count: @posts_left) %></span>

View File

@ -1,6 +1,6 @@
<header class='discourse' data-embed-state='error'>
<h3><%= t 'embed.error' %></h3>
<%= link_to(image_tag(SiteSetting.logo_url, class: 'logo'), Discourse.base_url) %>
<%= link_to(image_tag(SiteSetting.site_logo_url, class: 'logo'), Discourse.base_url) %>
<div class='clearfix'></div>
</header>
<%- if @show_reason %>

View File

@ -1,6 +1,6 @@
<header class='discourse' data-embed-state='loading'>
<h3><%= t 'embed.loading' %></h3>
<%= link_to(image_tag(SiteSetting.logo_url, class: 'logo'), Discourse.base_url) %>
<%= link_to(image_tag(SiteSetting.site_logo_url, class: 'logo'), Discourse.base_url) %>
<div class='clearfix'></div>
</header>
<script>

View File

@ -1,13 +1,14 @@
<meta name="author" content="">
<meta name="generator" content="Discourse <%= Discourse::VERSION::STRING %> - https://github.com/discourse/discourse version <%= Discourse.git_version %>">
<%- if SiteSetting.favicon_url.present? %>
<link rel="icon" type="image/png" href="<%=SiteSetting.favicon_url%>">
<%- if SiteSetting.site_favicon_url.present? %>
<link rel="icon" type="image/png" href="<%=SiteSetting.site_favicon_url%>">
<%- end %>
<%- if SiteSetting.apple_touch_icon_url.present? %>
<link rel="apple-touch-icon" type="image/png" href="<%=SiteSetting.apple_touch_icon_url%>">
<% site_apple_touch_icon_url = SiteSetting.site_apple_touch_icon_url %>
<%- if site_apple_touch_icon_url.present? %>
<link rel="apple-touch-icon" type="image/png" href="<%= site_apple_touch_icon_url %>">
<%- end %>
<%- if (SiteSetting.apple_touch_icon_url != "/images/default-apple-touch-icon.png") && SiteSetting.apple_touch_icon_url.present? %>
<link rel="icon" type="image/png" sizes="144x144" href="<%=SiteSetting.apple_touch_icon_url%>">
<%- if (site_apple_touch_icon_url != "/images/default-apple-touch-icon.png") && site_apple_touch_icon_url.present? %>
<link rel="icon" type="image/png" sizes="144x144" href="<%= site_apple_touch_icon_url %>">
<%- end %>
<meta name="theme-color" content="#<%= ColorScheme.hex_for_name('header_background', scheme_id) %>">
<% if mobile_view? %>

View File

@ -23,8 +23,8 @@
<%= theme_lookup("header") %>
<header>
<a href="<%= path "/" %>">
<%- if SiteSetting.logo_url.present? %>
<img src="<%=SiteSetting.logo_url%>" alt="<%=SiteSetting.title%>" id="site-logo" style="max-width: 150px;">
<%- if SiteSetting.site_logo_url.present? %>
<img src="<%=SiteSetting.site_logo_url%>" alt="<%=SiteSetting.title%>" id="site-logo" style="max-width: 150px;">
<%- else %>
<h1><%=SiteSetting.title%></h1>
<% end %>

View File

@ -3,10 +3,12 @@
<ShortName><%= SiteSetting.title %> Search</ShortName>
<Description>Search for posts on <%= SiteSetting.title %></Description>
<Tags>discourse forum</Tags>
<% if SiteSetting.favicon_url =~ /\.ico$/ -%>
<Image height="16" width="16" type="image/vnd.microsoft.icon"><%= UrlHelper.absolute SiteSetting.favicon_url %></Image>
<% site_favicon_url = SiteSetting.site_favicon_url %>
<% absolute_site_favicon_url = UrlHelper.absolute(site_favicon_url) %>
<% if site_favicon_url =~ /\.ico$/ -%>
<Image height="16" width="16" type="image/vnd.microsoft.icon"><%= absolute_site_favicon_url %></Image>
<%- else -%>
<Image type="image/png"><%= UrlHelper.absolute SiteSetting.favicon_url %></Image>
<Image type="image/png"><%= absolute_site_favicon_url %></Image>
<%- end %>
<Url type="application/opensearchdescription+xml" rel="self" template="<%= Discourse.base_url %>/opensearch.xml"/>
<Url type="text/html" template="<%= Discourse.base_url %>/search?q={searchTerms}"/>

View File

@ -1146,7 +1146,7 @@ en:
one: "Email polling has generated an error in the past 24 hours. Look at <a href='%{base_path}/logs' target='_blank'>the logs</a> for more details."
other: "Email polling has generated %{count} errors in the past 24 hours. Look at <a href='%{base_path}/logs' target='_blank'>the logs</a> for more details."
missing_mailgun_api_key: "The server is configured to send emails via Mailgun but you haven't provided an API key used to verify the webhook messages."
bad_favicon_url: "The favicon is failing to load. Check your favicon_url setting in <a href='%{base_path}/admin/site_settings'>Site Settings</a>."
bad_favicon_url: "The favicon is failing to load. Check your favicon setting in <a href='%{base_path}/admin/site_settings'>Site Settings</a>."
poll_pop3_timeout: "Connection to the POP3 server is timing out. Incoming email could not be retrieved. Please check your <a href='%{base_path}/admin/site_settings/category/email'>POP3 settings</a> and service provider."
poll_pop3_auth_error: "Connection to the POP3 server is failing with an authentication error. Please check your <a href='%{base_path}/admin/site_settings/category/email'>POP3 settings</a>."
force_https_warning: "Your website is using SSL. But `<a href='%{base_path}/admin/site_settings/category/all_results?filter=force_https'>force_https</a>` is not yet enabled in your site settings."
@ -1215,13 +1215,15 @@ en:
enable_inline_onebox_on_all_domains: "Ignore inline_onebox_domain_whitelist site setting and allow inline onebox on all domains."
max_oneboxes_per_post: "Maximum number of oneboxes in a post."
logo_url: "The logo image at the top left of your site, should be a wide rectangle shape. If left blank site title text will be shown. Use <a href='%{base_path}/wizard/steps/logos' target='_blank'>the wizard</a> to update."
digest_logo_url: "The alternate logo image used at the top of your site's email summary. Should be a wide rectangle shape. Should not be an SVG image. If left blank `logo_url` will be used."
logo_small_url: "The small logo image at the top left of your site, should be a square shape, seen when scrolling down. If left blank a home glyph will be shown. Use <a href='%{base_path}/wizard/steps/logos' target='_blank'>the wizard</a> to update."
favicon_url: "A favicon for your site, see <a href='https://en.wikipedia.org/wiki/Favicon' target='_blank'>https://en.wikipedia.org/wiki/Favicon</a>. To work correctly over a CDN it must be a png. Use <a href='%{base_path}/wizard/steps/icons' target='_blank'>the wizard</a> to update."
mobile_logo_url: "Custom logo url used on mobile version of your site. If left blank, `logo_url` will be used. eg: https://example.com/uploads/default/logo.png"
large_icon_url: "Image used as logo/splash image on Android. Recommended size is 512px by 512px. Use <a href='%{base_path}/wizard/steps/icons' target='_blank'>the wizard</a> to update."
apple_touch_icon_url: "Icon used for Apple touch devices. Recommended size is 144px by 144px."
logo: "The logo image at the top left of your site, should be a wide rectangle shape. If left blank site title text will be shown."
logo_small: "The small logo image at the top left of your site, should be a square shape, seen when scrolling down. If left blank a home glyph will be shown."
digest_logo: "The alternate logo image used at the top of your site's email summary. Should be a wide rectangle shape. Should not be an SVG image. If left blank `logo_url` will be used."
mobile_logo: "Custom logo url used on mobile version of your site. If left blank, `logo_url` will be used. eg: https://example.com/uploads/default/logo.png"
large_icon: "Image used as logo/splash image on Android. Recommended size is 512px by 512px."
favicon: "A favicon for your site, see <a href='https://en.wikipedia.org/wiki/Favicon' target='_blank'>https://en.wikipedia.org/wiki/Favicon</a>. To work correctly over a CDN it must be a png."
apple_touch_icon: "Icon used for Apple touch devices. Recommended size is 144px by 144px."
opengraph_image: "Default opengraph image, used when the page has no other suitable image or site logo."
twitter_summary_large_image: "Default Twitter summary card image (should be at least 280px in width, and at least 150px in height)."
notification_email: "The from: email address used when sending all essential system emails. The domain specified here must have SPF, DKIM and reverse PTR records set correctly for email to arrive."
email_custom_headers: "A pipe-delimited list of custom email headers"
@ -1454,9 +1456,6 @@ en:
selectable_avatars_enabled: "Force users to choose an avatar from the list."
selectable_avatars: "List of avatars users can choose from."
default_opengraph_image_url: "Default opengraph image, used when the page has no other suitable image or site logo."
twitter_summary_large_image_url: "Default Twitter summary card image (should be at least 280px in width, and at least 150px in height)."
allow_all_attachments_for_group_messages: "Allow all email attachments for group messages."
png_to_jpg_quality: "Quality of the converted JPG file (1 is lowest quality, 99 is best quality, 100 to disable)."
@ -1850,7 +1849,7 @@ en:
shared_drafts_category: "Enable the Shared Drafts feature by designating a category for topic drafts. Topics in this category will be suppressed from topic lists for staff users."
push_notifications_prompt: "Display user consent prompt."
push_notifications_icon_url: "The badge icon that appears in the notification corner. Recommended size is 96px by 96px."
push_notifications_icon: "The badge icon that appears in the notification corner. Recommended size is 96px by 96px."
errors:
invalid_email: "Invalid email address."
@ -4067,20 +4066,20 @@ en:
logos:
title: "Logos"
fields:
logo_url:
logo:
label: "Primary Logo"
description: "The logo image at the top left of your site. Use a wide rectangle shape."
logo_small_url:
logo_small:
label: "Compact Logo"
description: "A compact version of your logo, shown at the top left of your site when scrolling down. Use a square shape."
icons:
title: "Icons"
fields:
favicon_url:
favicon:
label: "Small Icon"
description: "Icon image used to represent your site in web browsers that looks good at small sizes such as 32px by 32px. Recommended image extensions are PNG or JPG."
apple_touch_icon_url:
apple_touch_icon:
label: "Large Icon"
description: "Icon image used to represent your site on modern devices that looks good at larger sizes. Recommended size is at least 512px by 512px."

View File

@ -45,25 +45,67 @@ required:
site_contact_username:
default: ''
type: username
logo:
default: ''
client: true
type: upload
logo_url:
client: true
hidden: true
default: '/images/d-logo-sketch.png'
logo_small:
default: ''
client: true
type: upload
logo_small_url:
client: true
hidden: true
default: '/images/d-logo-sketch-small.png'
digest_logo:
default: ''
client: true
type: upload
digest_logo_url:
hidden: true
default: ''
mobile_logo:
default: ''
client: true
type: upload
mobile_logo_url:
client: true
hidden: true
default: ''
large_icon:
default: ''
client: true
type: upload
large_icon_url:
hidden: true
default: ''
favicon:
default: ''
favicon_url:
client: true
type: upload
favicon_url:
hidden: true
default: '/images/default-favicon.ico'
apple_touch_icon_url: '/images/default-apple-touch-icon.png'
default_opengraph_image_url: ''
twitter_summary_large_image_url: ''
apple_touch_icon:
default: ''
client: true
type: upload
apple_touch_icon_url:
hidden: true
default: '/images/default-apple-touch-icon.png'
opengraph_image:
default: ''
type: upload
default_opengraph_image_url:
hidden: true
default: ''
twitter_summary_large_image:
default: ''
type: upload
twitter_summary_large_image_url:
hidden: true
default: ''
exclude_rel_nofollow_domains:
default: ''
type: list
@ -233,7 +275,11 @@ basic:
push_notifications_prompt:
default: true
client: true
push_notifications_icon:
default: ''
type: upload
push_notifications_icon_url:
hidden: true
default: ''
vapid_public_key_bytes:
default: ''

View File

@ -0,0 +1,75 @@
class MigrateUrlSiteSettings < ActiveRecord::Migration[5.2]
def up
[
['logo_url', 'logo'],
['logo_small_url', 'logo_small'],
['digest_logo_url', 'digest_logo'],
['mobile_logo_url', 'mobile_logo'],
['large_icon_url', 'large_icon'],
['favicon_url', 'favicon'],
['apple_touch_icon_url', 'apple_touch_icon'],
['default_opengraph_image_url', 'opengraph_image'],
['twitter_summary_large_image_url', 'twitter_summary_large_image'],
['push_notifications_icon_url', 'push_notifications_icon'],
].each do |old_setting, new_setting|
old_url = DB.query_single(
"SELECT value FROM site_settings WHERE name = '#{old_setting}'"
).first
next if old_url.blank?
count = 0
file = nil
sleep_interval = 5
loop do
url = UrlHelper.absolute(old_url)
begin
file = FileHelper.download(
url,
max_file_size: 20.megabytes,
tmp_file_name: 'tmp_site_setting_logo',
skip_rate_limit: true,
follow_redirect: true
)
rescue OpenURI::HTTPError => e
logger.info(
"HTTP error encountered when trying to download file " +
"for #{new_setting}.\n#{e.message}"
)
end
count += 1
break if file || (file.blank? && count >= 3)
logger.info(
"Failed to download upload from #{url} for #{new_setting}. Retrying..."
)
sleep(count * sleep_interval)
end
next if file.blank?
upload = UploadCreator.new(
file,
"#{new_setting}",
origin: UrlHelper.absolute(old_url),
for_site_setting: true
).create_for(Discourse.system_user.id)
SiteSetting.public_send("#{new_setting}=", upload)
end
end
def down
raise ActiveRecord::IrreversibleMigration
end
private
def logger
Rails.logger
end
end

View File

@ -190,7 +190,11 @@ module SiteSettingExtension
end
def client_settings_json_uncached
MultiJson.dump(Hash[*@client_settings.map { |n| [n, self.send(n)] }.flatten])
MultiJson.dump(Hash[*@client_settings.map do |name|
value = self.public_send(name)
value = value.to_s if type_supervisor.get_type(name) == :upload
[name, value]
end.flatten])
end
# Retrieve all settings
@ -212,7 +216,9 @@ module SiteSettingExtension
defaults.all(default_locale)
.reject { |s, _| !include_hidden && hidden_settings.include?(s) }
.map do |s, v|
value = send(s)
opts = {
setting: s,
description: description(s),
@ -312,6 +318,7 @@ module SiteSettingExtension
def remove_override!(name)
provider.destroy(name)
current[name] = defaults.get(name, default_locale)
uploads.delete(name)
clear_cache!
end
@ -319,6 +326,7 @@ module SiteSettingExtension
val, type = type_supervisor.to_db_value(name, val)
provider.save(name, val, type)
current[name] = type_supervisor.to_rb_value(name, val)
uploads.delete(name)
notify_clients!(name) if client_settings.include? name
clear_cache!
end
@ -410,12 +418,31 @@ module SiteSettingExtension
def setup_methods(name)
clean_name = name.to_s.sub("?", "").to_sym
define_singleton_method clean_name do
if (c = current[name]).nil?
refresh!
current[name]
else
c
if type_supervisor.get_type(name) == :upload
define_singleton_method clean_name do
upload = uploads[name]
return upload if upload
if (value = current[name]).nil?
refresh!
value = current[name]
end
value = value.to_i
if value > 0
upload = Upload.find_by(id: value)
uploads[name] = upload if upload
end
end
else
define_singleton_method clean_name do
if (c = current[name]).nil?
refresh!
current[name]
else
c
end
end
end
@ -448,6 +475,11 @@ module SiteSettingExtension
private
def uploads
@uploads ||= {}
@uploads[provider.current_site] ||= {}
end
def logger
Rails.logger
end

View File

@ -31,6 +31,7 @@ class SiteSettings::TypeSupervisor
username: 15,
category: 16,
uploaded_image_list: 17,
upload: 18,
)
end
@ -133,7 +134,7 @@ class SiteSettings::TypeSupervisor
def type_hash(name)
name = name.to_sym
type = self.class.types[@types[name]]
type = get_type(name)
result = { type: type.to_s }
@ -150,6 +151,10 @@ class SiteSettings::TypeSupervisor
result
end
def get_type(name)
self.class.types[@types[name.to_sym]]
end
private
def normalize_input(name, val)
@ -164,6 +169,8 @@ class SiteSettings::TypeSupervisor
type = get_data_type(name, val)
elsif type == self.class.types[:enum]
val = @defaults_provider[name].is_a?(Integer) ? val.to_i : val.to_s
elsif type == self.class.types[:upload] && val.present?
val = val.id
end
[val, type]

View File

@ -12,6 +12,7 @@ class SiteSettings::YamlLoader
if hash.is_a?(Hash)
# Get default value for the site setting:
value = hash.delete('default')
if value.is_a?(Hash)
raise Discourse::Deprecation, "The site setting `#{setting_name}` can no longer be set based on Rails environment. See also `config/environments/<env>.rb`."
elsif value.nil?

View File

@ -117,6 +117,7 @@ class UploadCreator
@upload.for_group_message = true if @opts[:for_group_message]
@upload.for_theme = true if @opts[:for_theme]
@upload.for_export = true if @opts[:for_export]
@upload.for_site_setting = true if @opts[:for_site_setting]
return @upload unless @upload.save

View File

@ -6,7 +6,7 @@ class Validators::UploadValidator < ActiveModel::Validator
def validate(upload)
# staff can upload any file in PM
if upload.for_private_message && SiteSetting.allow_staff_to_upload_any_file_in_pm
if (upload.for_private_message && SiteSetting.allow_staff_to_upload_any_file_in_pm)
return true if upload.user&.staff?
end
@ -17,6 +17,13 @@ class Validators::UploadValidator < ActiveModel::Validator
extension = File.extname(upload.original_filename)[1..-1] || ""
if upload.for_site_setting &&
upload.user&.staff? &&
FileHelper.is_supported_image?(upload.original_filename)
return true
end
if is_authorized?(upload, extension)
if FileHelper.is_supported_image?(upload.original_filename)
authorized_image_extension(upload, extension)

View File

@ -144,26 +144,27 @@ class Wizard
end
@wizard.append_step('logos') do |step|
step.add_field(id: 'logo_url', type: 'image', value: SiteSetting.logo_url)
step.add_field(id: 'logo_small_url', type: 'image', value: SiteSetting.logo_small_url)
step.add_field(id: 'logo', type: 'image', value: SiteSetting.site_logo_url)
step.add_field(id: 'logo_small', type: 'image', value: SiteSetting.site_logo_small_url)
step.on_update do |updater|
updater.apply_settings(:logo_url, :logo_small_url)
updater.apply_settings(:logo, :logo_small)
end
end
@wizard.append_step('icons') do |step|
step.add_field(id: 'favicon_url', type: 'image', value: SiteSetting.favicon_url)
step.add_field(id: 'apple_touch_icon_url', type: 'image', value: SiteSetting.apple_touch_icon_url)
step.add_field(id: 'favicon', type: 'image', value: SiteSetting.favicon)
step.add_field(id: 'apple_touch_icon', type: 'image', value: SiteSetting.apple_touch_icon)
step.on_update do |updater|
updater.apply_settings(:favicon_url)
updater.apply_settings(:favicon)
if updater.fields[:apple_touch_icon_url] != SiteSetting.apple_touch_icon_url
upload = Upload.find_by_url(updater.fields[:apple_touch_icon_url])
if updater.fields[:apple_touch_icon] != SiteSetting.apple_touch_icon
upload = Upload.find_by_url(updater.fields[:apple_touch_icon])
dimensions = 180 # for apple touch icon
if upload && upload.width > dimensions && upload.height > dimensions
updater.update_setting(:large_icon_url, updater.fields[:apple_touch_icon_url])
updater.update_setting(:large_icon, upload)
apple_touch_icon_optimized = OptimizedImage.create_for(
upload,
@ -172,13 +173,15 @@ class Wizard
)
original_file = File.new(Discourse.store.path_for(apple_touch_icon_optimized)) rescue nil
if original_file
apple_touch_icon_upload = UploadCreator.new(original_file, upload.original_filename).create_for(@wizard.user.id)
updater.update_setting(:apple_touch_icon_url, apple_touch_icon_upload.url)
updater.update_setting(:apple_touch_icon, apple_touch_icon_upload)
end
apple_touch_icon_optimized.destroy! if apple_touch_icon_optimized.present?
else
updater.apply_settings(:apple_touch_icon_url)
updater.apply_settings(:apple_touch_icon)
end
end
end

View File

@ -30,6 +30,11 @@ class Wizard
def update_setting(id, value)
value.strip! if value.is_a?(String)
if !value.is_a?(Upload) && SiteSetting.type_supervisor.get_type(id) == :upload
value = Upload.get_from_url(value) || ''
end
SiteSetting.set_and_log(id, value, @current_user) if SiteSetting.send(id) != value
end

View File

@ -568,10 +568,10 @@ module DiscourseNarrativeBot
end
def logo_group(size, width, height)
return unless SiteSetting.logo_small_url.present?
return unless SiteSetting.site_logo_small_url.present?
begin
uri = URI(SiteSetting.logo_small_url)
uri = URI(SiteSetting.site_logo_small_url)
logo_uri =
if uri.host.blank? || uri.scheme.blank?

View File

@ -10,8 +10,9 @@ RSpec.describe DiscourseNarrativeBot::CertificateGenerator do
end
describe '#logo_group' do
describe 'when SiteSetting.logo_small_url is blank' do
describe 'when SiteSetting.site_logo_small_url is blank' do
before do
SiteSetting.logo_small = ''
SiteSetting.logo_small_url = ''
end

View File

@ -703,4 +703,39 @@ describe SiteSettingExtension do
end
describe '.client_settings_json_uncached' do
it 'should return the right json value' do
upload = Fabricate(:upload)
settings.setting(:upload_type, upload.id.to_s, type: :upload, client: true)
settings.setting(:string_type, 'haha', client: true)
settings.refresh!
expect(settings.client_settings_json_uncached).to eq(
%Q|{"default_locale":"en","upload_type":"#{upload.url}","string_type":"haha"}|
)
end
end
describe '.setup_methods' do
describe 'for uploads site settings' do
let(:upload) { Fabricate(:upload) }
let(:upload2) { Fabricate(:upload) }
it 'should return the upload record' do
settings.setting(:some_upload, upload.id.to_s, type: :upload)
expect(settings.some_upload).to eq(upload)
# Ensure that we cache the upload record
expect(settings.some_upload.object_id).to eq(
settings.some_upload.object_id
)
settings.some_upload = upload2
expect(settings.some_upload).to eq(upload2)
end
end
end
end

View File

@ -79,6 +79,10 @@ describe SiteSettings::TypeSupervisor do
it "'uploaded_image_list' should be at 17th position" do
expect(SiteSettings::TypeSupervisor.types[:uploaded_image_list]).to eq(17)
end
it "'upload' should be at the right position" do
expect(SiteSettings::TypeSupervisor.types[:upload]).to eq(18)
end
end
end
@ -151,10 +155,11 @@ describe SiteSettings::TypeSupervisor do
settings.setting(:type_validator, 5, validator: 'TestSmallThanTenValidator')
settings.setting(:type_mock_validate_method, 'no_value')
settings.setting(:type_custom, 'custom', type: 'list')
settings.setting(:type_upload, '', type: 'upload')
settings.refresh!
end
describe '.to_db_value' do
describe '#to_db_value' do
let(:true_val) { 't' }
let(:false_val) { 'f' }
@ -187,6 +192,13 @@ describe SiteSettings::TypeSupervisor do
expect(settings.type_supervisor.to_db_value(:type_string, 'a')).to eq ['a', SiteSetting.types[:string]]
end
it 'returns the upload id' do
upload = Fabricate(:upload)
expect(settings.type_supervisor.to_db_value(:type_upload, upload))
.to eq([upload.id, SiteSetting.types[:upload]])
end
it 'returns enum value with string default' do
expect(settings.type_supervisor.to_db_value(:type_enum_default_string, 2)).to eq ['2', SiteSetting.types[:enum]]
expect(settings.type_supervisor.to_db_value(:type_enum_default_string, '2')).to eq ['2', SiteSetting.types[:enum]]
@ -224,7 +236,7 @@ describe SiteSettings::TypeSupervisor do
end
end
describe '.to_rb_value' do
describe '#to_rb_value' do
let(:true_val) { 't' }
let(:false_val) { 'f' }
@ -266,6 +278,16 @@ describe SiteSettings::TypeSupervisor do
expect(settings.type_supervisor.to_rb_value(:type_string, 2)).to eq '2'
end
it 'returns the upload record' do
upload = Fabricate(:upload)
expect(settings.type_supervisor.to_rb_value(:type_upload, ''))
.to eq('')
expect(settings.type_supervisor.to_rb_value(:type_upload, upload.id))
.to eq(upload.id)
end
it 'returns value with string default' do
expect(settings.type_supervisor.to_rb_value(:type_enum_default_string, 2)).to eq '2'
expect(settings.type_supervisor.to_rb_value(:type_enum_default_string, '2')).to eq '2'
@ -284,6 +306,18 @@ describe SiteSettings::TypeSupervisor do
end
end
describe '#get_type' do
before do
settings.setting(:type_null, nil)
settings.setting(:type_upload, '', type: :upload)
end
it 'should return the right type that has been registered' do
expect(settings.type_supervisor.get_type(:type_null)).to eq(:null)
expect(settings.type_supervisor.get_type(:type_upload)).to eq(:upload)
end
end
describe '.type_hash' do
class TestEnumClass2
def self.valid_value?(v)

View File

@ -53,5 +53,42 @@ describe Validators::UploadValidator do
end
end
end
describe 'upload for site settings' do
let(:user) { Fabricate(:admin) }
let(:upload) do
Fabricate.build(:upload,
user: user,
original_filename: 'test.ico',
for_site_setting: true
)
end
before do
SiteSetting.authorized_extensions = 'png'
end
describe 'for admin user' do
it 'should allow the upload' do
expect(subject.validate(upload)).to eq(true)
end
describe 'when filename is invalid' do
it 'should not allow the upload' do
upload.original_filename = 'test.txt'
expect(subject.validate(upload)).to eq(nil)
end
end
end
describe 'for normal user' do
let(:user) { Fabricate(:user) }
it 'should not allow the upload' do
expect(subject.validate(upload)).to eq(nil)
end
end
end
end
end

View File

@ -214,38 +214,49 @@ describe Wizard::StepUpdater do
context "logos step" do
it "updates the fields correctly" do
updater = wizard.create_updater('logos',
logo_url: '/uploads/logo.png',
logo_small_url: '/uploads/logo-small.png')
upload = Fabricate(:upload)
upload2 = Fabricate(:upload)
updater = wizard.create_updater(
'logos',
logo: upload.url,
logo_small: upload2.url
)
updater.update
expect(updater).to be_success
expect(wizard.completed_steps?('logos')).to eq(true)
expect(SiteSetting.logo_url).to eq('/uploads/logo.png')
expect(SiteSetting.logo_small_url).to eq('/uploads/logo-small.png')
expect(SiteSetting.logo).to eq(upload)
expect(SiteSetting.logo_small).to eq(upload2)
end
end
context "icons step" do
it "updates the fields correctly" do
upload = Fabricate(:upload)
upload2 = Fabricate(:upload)
updater = wizard.create_updater('icons',
favicon_url: "/uploads/favicon.png",
apple_touch_icon_url: "/uploads/apple.png")
favicon: upload.url,
apple_touch_icon: upload2.url
)
updater.update
expect(updater).to be_success
expect(wizard.completed_steps?('icons')).to eq(true)
expect(SiteSetting.favicon_url).to eq('/uploads/favicon.png')
expect(SiteSetting.apple_touch_icon_url).to eq('/uploads/apple.png')
expect(SiteSetting.favicon).to eq(upload)
expect(SiteSetting.apple_touch_icon).to eq(upload2)
end
it "updates large_icon_url if the uploaded icon size is greater than 180x180" do
it "updates large_icon if the uploaded icon size is greater than 180x180" do
upload = Fabricate(:upload, width: 512, height: 512)
updater = wizard.create_updater('icons', apple_touch_icon_url: upload.url)
updater = wizard.create_updater('icons', apple_touch_icon: upload.url)
updater.update
expect(updater).to be_success
expect(SiteSetting.large_icon_url).to eq(upload.url)
expect(SiteSetting.large_icon).to eq(upload)
end
end

View File

@ -0,0 +1,76 @@
require 'rails_helper'
require_relative '../../../db/post_migrate/20181112013117_migrate_url_site_settings'
RSpec.describe MigrateUrlSiteSettings do
before do
SiteSetting.authorized_extensions = ''
end
it 'should migrate to the new upload site settings correctly' do
[
%w{logo_url /test.png},
%w{logo_small_url https://test.discourse.awesome/test.png},
%w{favicon_url http://test.discourse.awesome/some.ico},
%w{digest_logo_url /test.png},
%w{mobile_logo_url /test.png},
%w{large_icon_url /test.png},
%w{apple_touch_icon_url /test.png},
%w{default_opengraph_image_url /test.png},
%w{twitter_summary_large_image_url //omg.aws.somestack/test.png},
%w{push_notifications_icon_url //omg.aws.somestack/test.png}
].each do |name, value|
SiteSetting.create!(
name: name,
value: value,
data_type: SiteSettings::TypeSupervisor.types[:string]
)
end
%w{
http://test.localhost/test.png
https://omg.aws.somestack/test.png
}.each do |url|
stub_request(:get, url).to_return(
status: 200, body: file_from_fixtures("smallest.png").read
)
end
stub_request(:get, "https://test.discourse.awesome/test.png")
.to_return(status: 200, body: file_from_fixtures("downsized.png").read)
stub_request(:get, "http://test.discourse.awesome/some.ico")
.to_return(status: 200, body: file_from_fixtures("smallest.ico").read)
begin
STDOUT.stubs(:write)
expect { MigrateUrlSiteSettings.new.up }.to change { Upload.count }.by(3)
ensure
STDOUT.unstub(:write)
end
upload = Upload.find_by(original_filename: "logo.png")
upload2 = Upload.find_by(original_filename: "logo_small.png")
upload3 = Upload.find_by(original_filename: "favicon.ico")
expect(SiteSetting.logo_small).to eq(upload2)
expect(SiteSetting.logo_small.is_a?(Upload)).to eq(true)
expect(SiteSetting.favicon).to eq(upload3)
expect(SiteSetting.favicon.is_a?(Upload)).to eq(true)
%i{
logo
digest_logo
mobile_logo
large_icon
apple_touch_icon
opengraph_image
twitter_summary_large_image
push_notifications_icon
}.each do |setting|
expect(SiteSetting.public_send(setting)).to eq(upload)
expect(SiteSetting.public_send(setting).is_a?(Upload)).to eq(true)
end
end
end

BIN
spec/fixtures/images/smallest.ico vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 B

View File

@ -44,7 +44,54 @@ describe Jobs::CleanUpUploads do
end
end
it "does not clean up uploads in site settings" do
it 'does not clean up upload site settings' do
begin
original_provider = SiteSetting.provider
SiteSetting.provider = SiteSettings::DbProvider.new(SiteSetting)
logo_upload = fabricate_upload
logo_small_upload = fabricate_upload
digest_logo_upload = fabricate_upload
mobile_logo_upload = fabricate_upload
large_icon_upload = fabricate_upload
opengraph_image_upload = fabricate_upload
twitter_summary_large_image_upload = fabricate_upload
favicon_upload = fabricate_upload
apple_touch_icon_upload = fabricate_upload
SiteSetting.logo = logo_upload
SiteSetting.logo_small = logo_small_upload
SiteSetting.digest_logo = digest_logo_upload
SiteSetting.mobile_logo = mobile_logo_upload
SiteSetting.large_icon = large_icon_upload
SiteSetting.opengraph_image = opengraph_image_upload
SiteSetting.twitter_summary_large_image =
twitter_summary_large_image_upload
SiteSetting.favicon = favicon_upload
SiteSetting.apple_touch_icon = apple_touch_icon_upload
Jobs::CleanUpUploads.new.execute(nil)
[
logo_upload,
logo_small_upload,
digest_logo_upload,
mobile_logo_upload,
large_icon_upload,
opengraph_image_upload,
twitter_summary_large_image_upload,
favicon_upload,
apple_touch_icon_upload
].each { |record| expect(Upload.exists?(id: record.id)).to eq(true) }
ensure
SiteSetting.delete_all
SiteSetting.provider = original_provider
end
end
it "does not clean up uploads with URLs used in site settings" do
logo_upload = fabricate_upload
logo_small_upload = fabricate_upload
digest_logo_upload = fabricate_upload

View File

@ -37,6 +37,26 @@ RSpec.describe UploadCreator do
end
end
describe 'when image is not authorized' do
describe 'when image is for site setting' do
let(:filename) { 'logo.png' }
let(:file) { file_from_fixtures(filename) }
before do
SiteSetting.authorized_extensions = 'jpg'
end
it 'should create the right upload' do
upload = UploadCreator.new(file, filename,
for_site_setting: true
).create_for(Discourse.system_user.id)
expect(upload.persisted?).to eq(true)
expect(upload.original_filename).to eq(filename)
end
end
end
describe 'when image has the wrong extension' do
let(:filename) { "png_as.bin" }
let(:file) { file_from_fixtures(filename) }

View File

@ -222,4 +222,10 @@ describe Upload do
end
end
describe '#to_s' do
it 'should return the right value' do
expect(upload.to_s).to eq(upload.url)
end
end
end

View File

@ -31,6 +31,7 @@ describe Admin::SiteSettingsController do
describe '#update' do
before do
SiteSetting.setting(:test_setting, "default")
SiteSetting.setting(:test_upload, "", type: :upload)
SiteSetting.refresh!
end
@ -50,6 +51,33 @@ describe Admin::SiteSettingsController do
expect(SiteSetting.test_setting).to eq('')
end
it 'allows upload site settings to be updated' do
upload = Fabricate(:upload)
put "/admin/site_settings/test_upload.json", params: {
test_upload: upload.url
}
expect(response.status).to eq(200)
expect(SiteSetting.test_upload).to eq(upload)
user_history = UserHistory.last
expect(user_history.action).to eq(
UserHistory.actions[:change_site_setting]
)
expect(user_history.previous_value).to eq(nil)
expect(user_history.new_value).to eq(upload.url)
put "/admin/site_settings/test_upload.json", params: {
test_upload: nil
}
expect(response.status).to eq(200)
expect(SiteSetting.test_upload).to eq(nil)
end
it 'logs the change' do
SiteSetting.test_setting = 'previous'

View File

@ -1,11 +1,13 @@
require 'rails_helper'
RSpec.describe MetadataController do
let(:upload) { Fabricate(:upload) }
describe 'manifest.webmanifest' do
it 'returns the right output' do
title = 'MyApp'
SiteSetting.title = title
SiteSetting.large_icon_url = "http://big.square/png"
SiteSetting.large_icon = upload
get "/manifest.webmanifest"
expect(response.status).to eq(200)
@ -13,19 +15,28 @@ RSpec.describe MetadataController do
manifest = JSON.parse(response.body)
expect(manifest["name"]).to eq(title)
expect(manifest["icons"].first["src"]).to eq("http://big.square/png")
expect(manifest["icons"].first["src"]).to eq(
UrlHelper.absolute(upload.url)
)
end
it 'can guess mime types' do
SiteSetting.large_icon_url = "http://big.square/ico.jpg"
upload = Fabricate(:upload,
original_filename: 'test.jpg',
extension: 'jpg'
)
SiteSetting.large_icon = upload
get "/manifest.webmanifest"
expect(response.status).to eq(200)
manifest = JSON.parse(response.body)
expect(manifest["icons"].first["type"]).to eq("image/jpeg")
end
it 'defaults to png' do
SiteSetting.large_icon_url = "http://big.square/noidea.bogus"
SiteSetting.large_icon = upload
get "/manifest.webmanifest"
expect(response.status).to eq(200)
manifest = JSON.parse(response.body)
@ -57,15 +68,15 @@ RSpec.describe MetadataController do
describe 'opensearch.xml' do
it 'returns the right output' do
title = 'MyApp'
favicon_path = '/uploads/something/23432.png'
SiteSetting.title = title
SiteSetting.favicon_url = favicon_path
SiteSetting.favicon = upload
get "/opensearch.xml"
expect(response.status).to eq(200)
expect(response.body).to include(title)
expect(response.body).to include("/search?q={searchTerms}")
expect(response.body).to include('image/png')
expect(response.body).to include(favicon_path)
expect(response.body).to include(UrlHelper.absolute(upload.url))
expect(response.content_type).to eq('application/xml')
end
end

View File

@ -1,33 +1,36 @@
require 'rails_helper'
describe SiteController do
describe '.basic_info' do
describe '#basic_info' do
it 'is visible always even for sites requiring login' do
SiteSetting.login_required = true
upload = Fabricate(:upload)
SiteSetting.login_required = true
SiteSetting.title = "Hammer Time"
SiteSetting.site_description = "A time for Hammer"
SiteSetting.logo_url = "/uploads/logo.png"
SiteSetting.logo_small_url = "http://boom.com/uploads/logo_small.png"
SiteSetting.apple_touch_icon_url = "https://boom.com/apple/logo.png"
SiteSetting.mobile_logo_url = "https://a.a/a.png"
SiteSetting.logo = upload
SiteSetting.logo_small = upload
SiteSetting.apple_touch_icon = upload
SiteSetting.mobile_logo = upload
Theme.clear_default!
get "/site/basic-info.json"
json = JSON.parse(response.body)
expected_url = UrlHelper.absolute(upload.url)
expect(json["title"]).to eq("Hammer Time")
expect(json["description"]).to eq("A time for Hammer")
expect(json["logo_url"]).to eq("http://test.localhost/uploads/logo.png")
expect(json["apple_touch_icon_url"]).to eq("https://boom.com/apple/logo.png")
expect(json["logo_small_url"]).to eq("http://boom.com/uploads/logo_small.png")
expect(json["mobile_logo_url"]).to eq("https://a.a/a.png")
expect(json["logo_url"]).to eq(expected_url)
expect(json["apple_touch_icon_url"]).to eq(expected_url)
expect(json["logo_small_url"]).to eq(expected_url)
expect(json["mobile_logo_url"]).to eq(expected_url)
expect(json["header_primary_color"]).to eq("333333")
expect(json["header_background_color"]).to eq("ffffff")
end
end
describe '.statistics' do
describe '#statistics' do
it 'is visible for sites requiring login' do
SiteSetting.login_required = true
SiteSetting.share_anonymized_statistics = true

View File

@ -1,19 +1,22 @@
require 'rails_helper'
describe StaticController do
let(:upload) { Fabricate(:upload) }
context '#favicon' do
let(:png) { Base64.decode64("R0lGODlhAQABALMAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwICAgP8AAAD/AP//AAAA//8A/wD//wBiZCH5BAEAAA8ALAAAAAABAAEAAAQC8EUAOw==") }
before { FinalDestination.stubs(:lookup_ip).returns("1.2.3.4") }
after do
$redis.flushall
end
it 'returns the default favicon for a missing download' do
url = "https://fav.icon/#{SecureRandom.hex}.png"
url = UrlHelper.absolute(upload.url)
SiteSetting.favicon = upload
stub_request(:get, url).to_return(status: 404)
SiteSetting.favicon_url = url
get '/favicon/proxied'
favicon = File.read(Rails.root + "public/images/default-favicon.png")
@ -24,12 +27,10 @@ describe StaticController do
end
it 'can proxy a favicon correctly' do
url = "https://fav.icon/#{SecureRandom.hex}.png"
url = UrlHelper.absolute(upload.url)
SiteSetting.favicon = upload
stub_request(:get, url).to_return(status: 200, body: png)
SiteSetting.favicon_url = url
get '/favicon/proxied'
expect(response.status).to eq(200)

View File

@ -124,7 +124,26 @@ describe UploadsController do
expect(response.status).to eq(200)
id = JSON.parse(response.body)["id"]
expect(id).to be_present
expect(Upload.last.id).to eq(id)
end
it 'allows staff to upload supported images for site settings' do
SiteSetting.authorized_extensions = ''
user.update!(admin: true)
post "/uploads.json", params: {
file: logo,
type: "site_setting",
for_site_setting: "true",
}
expect(response.status).to eq(200)
id = JSON.parse(response.body)["id"]
upload = Upload.last
expect(upload.id).to eq(id)
expect(upload.original_filename).to eq('logo.png')
end
it 'respects `authorized_extensions_for_staff` setting when staff upload file' do

View File

@ -10,6 +10,17 @@ acceptance("Admin - Site Settings", {
}
});
QUnit.test("upload site setting", async assert => {
await visit("/admin/site_settings");
assert.ok(
exists(".row.setting.upload .image-uploader"),
"image uploader is present"
);
assert.ok(exists(".row.setting.upload .undo"), "undo button is present");
});
QUnit.test("changing value updates dirty state", async assert => {
await visit("/admin/site_settings");
await fillIn("#setting-filter", "title");

View File

@ -33,6 +33,16 @@ export default {
secret: false,
type: "username"
},
{
setting: "logo",
description: "Some logo",
default: "",
value: "/some/image",
category: "required",
preview: null,
secret: false,
type: "upload"
},
{
setting: "top_menu",
description:

View File

@ -1,95 +1,103 @@
/*jshint maxlen:10000000 */
Discourse.SiteSettingsOriginal = {
"title":"Discourse Meta",
"logo_url":"/assets/logo.png",
"logo_small_url":"/assets/logo-single.png",
"mobile_logo_url":"",
"favicon_url":"//meta.discourse.org/uploads/default/2499/79d53726406d87af.ico",
"allow_user_locale":false,
"suggested_topics":7,
"ga_universal_tracking_code":"",
"ga_universal_domain_name":"auto",
"top_menu":"latest|new|unread|categories|top",
"post_menu":"like-count|like|share|flag|edit|bookmark|delete|admin|reply",
"post_menu_hidden_items":"flag|edit|delete|admin",
"share_links":"twitter|facebook|google+|email",
"category_colors":"BF1E2E|F1592A|F7941D|9EB83B|3AB54A|12A89D|25AAE2|0E76BD|652D90|92278F|ED207B|8C6238|231F20|27AA5B|B3B5B4|E45735",
"enable_mobile_theme":true,
"relative_date_duration":14,
"fixed_category_positions":false,
"enable_badges":true,
"invite_only":false,
"login_required":false,
"must_approve_users":false,
"enable_local_logins":true,
"allow_new_registrations":true,
"enable_google_logins":true,
"enable_google_oauth2_logins":false,
"enable_yahoo_logins":true,
"enable_twitter_logins":true,
"enable_facebook_logins":true,
"enable_github_logins":true,
"enable_sso":false,
"min_username_length":3,
"max_username_length":20,
"min_password_length":8,
"enable_names":true,
"invites_shown":30,
"delete_user_max_post_age":60,
"delete_all_posts_max":15,
"min_post_length":20,
"min_personal_message_post_length":10,
"max_post_length":32000,
"min_topic_title_length":15,
"max_topic_title_length":255,
"min_personal_message_title_length":2,
"allow_uncategorized_topics":true,
"min_title_similar_length":10,
"edit_history_visible_to_public":true,
"delete_removed_posts_after":24,
"traditional_markdown_linebreaks":false,
"suppress_reply_directly_below":true,
"suppress_reply_directly_above":true,
"newuser_max_images":0,
"newuser_max_attachments":0,
"display_name_on_posts":true,
"short_progress_text_threshold":10000,
"default_code_lang":"auto",
"autohighlight_all_code":false,
"email_in":false,
"authorized_extensions":".jpg|.jpeg|.png|.gif|.svg|.txt|.ico|.yml",
"authorized_extensions_for_staff": "",
"max_image_width":690,
"max_image_height":500,
"allow_profile_backgrounds":true,
"allow_uploaded_avatars":true,
"allow_animated_avatars":false,
"tl1_requires_read_posts":30,
"enable_long_polling":true,
"polling_interval":3000,
"anon_polling_interval":30000,
"flush_timings_secs":5,
"enable_user_directory":true,
"tos_url":"",
"privacy_policy_url":"",
"tos_accept_required":false,
"faq_url":"",
"allow_restore":false,
"maximum_backups":5,
"version_checks":true,
"suppress_uncategorized_badge":true,
"min_search_term_length":3,
"topic_views_heat_low":1000,
"topic_views_heat_medium":2000,
"topic_views_heat_high":5000,
"global_notice":"",
"show_create_topics_notice":true,
"available_locales":"cs|da|de|en|es|fr|he|id|it|ja|ko|nb_NO|nl|pl_PL|pt|pt_BR|ru|sv|uk|zh_CN|zh_TW",
"highlighted_languages":"apache|bash|cs|cpp|css|coffeescript|diff|xml|http|ini|json|java|javascript|makefile|markdown|nginx|objectivec|ruby|perl|php|python|sql|handlebars",
"enable_emoji":true,
"emoji_set":"emoji_one",
"desktop_category_page_style":"categories_and_latest_topics",
"enable_mentions":true,
"enable_personal_messages": true
title: "Discourse Meta",
site_logo_url: "/assets/logo.png",
site_logo_small_url: "/assets/logo-single.png",
site_mobile_logo_url: "",
site_favicon_url:
"//meta.discourse.org/uploads/default/2499/79d53726406d87af.ico",
allow_user_locale: false,
suggested_topics: 7,
ga_universal_tracking_code: "",
ga_universal_domain_name: "auto",
top_menu: "latest|new|unread|categories|top",
post_menu: "like-count|like|share|flag|edit|bookmark|delete|admin|reply",
post_menu_hidden_items: "flag|edit|delete|admin",
share_links: "twitter|facebook|google+|email",
category_colors:
"BF1E2E|F1592A|F7941D|9EB83B|3AB54A|12A89D|25AAE2|0E76BD|652D90|92278F|ED207B|8C6238|231F20|27AA5B|B3B5B4|E45735",
enable_mobile_theme: true,
relative_date_duration: 14,
fixed_category_positions: false,
enable_badges: true,
invite_only: false,
login_required: false,
must_approve_users: false,
enable_local_logins: true,
allow_new_registrations: true,
enable_google_logins: true,
enable_google_oauth2_logins: false,
enable_yahoo_logins: true,
enable_twitter_logins: true,
enable_facebook_logins: true,
enable_github_logins: true,
enable_sso: false,
min_username_length: 3,
max_username_length: 20,
min_password_length: 8,
enable_names: true,
invites_shown: 30,
delete_user_max_post_age: 60,
delete_all_posts_max: 15,
min_post_length: 20,
min_personal_message_post_length: 10,
max_post_length: 32000,
min_topic_title_length: 15,
max_topic_title_length: 255,
min_personal_message_title_length: 2,
allow_uncategorized_topics: true,
min_title_similar_length: 10,
edit_history_visible_to_public: true,
delete_removed_posts_after: 24,
traditional_markdown_linebreaks: false,
suppress_reply_directly_below: true,
suppress_reply_directly_above: true,
newuser_max_images: 0,
newuser_max_attachments: 0,
display_name_on_posts: true,
short_progress_text_threshold: 10000,
default_code_lang: "auto",
autohighlight_all_code: false,
email_in: false,
authorized_extensions: ".jpg|.jpeg|.png|.gif|.svg|.txt|.ico|.yml",
authorized_extensions_for_staff: "",
max_image_width: 690,
max_image_height: 500,
allow_profile_backgrounds: true,
allow_uploaded_avatars: true,
allow_animated_avatars: false,
tl1_requires_read_posts: 30,
enable_long_polling: true,
polling_interval: 3000,
anon_polling_interval: 30000,
flush_timings_secs: 5,
enable_user_directory: true,
tos_url: "",
privacy_policy_url: "",
tos_accept_required: false,
faq_url: "",
allow_restore: false,
maximum_backups: 5,
version_checks: true,
suppress_uncategorized_badge: true,
min_search_term_length: 3,
topic_views_heat_low: 1000,
topic_views_heat_medium: 2000,
topic_views_heat_high: 5000,
global_notice: "",
show_create_topics_notice: true,
available_locales:
"cs|da|de|en|es|fr|he|id|it|ja|ko|nb_NO|nl|pl_PL|pt|pt_BR|ru|sv|uk|zh_CN|zh_TW",
highlighted_languages:
"apache|bash|cs|cpp|css|coffeescript|diff|xml|http|ini|json|java|javascript|makefile|markdown|nginx|objectivec|ruby|perl|php|python|sql|handlebars",
enable_emoji: true,
emoji_set: "emoji_one",
desktop_category_page_style: "categories_and_latest_topics",
enable_mentions: true,
enable_personal_messages: true
};
Discourse.SiteSettings = jQuery.extend(true, {}, Discourse.SiteSettingsOriginal);
Discourse.SiteSettings = jQuery.extend(
true,
{},
Discourse.SiteSettingsOriginal
);

View File

@ -10,8 +10,8 @@ const title = "Cool Forum";
widgetTest("basics", {
template: '{{mount-widget widget="home-logo" args=args}}',
beforeEach() {
this.siteSettings.logo_url = bigLogo;
this.siteSettings.logo_small_url = smallLogo;
this.siteSettings.site_logo_url = bigLogo;
this.siteSettings.site_logo_small_url = smallLogo;
this.siteSettings.title = title;
this.set("args", { minimized: false });
},
@ -28,8 +28,8 @@ widgetTest("basics", {
widgetTest("basics - minimized", {
template: '{{mount-widget widget="home-logo" args=args}}',
beforeEach() {
this.siteSettings.logo_url = bigLogo;
this.siteSettings.logo_small_url = smallLogo;
this.siteSettings.site_logo_url = bigLogo;
this.siteSettings.site_logo_small_url = smallLogo;
this.siteSettings.title = title;
this.set("args", { minimized: true });
},
@ -44,8 +44,8 @@ widgetTest("basics - minimized", {
widgetTest("no logo", {
template: '{{mount-widget widget="home-logo" args=args}}',
beforeEach() {
this.siteSettings.logo_url = "";
this.siteSettings.logo_small_url = "";
this.siteSettings.site_logo_url = "";
this.siteSettings.site_logo_small_url = "";
this.siteSettings.title = title;
this.set("args", { minimized: false });
},
@ -59,8 +59,8 @@ widgetTest("no logo", {
widgetTest("no logo - minimized", {
template: '{{mount-widget widget="home-logo" args=args}}',
beforeEach() {
this.siteSettings.logo_url = "";
this.siteSettings.logo_small_url = "";
this.siteSettings.site_logo_url = "";
this.siteSettings.site_logo_small_url = "";
this.siteSettings.title = title;
this.set("args", { minimized: true });
},
@ -73,8 +73,8 @@ widgetTest("no logo - minimized", {
widgetTest("mobile logo", {
template: '{{mount-widget widget="home-logo" args=args}}',
beforeEach() {
this.siteSettings.mobile_logo_url = mobileLogo;
this.siteSettings.logo_small_url = smallLogo;
this.siteSettings.site_mobile_logo_url = mobileLogo;
this.siteSettings.site_logo_small_url = smallLogo;
this.site.mobileView = true;
},
@ -87,7 +87,7 @@ widgetTest("mobile logo", {
widgetTest("mobile without logo", {
template: '{{mount-widget widget="home-logo" args=args}}',
beforeEach() {
this.siteSettings.logo_url = bigLogo;
this.siteSettings.site_logo_url = bigLogo;
this.site.mobileView = true;
},
@ -101,8 +101,8 @@ widgetTest("basics, subfolder", {
template: '{{mount-widget widget="home-logo" args=args}}',
beforeEach() {
Discourse.BaseUri = "/forum";
this.siteSettings.logo_url = bigLogo;
this.siteSettings.logo_small_url = smallLogo;
this.siteSettings.site_logo_url = bigLogo;
this.siteSettings.site_logo_small_url = smallLogo;
this.siteSettings.title = title;
this.set("args", { minimized: false });
},
@ -118,8 +118,8 @@ widgetTest("basics, subfolder - minimized", {
template: '{{mount-widget widget="home-logo" args=args}}',
beforeEach() {
Discourse.BaseUri = "/forum";
this.siteSettings.logo_url = bigLogo;
this.siteSettings.logo_small_url = smallLogo;
this.siteSettings.site_logo_url = bigLogo;
this.siteSettings.site_logo_small_url = smallLogo;
this.siteSettings.title = title;
this.set("args", { minimized: true });
},
@ -135,8 +135,8 @@ widgetTest("mobile logo, subfolder", {
template: '{{mount-widget widget="home-logo" args=args}}',
beforeEach() {
Discourse.BaseUri = "/forum";
this.siteSettings.mobile_logo_url = mobileLogo;
this.siteSettings.logo_small_url = smallLogo;
this.siteSettings.site_mobile_logo_url = mobileLogo;
this.siteSettings.site_logo_small_url = smallLogo;
this.site.mobileView = true;
},