mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-11-23 07:38:45 -05:00
Upload Logos Step
This commit is contained in:
parent
c94e6f1b96
commit
af83c8dc14
18 changed files with 223 additions and 43 deletions
|
@ -1,2 +1,4 @@
|
|||
//= require template_include.js
|
||||
//= require select2.js
|
||||
//= require jquery.ui.widget.js
|
||||
//= require jquery.fileupload.js
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
},
|
||||
});
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"}}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
<label class="wizard-btn wizard-btn-upload {{if uploading 'disabled'}}">
|
||||
{{#if uploading}}
|
||||
{{i18n "wizard.uploading"}}
|
||||
{{else}}
|
||||
{{i18n "wizard.upload"}}
|
||||
{{fa-icon "picture-o"}}
|
||||
{{/if}}
|
||||
|
||||
<input disabled={{uploading}} type="file" accept="image/*" style="visibility: hidden; position: absolute;" />
|
||||
</label>
|
||||
|
||||
{{#if field.value}}
|
||||
<div class='wizard-image-preview {{fieldClass}}'>
|
||||
<img src={{field.value}} class={{fieldClass}}>
|
||||
</div>
|
||||
{{/if}}
|
|
@ -1 +1 @@
|
|||
{{input value=field.value class=inputClassName placeholder=field.placeholder}}
|
||||
{{input value=field.value class=fieldClass placeholder=field.placeholder}}
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
<label>
|
||||
<span class='label-value'>{{field.label}}</span>
|
||||
|
||||
{{#if field.description}}
|
||||
<div class='field-description'>{{{field.description}}}</div>
|
||||
{{/if}}
|
||||
|
||||
<div class='input-area'>
|
||||
{{component inputComponentName field=field step=step inputClassName=inputClassName}}
|
||||
{{component inputComponentName field=field step=step fieldClass=fieldClass}}
|
||||
</div>
|
||||
|
||||
{{#if field.errorDescription}}
|
||||
<div class='field-error-description'>{{field.errorDescription}}</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if field.description}}
|
||||
<div class='field-description'>{{field.description}}</div>
|
||||
{{/if}}
|
||||
</label>
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
<script src="/extra-locales/wizard"></script>
|
||||
<%= csrf_meta_tags %>
|
||||
|
||||
<%= render partial: "layouts/head" %>
|
||||
<title><%= t 'wizard.title' %></title>
|
||||
</head>
|
||||
|
||||
|
|
|
@ -3231,4 +3231,6 @@ en:
|
|||
back: "Back"
|
||||
next: "Next"
|
||||
step: "Step %{current} of %{total}"
|
||||
upload: "Upload"
|
||||
uploading: "Uploading..."
|
||||
|
||||
|
|
|
@ -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 <a href=\"http://en.wikipedia.org/wiki/Favicon\">favicon</a> 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!"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in a new issue