FEATURE: Can upload images to categories

This commit is contained in:
Robin Ward 2014-06-27 15:35:25 -04:00
parent 9000c358d1
commit e22688a204
15 changed files with 102 additions and 24 deletions

View File

@ -13,7 +13,19 @@ export default Em.Component.extend(UploadMixin, {
this.set('imageUrl', data.result.url); this.set('imageUrl', data.result.url);
}, },
deleteDone: function() { actions: {
this.set('imageUrl', null); trash: function() {
this.set('imageUrl', null);
// Do we want to signal the delete to the server right away?
if (this.get('instantDelete')) {
Discourse.ajax(this.get('uploadUrl'), {
type: 'DELETE',
data: { image_type: this.get('type') }
}).then(null, function() {
bootbox.alert(I18n.t('generic_error'));
});
}
}
} }
}); });

View File

@ -9,6 +9,7 @@
**/ **/
export default Discourse.ObjectController.extend(Discourse.ModalFunctionality, { export default Discourse.ObjectController.extend(Discourse.ModalFunctionality, {
foregroundColors: ['FFFFFF', '000000'], foregroundColors: ['FFFFFF', '000000'],
categoryUploadUrl: '/category/uploads',
parentCategories: function() { parentCategories: function() {
return Discourse.Category.list().filter(function (c) { return Discourse.Category.list().filter(function (c) {

View File

@ -53,18 +53,6 @@ export default Em.Mixin.create({
actions: { actions: {
selectFile: function() { selectFile: function() {
this.$('input[type=file]').click(); this.$('input[type=file]').click();
},
trash: function() {
var self = this;
Discourse.ajax(this.get('uploadUrl'), {
type: 'DELETE',
data: { image_type: this.get('type') }
}).then(function() {
self.deleteDone();
}).catch(function() {
bootbox.alert(I18n.t('generic_error'));
});
} }
} }
}); });

View File

@ -68,7 +68,9 @@ Discourse.Category = Discourse.Model.extend({
position: this.get('position'), position: this.get('position'),
email_in: this.get('email_in'), email_in: this.get('email_in'),
email_in_allow_strangers: this.get('email_in_allow_strangers'), email_in_allow_strangers: this.get('email_in_allow_strangers'),
parent_category_id: this.get('parent_category_id') parent_category_id: this.get('parent_category_id'),
logo_url: this.get('logo_url'),
background_url: this.get('background_url')
}, },
type: this.get('id') ? 'PUT' : 'POST' type: this.get('id') ? 'PUT' : 'POST'
}); });

View File

@ -0,0 +1,9 @@
<section class='field'>
<label>{{i18n category.logo}}</label>
{{image-uploader uploadUrl=categoryUploadUrl imageUrl=logo_url type="logo"}}
</section>
<section class='field'>
<label>{{i18n category.background_image}}</label>
{{image-uploader uploadUrl=categoryUploadUrl imageUrl=background_url type="background"}}
</section>

View File

@ -94,6 +94,7 @@
<div class="controls"> <div class="controls">
{{image-uploader uploadUrl=imageUploadUrl {{image-uploader uploadUrl=imageUploadUrl
imageUrl=profile_background imageUrl=profile_background
instantDelete="true"
type="profile_background"}} type="profile_background"}}
</div> </div>
</div> </div>

View File

@ -186,3 +186,9 @@ animation: modal .25s;
.uploaded-avatar { .uploaded-avatar {
margin-top: 20px; margin-top: 20px;
} }
.uploaded-image-preview {
width: 400px;
max-height: 150px;
margin-bottom: 10px;
}

View File

@ -26,6 +26,19 @@ class CategoriesController < ApplicationController
end end
end end
def upload
params.require(:image_type)
guardian.ensure_can_create!(Category)
file = params[:file] || params[:files].first
upload = Upload.create_for(current_user.id, file.tempfile, file.original_filename, File.size(file.tempfile))
if upload.errors.blank?
render json: { url: upload.url, width: upload.width, height: upload.height }
else
render status: 422, text: upload.errors.full_messages
end
end
def move def move
guardian.ensure_can_create!(Category) guardian.ensure_can_create!(Category)
@ -105,7 +118,7 @@ class CategoriesController < ApplicationController
end end
end end
params.permit(*required_param_keys, :position, :email_in, :email_in_allow_strangers, :parent_category_id, :auto_close_hours, :permissions => [*p.try(:keys)]) params.permit(*required_param_keys, :position, :email_in, :email_in_allow_strangers, :parent_category_id, :auto_close_hours, :logo_url, :background_url, :permissions => [*p.try(:keys)])
end end
end end

View File

@ -6,7 +6,11 @@ module Jobs
def execute(args) def execute(args)
return unless SiteSetting.clean_up_uploads? return unless SiteSetting.clean_up_uploads?
uploads_used_as_profile_backgrounds = UserProfile.uniq.where("profile_background IS NOT NULL AND profile_background != ''").pluck(:profile_background) ignore_urls = []
ignore_urls << UserProfile.uniq.where("profile_background IS NOT NULL AND profile_background != ''").pluck(:profile_background)
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)
ignore_urls.flatten!
grace_period = [SiteSetting.clean_orphan_uploads_grace_period_hours, 1].max grace_period = [SiteSetting.clean_orphan_uploads_grace_period_hours, 1].max
@ -14,7 +18,7 @@ module Jobs
.where("id NOT IN (SELECT upload_id from post_uploads)") .where("id NOT IN (SELECT upload_id from post_uploads)")
.where("id NOT IN (SELECT custom_upload_id from user_avatars)") .where("id NOT IN (SELECT custom_upload_id from user_avatars)")
.where("id NOT IN (SELECT gravatar_upload_id from user_avatars)") .where("id NOT IN (SELECT gravatar_upload_id from user_avatars)")
.where("url NOT IN (?)", uploads_used_as_profile_backgrounds) .where("url NOT IN (?)", ignore_urls)
.find_each do |upload| .find_each do |upload|
upload.destroy upload.destroy
end end

View File

@ -12,7 +12,9 @@ class BasicCategorySerializer < ApplicationSerializer
:read_restricted, :read_restricted,
:permission, :permission,
:parent_category_id, :parent_category_id,
:notification_level :notification_level,
:logo_url,
:background_url
def include_parent_category_id? def include_parent_category_id?
parent_category_id parent_category_id

View File

@ -1146,6 +1146,8 @@ en:
name: "Category Name" name: "Category Name"
description: "Description" description: "Description"
topic: "category topic" topic: "category topic"
logo: "Category Logo Image"
background_image: "Category Background Image"
badge_colors: "Badge colors" badge_colors: "Badge colors"
background_color: "Background color" background_color: "Background color"
foreground_color: "Foreground color" foreground_color: "Foreground color"

View File

@ -275,6 +275,7 @@ Discourse::Application.routes.draw do
resources :categories, :except => :show resources :categories, :except => :show
get "category/:id/show" => "categories#show" get "category/:id/show" => "categories#show"
post "category/uploads" => "categories#upload"
post "category/:category_id/move" => "categories#move" post "category/:category_id/move" => "categories#move"
get "category/:category.rss" => "list#category_feed", format: :rss get "category/:category.rss" => "list#category_feed", format: :rss
get "category/:parent_category/:category.rss" => "list#category_feed", format: :rss get "category/:parent_category/:category.rss" => "list#category_feed", format: :rss

View File

@ -0,0 +1,6 @@
class AddImagesToCategories < ActiveRecord::Migration
def change
add_column :categories, :logo_url, :string
add_column :categories, :background_url, :string
end
end

View File

@ -95,6 +95,37 @@ describe CategoriesController do
end end
describe "upload" do
it "requires the user to be logged in" do
lambda { xhr :post, :upload, image_type: 'logo'}.should raise_error(Discourse::NotLoggedIn)
end
describe "logged in" do
let!(:user) { log_in(:admin) }
let(:logo) { File.new("#{Rails.root}/spec/fixtures/images/logo.png") }
let(:upload) do
ActionDispatch::Http::UploadedFile.new({ filename: 'logo.png', tempfile: logo })
end
it "raises an error when you don't have permission to upload" do
Guardian.any_instance.expects(:can_create?).with(Category).returns(false)
xhr :post, :upload, image_type: 'logo', file: upload
response.should be_forbidden
end
it "requires the `image_type` param" do
-> { xhr :post, :upload }.should raise_error(ActionController::ParameterMissing)
end
it "calls Upload.create_for" do
Upload.expects(:create_for).returns(Upload.new)
xhr :post, :upload, image_type: 'logo', file: upload
response.should be_success
end
end
end
describe "update" do describe "update" do
it "requires the user to be logged in" do it "requires the user to be logged in" do

View File

@ -1272,7 +1272,7 @@ describe UsersController do
describe '.destroy_user_image' do describe '.destroy_user_image' do
it 'raises an error when not logged in' do it 'raises an error when not logged in' do
lambda { xhr :put, :destroy_user_image, type: 'profile_background', username: 'asdf' }.should raise_error(Discourse::NotLoggedIn) lambda { xhr :delete, :destroy_user_image, type: 'profile_background', username: 'asdf' }.should raise_error(Discourse::NotLoggedIn)
end end
context 'while logged in' do context 'while logged in' do
@ -1281,20 +1281,20 @@ describe UsersController do
it 'raises an error when you don\'t have permission to clear the profile background' do it 'raises an error when you don\'t have permission to clear the profile background' do
Guardian.any_instance.expects(:can_edit?).with(user).returns(false) Guardian.any_instance.expects(:can_edit?).with(user).returns(false)
xhr :put, :destroy_user_image, username: user.username, image_type: 'profile_background' xhr :delete, :destroy_user_image, username: user.username, image_type: 'profile_background'
response.should be_forbidden response.should be_forbidden
end end
it "requires the `image_type` param" do it "requires the `image_type` param" do
-> { xhr :put, :destroy_user_image, username: user.username }.should raise_error(ActionController::ParameterMissing) -> { xhr :delete, :destroy_user_image, username: user.username }.should raise_error(ActionController::ParameterMissing)
end end
it "only allows certain `image_types`" do it "only allows certain `image_types`" do
-> { xhr :put, :destroy_user_image, username: user.username, image_type: 'wat' }.should raise_error(Discourse::InvalidParameters) -> { xhr :delete, :destroy_user_image, username: user.username, image_type: 'wat' }.should raise_error(Discourse::InvalidParameters)
end end
it 'can clear the profile background' do it 'can clear the profile background' do
xhr :put, :destroy_user_image, image_type: 'profile_background', username: user.username xhr :delete, :destroy_user_image, image_type: 'profile_background', username: user.username
user.reload.user_profile.profile_background.should == "" user.reload.user_profile.profile_background.should == ""
response.should be_success response.should be_success
end end