diff --git a/app/assets/javascripts/wizard-vendor.js b/app/assets/javascripts/wizard-vendor.js
index d25fb76a086..1e8eb3fc888 100644
--- a/app/assets/javascripts/wizard-vendor.js
+++ b/app/assets/javascripts/wizard-vendor.js
@@ -1,2 +1,4 @@
//= require template_include.js
//= require select2.js
+//= require jquery.ui.widget.js
+//= require jquery.fileupload.js
diff --git a/app/assets/javascripts/wizard/components/wizard-field-image.js.es6 b/app/assets/javascripts/wizard/components/wizard-field-image.js.es6
new file mode 100644
index 00000000000..5a2d225a561
--- /dev/null
+++ b/app/assets/javascripts/wizard/components/wizard-field-image.js.es6
@@ -0,0 +1,31 @@
+import { getToken } from 'wizard/lib/ajax';
+
+export default Ember.Component.extend({
+ classNames: ['wizard-image-row'],
+
+ uploading: false,
+
+ didInsertElement() {
+ this._super();
+
+ const $upload = this.$();
+
+ const id = this.get('field.id');
+
+ $upload.fileupload({
+ url: "/uploads.json",
+ formData: { synchronous: true,
+ type: `wizard_${id}`,
+ authenticity_token: getToken() },
+ dataType: 'json',
+ dropZone: $upload,
+ });
+
+ $upload.on("fileuploadsubmit", () => this.set('uploading', true));
+
+ $upload.on('fileuploaddone', (e, response) => {
+ this.set('field.value', response.result.url);
+ this.set('uploading', false);
+ });
+ },
+});
diff --git a/app/assets/javascripts/wizard/components/wizard-field.js.es6 b/app/assets/javascripts/wizard/components/wizard-field.js.es6
index 67b65ee47b7..4b09212fbeb 100644
--- a/app/assets/javascripts/wizard/components/wizard-field.js.es6
+++ b/app/assets/javascripts/wizard/components/wizard-field.js.es6
@@ -1,10 +1,13 @@
import computed from 'ember-addons/ember-computed-decorators';
export default Ember.Component.extend({
- classNameBindings: [':wizard-field', ':text-field', 'field.invalid'],
+ classNameBindings: [':wizard-field', 'typeClass', 'field.invalid'],
+
+ @computed('field.type')
+ typeClass: type => `${Ember.String.dasherize(type)}-field`,
@computed('field.id')
- inputClassName: id => `field-${Ember.String.dasherize(id)}`,
+ fieldClass: id => `field-${Ember.String.dasherize(id)}`,
@computed('field.type', 'field.id')
inputComponentName(type, id) {
diff --git a/app/assets/javascripts/wizard/lib/ajax.js.es6 b/app/assets/javascripts/wizard/lib/ajax.js.es6
index b4b78c773c3..6e4c2beaef7 100644
--- a/app/assets/javascripts/wizard/lib/ajax.js.es6
+++ b/app/assets/javascripts/wizard/lib/ajax.js.es6
@@ -1,16 +1,17 @@
let token;
-export function ajax(args) {
-
+export function getToken() {
if (!token) {
token = $('meta[name="csrf-token"]').attr('content');
}
+ return token;
+}
+
+export function ajax(args) {
return new Ember.RSVP.Promise((resolve, reject) => {
- args.headers = {
- 'X-CSRF-Token': token
- };
+ args.headers = { 'X-CSRF-Token': getToken() };
args.success = data => Ember.run(null, resolve, data);
args.error = xhr => Ember.run(null, reject, xhr);
Ember.$.ajax(args);
diff --git a/app/assets/javascripts/wizard/templates/components/wizard-field-dropdown.hbs b/app/assets/javascripts/wizard/templates/components/wizard-field-dropdown.hbs
index 85dd971b013..e993ecb95ef 100644
--- a/app/assets/javascripts/wizard/templates/components/wizard-field-dropdown.hbs
+++ b/app/assets/javascripts/wizard/templates/components/wizard-field-dropdown.hbs
@@ -1 +1 @@
-{{combo-box class=inputClassName value=field.value content=field.choices nameProperty="label" width="400px"}}
+{{combo-box class=fieldClass value=field.value content=field.choices nameProperty="label" width="400px"}}
diff --git a/app/assets/javascripts/wizard/templates/components/wizard-field-image.hbs b/app/assets/javascripts/wizard/templates/components/wizard-field-image.hbs
new file mode 100644
index 00000000000..6e798194bb4
--- /dev/null
+++ b/app/assets/javascripts/wizard/templates/components/wizard-field-image.hbs
@@ -0,0 +1,16 @@
+
+
+{{#if field.value}}
+
+
+
+{{/if}}
diff --git a/app/assets/javascripts/wizard/templates/components/wizard-field-text.hbs b/app/assets/javascripts/wizard/templates/components/wizard-field-text.hbs
index 2413f146dc3..2a0770e896a 100644
--- a/app/assets/javascripts/wizard/templates/components/wizard-field-text.hbs
+++ b/app/assets/javascripts/wizard/templates/components/wizard-field-text.hbs
@@ -1 +1 @@
-{{input value=field.value class=inputClassName placeholder=field.placeholder}}
+{{input value=field.value class=fieldClass placeholder=field.placeholder}}
diff --git a/app/assets/javascripts/wizard/templates/components/wizard-field.hbs b/app/assets/javascripts/wizard/templates/components/wizard-field.hbs
index 973630851f0..21f76b26930 100644
--- a/app/assets/javascripts/wizard/templates/components/wizard-field.hbs
+++ b/app/assets/javascripts/wizard/templates/components/wizard-field.hbs
@@ -1,15 +1,16 @@
diff --git a/app/assets/stylesheets/wizard.scss b/app/assets/stylesheets/wizard.scss
index cfb048e5b6c..024899c7a37 100644
--- a/app/assets/stylesheets/wizard.scss
+++ b/app/assets/stylesheets/wizard.scss
@@ -71,6 +71,52 @@ body.wizard {
}
}
+ .wizard-btn {
+ border-radius: 2px;
+ font-size: 1.0em;
+ border: 0px;
+ padding: 0.5em;
+ outline: 0;
+ transition: background-color .3s;
+ margin-right: 0.5em;
+
+ background-color: #fff;
+ color: #333;
+ box-shadow: 0 1px 4px rgba(0, 0, 0, .4);
+
+ &:hover {
+ background-color: #eee;
+ }
+
+ &:active {
+ background-color: #ddd;
+ }
+
+ &:disabled {
+ background-color: #ccc;
+ }
+
+ &.disabled {
+ background-color: #ccc;
+ }
+
+ i.fa-chevron-right {
+ margin-left: 0.25em;
+ font-size: 0.8em;
+ }
+ i.fa-chevron-left {
+ margin-right: 0.25em;
+ font-size: 0.8em;
+ }
+ }
+
+ .wizard-btn-upload {
+ clear: both;
+ display: inline-block;
+ .fa {
+ margin-left: 0.5em;
+ }
+ }
.wizard-step-footer {
display: flex;
@@ -78,17 +124,10 @@ body.wizard {
justify-content: space-between;
align-items: center;
- button.wizard-btn {
- border-radius: 2px;
- box-shadow: 0 1px 4px rgba(0, 0, 0, .6);
- font-size: 1.0em;
+ .wizard-btn.next {
background-color: #6699ff;
color: white;
- border: 0px;
- padding: 0.5em;
- outline: 0;
- transition: background-color .3s;
- margin-right: 0.5em;
+ box-shadow: 0 1px 4px rgba(0, 0, 0, .6);
&:hover {
background-color: #80B3FF;
@@ -102,17 +141,6 @@ body.wizard {
background-color: #000167;
}
- i.fa-chevron-right {
- margin-left: 0.25em;
- font-size: 0.8em;
- }
- i.fa-chevron-left {
- margin-right: 0.25em;
- font-size: 0.8em;
- }
- }
-
- button.wizard-btn.next {
min-width: 70px;
i.fa-chevron-right {
@@ -121,7 +149,7 @@ body.wizard {
}
}
- button.wizard-btn.back {
+ .wizard-btn.back {
background-color: #fff;
color: #333;
box-shadow: 0 1px 4px rgba(0, 0, 0, .4);
@@ -144,6 +172,7 @@ body.wizard {
}
button.wizard-btn.done {
+ color: #fff;
background-color: #33B333;
&:hover {
@@ -160,6 +189,34 @@ body.wizard {
}
}
+ .wizard-image-row {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ .wizard-image-preview {
+ img.field-logo-url {
+ max-height: 40px;
+ }
+ img.field-logo-small-url {
+ max-height: 40px;
+ max-width: 80px;
+ }
+ img.field-favicon-url {
+ max-height: 16px;
+ max-width: 16px;
+ }
+ img.field-apple-touch-icon-url {
+ max-height: 40px;
+ max-width: 40px;
+ }
+
+ padding: 0.1em;
+ border: 1px dotted #bbb;
+ }
+
.wizard-field {
label .label-value {
font-weight: bold;
@@ -177,6 +234,10 @@ body.wizard {
.field-description {
color: #999;
margin-top: 0.5em;
+
+ a {
+ color: #7B68EE;
+ }
}
&.text-field {
diff --git a/app/controllers/uploads_controller.rb b/app/controllers/uploads_controller.rb
index 95413db5a42..0f6ccba284a 100644
--- a/app/controllers/uploads_controller.rb
+++ b/app/controllers/uploads_controller.rb
@@ -7,7 +7,7 @@ class UploadsController < ApplicationController
file = params[:file] || params[:files].try(:first)
url = params[:url]
client_id = params[:client_id]
- synchronous = is_api? && params[:synchronous]
+ synchronous = (current_user.staff? || is_api?) && params[:synchronous]
if type == "avatar"
if SiteSetting.sso_overrides_avatar || !SiteSetting.allow_uploaded_avatars
diff --git a/app/jobs/scheduled/clean_up_uploads.rb b/app/jobs/scheduled/clean_up_uploads.rb
index b4b55ae5c20..88d29c0c8a5 100644
--- a/app/jobs/scheduled/clean_up_uploads.rb
+++ b/app/jobs/scheduled/clean_up_uploads.rb
@@ -11,6 +11,10 @@ module Jobs
ignore_urls |= Category.uniq.where("logo_url IS NOT NULL AND logo_url != ''").pluck(:logo_url)
ignore_urls |= Category.uniq.where("background_url IS NOT NULL AND background_url != ''").pluck(:background_url)
+ # Any URLs in site settings are fair game
+ ignore_urls |= [SiteSetting.logo_url, SiteSetting.logo_small_url, SiteSetting.favicon_url,
+ SiteSetting.apple_touch_icon_url]
+
ids = []
ids |= PostUpload.uniq.pluck(:upload_id)
ids |= User.uniq.where("uploaded_avatar_id IS NOT NULL").pluck(:uploaded_avatar_id)
diff --git a/app/views/wizard/index.html.erb b/app/views/wizard/index.html.erb
index 153026a2fac..fad9c7d92d6 100644
--- a/app/views/wizard/index.html.erb
+++ b/app/views/wizard/index.html.erb
@@ -9,6 +9,7 @@
<%= csrf_meta_tags %>
+ <%= render partial: "layouts/head" %>
<%= t 'wizard.title' %>
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 3f73411d392..41eb7ddb56d 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -3231,4 +3231,6 @@ en:
back: "Back"
next: "Next"
step: "Step %{current} of %{total}"
+ upload: "Upload"
+ uploading: "Uploading..."
diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml
index 97ec50c98ff..605177aebfd 100644
--- a/config/locales/server.en.yml
+++ b/config/locales/server.en.yml
@@ -3258,6 +3258,21 @@ en:
options:
default: "Simple"
dark: "Dark"
+ logos:
+ title: "Personalize your Forum"
+ fields:
+ logo_url:
+ label: "Site Logo"
+ description: "The logo image at the top left of your site, should be a wide rectangle shape."
+ logo_small_url:
+ label: "Small Logo"
+ description: "The small logo image at the top left of your site, should be a square shape, seen when scrolling down."
+ favicon_url:
+ label: "Small Icon"
+ description: "A favicon for your site. To work correctly over a CDN it must be a png."
+ apple_touch_icon_url:
+ label: "Large Icon"
+ description: "Icon used for Apple touch devices. Recommended size is 144px by 144px."
finished:
title: "Your Discourse Forum is Ready!"
diff --git a/lib/wizard.rb b/lib/wizard.rb
index ba0685aeabf..a58e7332b75 100644
--- a/lib/wizard.rb
+++ b/lib/wizard.rb
@@ -59,7 +59,6 @@ class Wizard
end
wizard.append_step('colors') do |step|
-
theme_id = ColorScheme.where(via_wizard: true).pluck(:theme_id)
theme_id = theme_id.present? ? theme_id[0] : 'default'
@@ -68,6 +67,13 @@ class Wizard
step.add_field(id: 'theme_preview', type: 'component')
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: 'favicon_url', type: 'image', value: SiteSetting.favicon_url)
+ step.add_field(id: 'apple_touch_icon_url', type: 'image', value: SiteSetting.apple_touch_icon_url)
+ end
+
wizard.append_step('finished')
wizard
diff --git a/lib/wizard/step_updater.rb b/lib/wizard/step_updater.rb
index fe0c0278aa1..5e473fe21a3 100644
--- a/lib/wizard/step_updater.rb
+++ b/lib/wizard/step_updater.rb
@@ -59,6 +59,13 @@ class Wizard
end
end
+ def update_logos(fields)
+ update_setting(:logo_url, fields, :logo_url)
+ update_setting(:logo_small_url, fields, :logo_small_url)
+ update_setting(:favicon_url, fields, :favicon_url)
+ update_setting(:apple_touch_icon_url, fields, :apple_touch_icon_url)
+ end
+
def success?
@errors.blank?
end
diff --git a/spec/components/step_updater_spec.rb b/spec/components/step_updater_spec.rb
index b61e3a288cb..4867ddb5a05 100644
--- a/spec/components/step_updater_spec.rb
+++ b/spec/components/step_updater_spec.rb
@@ -60,7 +60,7 @@ describe Wizard::StepUpdater do
let!(:color_scheme) { Fabricate(:color_scheme, name: 'existing', via_wizard: true) }
it "updates the scheme" do
- updater.update(color_scheme: 'dark')
+ updater.update(theme_id: 'dark')
expect(updater.success?).to eq(true)
color_scheme.reload
@@ -72,7 +72,7 @@ describe Wizard::StepUpdater do
context "without an existing scheme" do
it "creates the scheme" do
- updater.update(color_scheme: 'dark')
+ updater.update(theme_id: 'dark')
expect(updater.success?).to eq(true)
color_scheme = ColorScheme.where(via_wizard: true).first
@@ -83,4 +83,24 @@ describe Wizard::StepUpdater do
end
end
+ context "logos step" do
+ let(:updater) { Wizard::StepUpdater.new(user, 'logos') }
+
+ it "updates the fields correctly" do
+ updater.update(
+ logo_url: '/uploads/logo.png',
+ logo_small_url: '/uploads/logo-small.png',
+ favicon_url: "/uploads/favicon.png",
+ apple_touch_icon_url: "/uploads/apple.png"
+ )
+
+ expect(updater).to be_success
+ expect(SiteSetting.logo_url).to eq('/uploads/logo.png')
+ expect(SiteSetting.logo_small_url).to eq('/uploads/logo-small.png')
+ expect(SiteSetting.favicon_url).to eq('/uploads/favicon.png')
+ expect(SiteSetting.apple_touch_icon_url).to eq('/uploads/apple.png')
+ end
+ end
+
+
end
diff --git a/spec/jobs/clean_up_uploads_spec.rb b/spec/jobs/clean_up_uploads_spec.rb
index 9b7f22d9943..4341ec728ed 100644
--- a/spec/jobs/clean_up_uploads_spec.rb
+++ b/spec/jobs/clean_up_uploads_spec.rb
@@ -23,6 +23,16 @@ describe Jobs::CleanUpUploads do
expect(Upload.count).to be(0)
end
+ it "does not clean up uploads in site settings" do
+ logo_upload = fabricate_upload
+ SiteSetting.logo_url = logo_upload.url
+
+ Jobs::CleanUpUploads.new.execute(nil)
+
+ expect(Upload.find_by(id: @upload.id)).to eq(nil)
+ expect(Upload.find_by(id: logo_upload.id)).to eq(logo_upload)
+ end
+
it "does not delete profile background uploads" do
profile_background_upload = fabricate_upload
UserProfile.last.update_attributes!(profile_background: profile_background_upload.url)
@@ -45,7 +55,7 @@ describe Jobs::CleanUpUploads do
it "does not delete category logo uploads" do
category_logo_upload = fabricate_upload
- category = Fabricate(:category, logo_url: category_logo_upload.url)
+ Fabricate(:category, logo_url: category_logo_upload.url)
Jobs::CleanUpUploads.new.execute(nil)
@@ -55,7 +65,7 @@ describe Jobs::CleanUpUploads do
it "does not delete category background url uploads" do
category_background_url = fabricate_upload
- category = Fabricate(:category, background_url: category_background_url.url)
+ Fabricate(:category, background_url: category_background_url.url)
Jobs::CleanUpUploads.new.execute(nil)
@@ -65,7 +75,7 @@ describe Jobs::CleanUpUploads do
it "does not delete post uploads" do
upload = fabricate_upload
- post = Fabricate(:post, uploads: [upload])
+ Fabricate(:post, uploads: [upload])
Jobs::CleanUpUploads.new.execute(nil)
@@ -75,7 +85,7 @@ describe Jobs::CleanUpUploads do
it "does not delete user uploaded avatar" do
upload = fabricate_upload
- user = Fabricate(:user, uploaded_avatar: upload)
+ Fabricate(:user, uploaded_avatar: upload)
Jobs::CleanUpUploads.new.execute(nil)
@@ -85,7 +95,7 @@ describe Jobs::CleanUpUploads do
it "does not delete user gravatar" do
upload = fabricate_upload
- user = Fabricate(:user, user_avatar: Fabricate(:user_avatar, gravatar_upload: upload))
+ Fabricate(:user, user_avatar: Fabricate(:user_avatar, gravatar_upload: upload))
Jobs::CleanUpUploads.new.execute(nil)