REFACTOR: Convert profile background uploader to be an ember component

This commit is contained in:
Robin Ward 2014-06-26 11:43:44 -04:00
parent 06880c28f8
commit 4088fba4f2
16 changed files with 118 additions and 107 deletions

View file

@ -0,0 +1,61 @@
export default Em.Component.extend({
uploading: false,
uploadProgress: 0,
backgroundStyle: function() {
var imageUrl = this.get('imageUrl');
if (Em.isNone(imageUrl)) { return; }
return "background-image: url(" + imageUrl + ")";
}.property('imageUrl'),
_initializeUploader: function() {
var $upload = this.$('input[type=file]'), // note: we can't cache this as fileupload replaces the input after upload
self = this;
$upload.fileupload({
url: this.get('uploadUrl'),
dataType: "json",
fileInput: $upload,
formData: { image_type: this.get('type') }
});
$upload.on('fileuploadsubmit', function (e, data) {
var result = Discourse.Utilities.validateUploadedFiles(data.files, true);
self.setProperties({ uploadProgress: 0, uploading: result });
return result;
});
$upload.on("fileuploadprogressall", function(e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
self.set("uploadProgress", progress);
});
$upload.on("fileuploaddone", function(e, data) {
if(data.result.url) {
self.set('imageUrl', data.result.url);
} else {
bootbox.alert(I18n.t('post.errors.upload'));
}
});
$upload.on("fileuploadfail", function(e, data) {
Discourse.Utilities.displayErrorForUpload(data);
});
$upload.on("fileuploadalways", function() {
self.setProperties({ uploading: false, uploadProgress: 0});
});
}.on('didInsertElement'),
_destroyUploader: function() {
this.$('input[type=file]').fileupload('destroy');
}.on('willDestroyElement'),
actions: {
selectFile: function() {
this.$('input[type=file]').click();
},
trash: function() {
this.set('imageUrl', null);
this.sendAction('clear');
}
}
});

View file

@ -66,7 +66,14 @@ export default Discourse.ObjectController.extend({
return this.get('saving') ? I18n.t('saving') : I18n.t('save'); return this.get('saving') ? I18n.t('saving') : I18n.t('save');
}.property('saving'), }.property('saving'),
imageUploadUrl: Discourse.computed.url('username', '/users/%@/preferences/user_image'),
actions: { actions: {
clearProfileBackground: function() {
this.get('model').clearProfileBackground();
},
save: function() { save: function() {
var self = this; var self = this;
this.setProperties({ saving: true, saved: false }); this.setProperties({ saving: true, saved: false });

View file

@ -354,8 +354,6 @@ Discourse.User = Discourse.Model.extend({
return Discourse.ajax("/users/" + this.get("username_lower") + "/preferences/profile_background/clear", { return Discourse.ajax("/users/" + this.get("username_lower") + "/preferences/profile_background/clear", {
type: 'PUT', type: 'PUT',
data: { } data: { }
}).then(function() {
user.set('profile_background', null);
}); });
}, },

View file

@ -63,12 +63,6 @@ Discourse.PreferencesRoute = Discourse.RestrictedUserRoute.extend({
avatarSelector.send('closeModal'); avatarSelector.send('closeModal');
}, },
showProfileBackgroundFileSelector: function() {
$("#profile-background-input").click();
},
clearProfileBackground: function() {
this.modelFor('user').clearProfileBackground();
}
} }
}); });

View file

@ -0,0 +1,10 @@
<input type="file" accept="image/*" style="display:none" />
<div class="uploaded-image-preview" class="input-xxlarge" {{bind-attr style="backgroundStyle"}}>
<div class="image-upload-controls">
<button {{action selectFile}} class="btn pad-left no-text"><i class="fa fa-picture-o"></i></button>
{{#if backgroundStyle}}
<button {{action trash}} class="btn btn-danger pad-left no-text"><i class="fa fa-trash-o"></i></button>
{{/if}}
<span {{bind-attr class=":btn uploading::hidden"}}>{{i18n upload_selector.uploading}} {{uploadProgress}}%</span>
</div>
</div>

View file

@ -4,6 +4,7 @@
{{#unless isUncategorizedCategory}} {{#unless isUncategorizedCategory}}
{{edit-category-tab selectedTab=selectedTab tab="security"}} {{edit-category-tab selectedTab=selectedTab tab="security"}}
{{edit-category-tab selectedTab=selectedTab tab="settings"}} {{edit-category-tab selectedTab=selectedTab tab="settings"}}
{{edit-category-tab selectedTab=selectedTab tab="images"}}
{{/unless}} {{/unless}}
</ul> </ul>

View file

@ -92,18 +92,10 @@
<div class="control-group pref-profile-bg"> <div class="control-group pref-profile-bg">
<label class="control-label">{{i18n user.change_profile_background.title}}</label> <label class="control-label">{{i18n user.change_profile_background.title}}</label>
<div class="controls"> <div class="controls">
<input type="file" id="profile-background-input" accept="image/*" style="display:none" /> {{image-uploader uploadUrl=imageUploadUrl
<div id="profile-background-preview" class="input-xxlarge" {{bind-attr style="profileBackground"}}> imageUrl=profile_background
<div id="profile-background-controls"> type="profile_background"
<button {{action showProfileBackgroundFileSelector}} class="btn pad-left no-text"><i class="fa fa-picture-o"></i></button> clear="clearProfileBackground"}}
{{#if profileBackground}}
<button {{action clearProfileBackground}} class="btn btn-danger pad-left no-text"><i class="fa fa-trash-o"></i></button>
{{/if}}
{{#if view.uploading}}
<span class="btn">{{i18n upload_selector.uploading}} {{view.uploadProgress}}%</span>
{{/if}}
</div>
</div>
</div> </div>
</div> </div>
{{/if}} {{/if}}

View file

@ -37,7 +37,7 @@ Discourse.AvatarSelectorView = Discourse.ModalBodyView.extend({
url: Discourse.getURL("/users/" + this.get("controller.username") + "/preferences/user_image"), url: Discourse.getURL("/users/" + this.get("controller.username") + "/preferences/user_image"),
dataType: "json", dataType: "json",
fileInput: $upload, fileInput: $upload,
formData: { user_image_type: "avatar" } formData: { image_type: "avatar" }
}); });
// when a file has been selected // when a file has been selected

View file

@ -1,55 +1,4 @@
/**
This view handles rendering of a user's preferences
@class PreferencesView
@extends Discourse.View
@namespace Discourse
@module Discourse
**/
Discourse.PreferencesView = Discourse.View.extend({ Discourse.PreferencesView = Discourse.View.extend({
templateName: 'user/preferences', templateName: 'user/preferences',
classNames: ['user-preferences'], classNames: ['user-preferences']
uploading: false,
uploadProgress: 0,
didInsertElement: function() {
var self = this;
var $upload = $("#profile-background-input");
this._super();
$upload.fileupload({
url: Discourse.getURL("/users/" + this.get('controller.model.username') + "/preferences/user_image"),
dataType: "json",
fileInput: $upload,
formData: { user_image_type: "profile_background" }
});
$upload.on('fileuploadsubmit', function (e, data) {
var result = Discourse.Utilities.validateUploadedFiles(data.files, true);
self.setProperties({ uploadProgress: 0, uploading: result });
return result;
});
$upload.on("fileuploadprogressall", function(e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
self.set("uploadProgress", progress);
});
$upload.on("fileuploaddone", function(e, data) {
if(data.result.url) {
self.set("controller.model.profile_background", data.result.url);
} else {
bootbox.alert(I18n.t('post.errors.upload'));
}
});
$upload.on("fileuploadfail", function(e, data) {
Discourse.Utilities.displayErrorForUpload(data);
});
$upload.on("fileuploadalways", function() {
self.setProperties({ uploading: false, uploadProgress: 0});
});
},
willDestroyElement: function() {
$("#profile-background-input").fileupload("destroy");
}
}); });

View file

@ -29,3 +29,14 @@
height: 20px; height: 20px;
} }
} }
.uploaded-image-preview {
height: 270px;
background-position: center center;
background-size: cover;
background-color: $primary;
}
.image-upload-controls {
padding: 10px;
}

View file

@ -43,19 +43,6 @@
display: none; display: none;
} }
#profile-background-preview {
height: 270px;
background-position: center center;
background-size: cover;
background-color: $primary;
}
#profile-background-controls {
padding: 10px;
}
.static { .static {
color: $primary; color: $primary;
display: inline-block; display: inline-block;

View file

@ -10,3 +10,10 @@
color: scale-color($primary, $lightness: 50%); color: scale-color($primary, $lightness: 50%);
} }
} }
.uploaded-image-preview {
height: 150px;
background-position: center center;
background-size: cover;
background-color: $primary;
}

View file

@ -63,13 +63,6 @@
padding: 5px 8px; padding: 5px 8px;
} }
#profile-background-preview {
height: 150px;
background-position: center center;
background-size: cover;
background-color: $primary;
}
.bio-composer #wmd-quote-post { .bio-composer #wmd-quote-post {
display: none; display: none;
} }

View file

@ -333,12 +333,12 @@ class UsersController < ApplicationController
# LEGACY: used by the API # LEGACY: used by the API
def upload_avatar def upload_avatar
params[:user_image_type] = "avatar" params[:image_type] = "avatar"
upload_user_image upload_user_image
end end
def upload_user_image def upload_user_image
params.require(:user_image_type) params.require(:image_type)
user = fetch_user_from_params user = fetch_user_from_params
guardian.ensure_can_edit!(user) guardian.ensure_can_edit!(user)
@ -353,7 +353,7 @@ class UsersController < ApplicationController
upload = Upload.create_for(user.id, image.file, image.filename, image.filesize) upload = Upload.create_for(user.id, image.file, image.filename, image.filesize)
if upload.errors.empty? if upload.errors.empty?
case params[:user_image_type] case params[:image_type]
when "avatar" when "avatar"
upload_avatar_for(user, upload) upload_avatar_for(user, upload)
when "profile_background" when "profile_background"

View file

@ -1158,6 +1158,7 @@ en:
change_in_category_topic: "Edit Description" change_in_category_topic: "Edit Description"
already_used: 'This color has been used by another category' already_used: 'This color has been used by another category'
security: "Security" security: "Security"
images: "Images"
auto_close_label: "Auto-close topics after:" auto_close_label: "Auto-close topics after:"
auto_close_units: "hours" auto_close_units: "hours"
email_in: "Custom incoming email address:" email_in: "Custom incoming email address:"

View file

@ -1126,7 +1126,7 @@ describe UsersController do
ActionDispatch::Http::UploadedFile.new({ filename: 'logo.png', tempfile: logo }) ActionDispatch::Http::UploadedFile.new({ filename: 'logo.png', tempfile: logo })
end end
it 'raises an error without a user_image_type param' do it 'raises an error without a image_type param' do
lambda { xhr :put, :upload_user_image, username: user.username }.should raise_error(ActionController::ParameterMissing) lambda { xhr :put, :upload_user_image, username: user.username }.should raise_error(ActionController::ParameterMissing)
end end
@ -1134,19 +1134,19 @@ describe UsersController do
it 'raises an error when you don\'t have permission to upload an user image' do it 'raises an error when you don\'t have permission to upload an user image' do
Guardian.any_instance.expects(:can_edit?).with(user).returns(false) Guardian.any_instance.expects(:can_edit?).with(user).returns(false)
xhr :post, :upload_user_image, username: user.username, user_image_type: "avatar" xhr :post, :upload_user_image, username: user.username, image_type: "avatar"
response.should be_forbidden response.should be_forbidden
end end
it 'rejects large images' do it 'rejects large images' do
SiteSetting.stubs(:max_image_size_kb).returns(1) SiteSetting.stubs(:max_image_size_kb).returns(1)
xhr :post, :upload_user_image, username: user.username, file: user_image, user_image_type: "avatar" xhr :post, :upload_user_image, username: user.username, file: user_image, image_type: "avatar"
response.status.should eq 422 response.status.should eq 422
end end
it 'rejects unauthorized images' do it 'rejects unauthorized images' do
SiteSetting.stubs(:authorized_extensions).returns(".txt") SiteSetting.stubs(:authorized_extensions).returns(".txt")
xhr :post, :upload_user_image, username: user.username, file: user_image, user_image_type: "avatar" xhr :post, :upload_user_image, username: user.username, file: user_image, image_type: "avatar"
response.status.should eq 422 response.status.should eq 422
end end
@ -1154,7 +1154,7 @@ describe UsersController do
upload = Fabricate(:upload) upload = Fabricate(:upload)
Upload.expects(:create_for).returns(upload) Upload.expects(:create_for).returns(upload)
# enqueues the user_image generator job # enqueues the user_image generator job
xhr :post, :upload_user_image, username: user.username, file: user_image, user_image_type: "avatar" xhr :post, :upload_user_image, username: user.username, file: user_image, image_type: "avatar"
# returns the url, width and height of the uploaded image # returns the url, width and height of the uploaded image
json = JSON.parse(response.body) json = JSON.parse(response.body)
json['url'].should == "/uploads/default/1/1234567890123456.png" json['url'].should == "/uploads/default/1/1234567890123456.png"
@ -1166,7 +1166,7 @@ describe UsersController do
it 'is successful for profile backgrounds' do it 'is successful for profile backgrounds' do
upload = Fabricate(:upload) upload = Fabricate(:upload)
Upload.expects(:create_for).returns(upload) Upload.expects(:create_for).returns(upload)
xhr :post, :upload_user_image, username: user.username, file: user_image, user_image_type: "profile_background" xhr :post, :upload_user_image, username: user.username, file: user_image, image_type: "profile_background"
user.reload user.reload
user.user_profile.profile_background.should == "/uploads/default/1/1234567890123456.png" user.user_profile.profile_background.should == "/uploads/default/1/1234567890123456.png"
@ -1191,13 +1191,13 @@ describe UsersController do
it 'rejects large images' do it 'rejects large images' do
SiteSetting.stubs(:max_image_size_kb).returns(1) SiteSetting.stubs(:max_image_size_kb).returns(1)
xhr :post, :upload_user_image, username: user.username, file: user_image_url, user_image_type: "profile_background" xhr :post, :upload_user_image, username: user.username, file: user_image_url, image_type: "profile_background"
response.status.should eq 422 response.status.should eq 422
end end
it 'rejects unauthorized images' do it 'rejects unauthorized images' do
SiteSetting.stubs(:authorized_extensions).returns(".txt") SiteSetting.stubs(:authorized_extensions).returns(".txt")
xhr :post, :upload_user_image, username: user.username, file: user_image_url, user_image_type: "profile_background" xhr :post, :upload_user_image, username: user.username, file: user_image_url, image_type: "profile_background"
response.status.should eq 422 response.status.should eq 422
end end
@ -1205,7 +1205,7 @@ describe UsersController do
upload = Fabricate(:upload) upload = Fabricate(:upload)
Upload.expects(:create_for).returns(upload) Upload.expects(:create_for).returns(upload)
# enqueues the user_image generator job # enqueues the user_image generator job
xhr :post, :upload_avatar, username: user.username, file: user_image_url, user_image_type: "avatar" xhr :post, :upload_avatar, username: user.username, file: user_image_url, image_type: "avatar"
json = JSON.parse(response.body) json = JSON.parse(response.body)
json['url'].should == "/uploads/default/1/1234567890123456.png" json['url'].should == "/uploads/default/1/1234567890123456.png"
json['width'].should == 100 json['width'].should == 100
@ -1216,7 +1216,7 @@ describe UsersController do
it 'is successful for profile backgrounds' do it 'is successful for profile backgrounds' do
upload = Fabricate(:upload) upload = Fabricate(:upload)
Upload.expects(:create_for).returns(upload) Upload.expects(:create_for).returns(upload)
xhr :post, :upload_user_image, username: user.username, file: user_image_url, user_image_type: "profile_background" xhr :post, :upload_user_image, username: user.username, file: user_image_url, image_type: "profile_background"
user.reload user.reload
user.user_profile.profile_background.should == "/uploads/default/1/1234567890123456.png" user.user_profile.profile_background.should == "/uploads/default/1/1234567890123456.png"
@ -1229,7 +1229,7 @@ describe UsersController do
end end
it "should handle malformed urls" do it "should handle malformed urls" do
xhr :post, :upload_user_image, username: user.username, file: "foobar", user_image_type: "profile_background" xhr :post, :upload_user_image, username: user.username, file: "foobar", image_type: "profile_background"
response.status.should eq 422 response.status.should eq 422
end end