remove useless upload topic direct association

This commit is contained in:
Régis Hanol 2013-06-15 09:54:49 +02:00
parent e603c85fa0
commit 6ea91b4416
6 changed files with 81 additions and 93 deletions

View file

@ -44,15 +44,15 @@ Discourse.ComposerView = Discourse.View.extend({
}.property('content.createdPost'), }.property('content.createdPost'),
observeReplyChanges: function() { observeReplyChanges: function() {
var _this = this; var composerView = this;
if (this.get('content.hidePreview')) return; if (this.get('content.hidePreview')) return;
Ember.run.next(null, function() { Ember.run.next(null, function() {
var $wmdPreview, caretPosition; var $wmdPreview, caretPosition;
if (_this.editor) { if (composerView.editor) {
_this.editor.refreshPreview(); composerView.editor.refreshPreview();
// if the caret is on the last line ensure preview scrolled to bottom // if the caret is on the last line ensure preview scrolled to bottom
caretPosition = Discourse.Utilities.caretPosition(_this.wmdInput[0]); caretPosition = Discourse.Utilities.caretPosition(composerView.wmdInput[0]);
if (!_this.wmdInput.val().substring(caretPosition).match(/\n/)) { if (!composerView.wmdInput.val().substring(caretPosition).match(/\n/)) {
$wmdPreview = $('#wmd-preview'); $wmdPreview = $('#wmd-preview');
if ($wmdPreview.is(':visible')) { if ($wmdPreview.is(':visible')) {
return $wmdPreview.scrollTop($wmdPreview[0].scrollHeight); return $wmdPreview.scrollTop($wmdPreview[0].scrollHeight);
@ -164,53 +164,50 @@ Discourse.ComposerView = Discourse.View.extend({
initEditor: function() { initEditor: function() {
// not quite right, need a callback to pass in, meaning this gets called once, // not quite right, need a callback to pass in, meaning this gets called once,
// but if you start replying to another topic it will get the avatars wrong // but if you start replying to another topic it will get the avatars wrong
var $uploadTarget, $wmdInput, editor, saveDraft, selected, template, topic, transformTemplate, var $wmdInput, editor, composerView = this;
_this = this;
this.wmdInput = $wmdInput = $('#wmd-input'); this.wmdInput = $wmdInput = $('#wmd-input');
if ($wmdInput.length === 0 || $wmdInput.data('init') === true) return; if ($wmdInput.length === 0 || $wmdInput.data('init') === true) return;
$LAB.script(assetPath('defer/html-sanitizer-bundle')); $LAB.script(assetPath('defer/html-sanitizer-bundle'));
Discourse.ComposerView.trigger("initWmdEditor"); Discourse.ComposerView.trigger("initWmdEditor");
template = Discourse.UserSelector.templateFunction(); var template = Discourse.UserSelector.templateFunction();
transformTemplate = Handlebars.compile("{{avatar this imageSize=\"tiny\"}} {{this.username}}");
$wmdInput.data('init', true); $wmdInput.data('init', true);
$wmdInput.autocomplete({ $wmdInput.autocomplete({
template: template, template: template,
dataSource: function(term) { dataSource: function(term) {
return Discourse.UserSearch.search({ return Discourse.UserSearch.search({
term: term, term: term,
topicId: _this.get('controller.controllers.topic.content.id') topicId: composerView.get('controller.controllers.topic.content.id')
}); });
}, },
key: "@", key: "@",
transformComplete: function(v) { return v.username; } transformComplete: function(v) { return v.username; }
}); });
topic = this.get('topic');
this.editor = editor = Discourse.Markdown.createEditor({ this.editor = editor = Discourse.Markdown.createEditor({
lookupAvatar: function(username) { lookupAvatar: function(username) {
return Discourse.Utilities.avatarImg({ username: username, size: 'tiny' }); return Discourse.Utilities.avatarImg({ username: username, size: 'tiny' });
} }
}); });
$uploadTarget = $('#reply-control'); var $uploadTarget = $('#reply-control');
this.editor.hooks.insertImageDialog = function(callback) { this.editor.hooks.insertImageDialog = function(callback) {
callback(null); callback(null);
_this.get('controller').send('showImageSelector', _this); composerView.get('controller').send('showImageSelector', composerView);
return true; return true;
}; };
this.editor.hooks.onPreviewRefresh = function() { this.editor.hooks.onPreviewRefresh = function() {
return _this.afterRender(); return composerView.afterRender();
}; };
this.editor.run(); this.editor.run();
this.set('editor', this.editor); this.set('editor', this.editor);
this.loadingChanged(); this.loadingChanged();
saveDraft = Discourse.debounce((function() { var saveDraft = Discourse.debounce((function() {
return _this.get('controller').saveDraft(); return composerView.get('controller').saveDraft();
}), 2000); }), 2000);
$wmdInput.keyup(function() { $wmdInput.keyup(function() {
@ -223,7 +220,7 @@ Discourse.ComposerView = Discourse.View.extend({
$replyTitle.keyup(function() { $replyTitle.keyup(function() {
saveDraft(); saveDraft();
// removes the red background once the requirements are met // removes the red background once the requirements are met
if (_this.get('controller.content.missingTitleCharacters') <= 0) { if (composerView.get('controller.content.missingTitleCharacters') <= 0) {
$replyTitle.removeClass("requirements-not-met"); $replyTitle.removeClass("requirements-not-met");
} }
return true; return true;
@ -232,7 +229,7 @@ Discourse.ComposerView = Discourse.View.extend({
// when the title field loses the focus... // when the title field loses the focus...
$replyTitle.blur(function(){ $replyTitle.blur(function(){
// ...and the requirements are not met (ie. the minimum number of characters) // ...and the requirements are not met (ie. the minimum number of characters)
if (_this.get('controller.content.missingTitleCharacters') > 0) { if (composerView.get('controller.content.missingTitleCharacters') > 0) {
// then, "redify" the background // then, "redify" the background
$replyTitle.toggleClass("requirements-not-met", true); $replyTitle.toggleClass("requirements-not-met", true);
} }
@ -245,22 +242,21 @@ Discourse.ComposerView = Discourse.View.extend({
$uploadTarget.fileupload({ $uploadTarget.fileupload({
url: Discourse.getURL('/uploads'), url: Discourse.getURL('/uploads'),
dataType: 'json', dataType: 'json',
timeout: 20000, timeout: 20000
formData: { topic_id: 1234 }
}); });
// submit - this event is triggered for each upload // submit - this event is triggered for each upload
$uploadTarget.on('fileuploadsubmit', function (e, data) { $uploadTarget.on('fileuploadsubmit', function (e, data) {
var result = Discourse.Utilities.validateFilesForUpload(data.files); var result = Discourse.Utilities.validateFilesForUpload(data.files);
// reset upload status when everything is ok // reset upload status when everything is ok
if (result) _this.setProperties({ uploadProgress: 0, loadingImage: true }); if (result) composerView.setProperties({ uploadProgress: 0, loadingImage: true });
return result; return result;
}); });
// send - this event is triggered when the upload request is about to start // send - this event is triggered when the upload request is about to start
$uploadTarget.on('fileuploadsend', function (e, data) { $uploadTarget.on('fileuploadsend', function (e, data) {
// hide the "image selector" modal // hide the "image selector" modal
_this.get('controller').send('closeModal'); composerView.get('controller').send('closeModal');
// cf. https://github.com/blueimp/jQuery-File-Upload/wiki/API#how-to-cancel-an-upload // cf. https://github.com/blueimp/jQuery-File-Upload/wiki/API#how-to-cancel-an-upload
var jqXHR = data.xhr(); var jqXHR = data.xhr();
// need to wait for the link to show up in the DOM // need to wait for the link to show up in the DOM
@ -279,21 +275,21 @@ Discourse.ComposerView = Discourse.View.extend({
// progress all // progress all
$uploadTarget.on('fileuploadprogressall', function (e, data) { $uploadTarget.on('fileuploadprogressall', function (e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10); var progress = parseInt(data.loaded / data.total * 100, 10);
_this.set('uploadProgress', progress); composerView.set('uploadProgress', progress);
}); });
// done // done
$uploadTarget.on('fileuploaddone', function (e, data) { $uploadTarget.on('fileuploaddone', function (e, data) {
var upload = data.result; var upload = data.result;
var html = "<img src=\"" + upload.url + "\" width=\"" + upload.width + "\" height=\"" + upload.height + "\">"; var html = "<img src=\"" + upload.url + "\" width=\"" + upload.width + "\" height=\"" + upload.height + "\">";
_this.addMarkdown(html); composerView.addMarkdown(html);
_this.set('loadingImage', false); composerView.set('loadingImage', false);
}); });
// fail // fail
$uploadTarget.on('fileuploadfail', function (e, data) { $uploadTarget.on('fileuploadfail', function (e, data) {
// hide upload status // hide upload status
_this.set('loadingImage', false); composerView.set('loadingImage', false);
// deal with meaningful errors first // deal with meaningful errors first
if (data.jqXHR) { if (data.jqXHR) {
switch (data.jqXHR.status) { switch (data.jqXHR.status) {
@ -321,7 +317,7 @@ Discourse.ComposerView = Discourse.View.extend({
// to finish. // to finish.
return Em.run.later(jQuery, (function() { return Em.run.later(jQuery, (function() {
var replyTitle = $('#reply-title'); var replyTitle = $('#reply-title');
_this.resize(); composerView.resize();
if (replyTitle.length) { if (replyTitle.length) {
return replyTitle.putCursorAtEnd(); return replyTitle.putCursorAtEnd();
} else { } else {

View file

@ -2,16 +2,15 @@ class UploadsController < ApplicationController
before_filter :ensure_logged_in before_filter :ensure_logged_in
def create def create
params.require(:topic_id)
file = params[:file] || params[:files].first file = params[:file] || params[:files].first
# only supports images for now # only supports images for now
return render status: 415, json: failed_json unless file.content_type =~ /^image\/.+/ return render status: 415, json: failed_json unless file.content_type =~ /^image\/.+/
upload = Upload.create_for(current_user.id, file, params[:topic_id]) upload = Upload.create_for(current_user.id, file)
render_serialized(upload, UploadSerializer, root: false) render_serialized(upload, UploadSerializer, root: false)
rescue FastImage::ImageFetchFailure rescue FastImage::ImageFetchFailure
render status: 422, text: I18n.t("upload.image.fetch_failure") render status: 422, text: I18n.t("upload.image.fetch_failure")
rescue FastImage::UnknownImageType rescue FastImage::UnknownImageType

View file

@ -5,7 +5,6 @@ require 'local_store'
class Upload < ActiveRecord::Base class Upload < ActiveRecord::Base
belongs_to :user belongs_to :user
belongs_to :topic
has_many :post_uploads has_many :post_uploads
has_many :posts, through: :post_uploads has_many :posts, through: :post_uploads
@ -13,7 +12,7 @@ class Upload < ActiveRecord::Base
validates_presence_of :filesize validates_presence_of :filesize
validates_presence_of :original_filename validates_presence_of :original_filename
def self.create_for(user_id, file, topic_id) def self.create_for(user_id, file)
# retrieve image info # retrieve image info
image_info = FastImage.new(file.tempfile, raise_on_failure: true) image_info = FastImage.new(file.tempfile, raise_on_failure: true)
# compute image aspect ratio # compute image aspect ratio
@ -21,7 +20,6 @@ class Upload < ActiveRecord::Base
upload = Upload.create!({ upload = Upload.create!({
user_id: user_id, user_id: user_id,
topic_id: topic_id,
original_filename: file.original_filename, original_filename: file.original_filename,
filesize: File.size(file.tempfile), filesize: File.size(file.tempfile),
width: width, width: width,
@ -53,7 +51,6 @@ end
# #
# id :integer not null, primary key # id :integer not null, primary key
# user_id :integer not null # user_id :integer not null
# topic_id :integer not null
# original_filename :string(255) not null # original_filename :string(255) not null
# filesize :integer not null # filesize :integer not null
# width :integer # width :integer

View file

@ -0,0 +1,9 @@
class RemoveTopicIdFromUploads < ActiveRecord::Migration
def up
remove_column :uploads, :topic_id
end
def down
add_column :uploads, :topic_id, :interger, null: false, default: -1
end
end

View file

@ -14,64 +14,54 @@ describe UploadsController do
context '.create' do context '.create' do
context 'missing params' do let(:logo) do
it 'raises an error without the topic_id param' do ActionDispatch::Http::UploadedFile.new({
-> { xhr :post, :create }.should raise_error(ActionController::ParameterMissing) filename: 'logo.png',
type: 'image/png',
tempfile: File.new("#{Rails.root}/spec/fixtures/images/logo.png")
})
end
let(:logo_dev) do
ActionDispatch::Http::UploadedFile.new({
filename: 'logo-dev.png',
type: 'image/png',
tempfile: File.new("#{Rails.root}/spec/fixtures/images/logo-dev.png")
})
end
let(:text_file) do
ActionDispatch::Http::UploadedFile.new({
filename: 'LICENSE.txt',
type: 'text/plain',
tempfile: File.new("#{Rails.root}/LICENSE.txt")
})
end
let(:files) { [ logo_dev, logo ] }
context 'with a file' do
it 'is succesful' do
xhr :post, :create, file: logo
response.should be_success
end
it 'supports only images' do
xhr :post, :create, file: text_file
response.status.should eq 415
end end
end end
context 'correct params' do context 'with some files' do
let(:logo) do it 'is succesful' do
ActionDispatch::Http::UploadedFile.new({ xhr :post, :create, files: files
filename: 'logo.png', response.should be_success
type: 'image/png',
tempfile: File.new("#{Rails.root}/spec/fixtures/images/logo.png")
})
end end
let(:logo_dev) do it 'takes the first file' do
ActionDispatch::Http::UploadedFile.new({ xhr :post, :create, files: files
filename: 'logo-dev.png', response.body.should match /logo-dev.png/
type: 'image/png',
tempfile: File.new("#{Rails.root}/spec/fixtures/images/logo-dev.png")
})
end
let(:text_file) do
ActionDispatch::Http::UploadedFile.new({
filename: 'LICENSE.txt',
type: 'text/plain',
tempfile: File.new("#{Rails.root}/LICENSE.txt")
})
end
let(:files) { [ logo_dev, logo ] }
context 'with a file' do
it 'is succesful' do
xhr :post, :create, topic_id: 1234, file: logo
response.should be_success
end
it 'supports only images' do
xhr :post, :create, topic_id: 1234, file: text_file
response.status.should eq 415
end
end
context 'with some files' do
it 'is succesful' do
xhr :post, :create, topic_id: 1234, files: files
response.should be_success
end
it 'takes the first file' do
xhr :post, :create, topic_id: 1234, files: files
response.body.should match /logo-dev.png/
end
end end
end end

View file

@ -3,7 +3,6 @@ require 'spec_helper'
describe Upload do describe Upload do
it { should belong_to :user } it { should belong_to :user }
it { should belong_to :topic }
it { should have_many :post_uploads } it { should have_many :post_uploads }
it { should have_many :posts } it { should have_many :posts }
@ -14,7 +13,6 @@ describe Upload do
context '.create_for' do context '.create_for' do
let(:user_id) { 1 } let(:user_id) { 1 }
let(:topic_id) { 42 }
let(:logo) do let(:logo) do
ActionDispatch::Http::UploadedFile.new({ ActionDispatch::Http::UploadedFile.new({
@ -24,14 +22,13 @@ describe Upload do
}) })
end end
let(:upload) { Upload.create_for(user_id, logo, topic_id) } let(:upload) { Upload.create_for(user_id, logo) }
let(:url) { "http://domain.com" } let(:url) { "http://domain.com" }
shared_examples_for "upload" do shared_examples_for "upload" do
it "is valid" do it "is valid" do
upload.user_id.should == user_id upload.user_id.should == user_id
upload.topic_id.should == topic_id
upload.original_filename.should == logo.original_filename upload.original_filename.should == logo.original_filename
upload.filesize.should == File.size(logo.tempfile) upload.filesize.should == File.size(logo.tempfile)
upload.width.should == 244 upload.width.should == 244