Merge pull request #1199 from ZogStriP/uploads

adds the `max_attachment_size_kb` setting
This commit is contained in:
Sam 2013-07-16 23:03:42 -07:00
commit c2be81a76e
19 changed files with 115 additions and 83 deletions

View File

@ -88,9 +88,8 @@ Discourse.Utilities = {
},
emailValid: function(email) {
// see: http://stackoverflow.com/questions/46155/validate-email-address-in-javascript
var re;
re = /^[a-zA-Z0-9!#$%&'*+\/=?\^_`{|}~\-]+(?:\.[a-zA-Z0-9!#$%&'\*+\/=?\^_`{|}~\-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?$/;
// see: http://stackoverflow.com/questions/46155/validate-email-address-in-javascript
var re = /^[a-zA-Z0-9!#$%&'*+\/=?\^_`{|}~\-]+(?:\.[a-zA-Z0-9!#$%&'\*+\/=?\^_`{|}~\-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?$/;
return re.test(email);
},
@ -165,40 +164,35 @@ Discourse.Utilities = {
@param {Array} files The list of files we want to upload
**/
validateFilesForUpload: function(files) {
if (files) {
// can only upload one file at a time
if (files.length > 1) {
bootbox.alert(I18n.t('post.errors.too_many_uploads'));
return false;
} else if (files.length > 0) {
var upload = files[0];
// ensures that new users can upload image
if (Discourse.User.current('trust_level') === 0 && Discourse.SiteSettings.newuser_max_images === 0) {
bootbox.alert(I18n.t('post.errors.upload_not_allowed_for_new_user'));
return false;
}
// if the image was pasted, sets its name to a default one
if (upload instanceof Blob && !(upload instanceof File) && upload.type === "image/png") { upload.name = "blob.png"; }
// check that the uploaded file is authorized
if (!Discourse.Utilities.isAuthorizedUpload(upload)) {
var extensions = Discourse.SiteSettings.authorized_extensions.replace(/\|/g, ", ");
bootbox.alert(I18n.t('post.errors.upload_not_authorized', { authorized_extensions: extensions }));
return false;
}
// check file size
if (upload.size && upload.size > 0) {
var fileSizeInKB = upload.size / 1024;
if (fileSizeInKB > Discourse.SiteSettings.max_upload_size_kb) {
bootbox.alert(I18n.t('post.errors.upload_too_large', { max_size_kb: Discourse.SiteSettings.max_upload_size_kb }));
return false;
}
// everything is fine
return true;
}
}
if (!files || files.length === 0) { return false; }
// can only upload one file at a time
if (files.length > 1) {
bootbox.alert(I18n.t('post.errors.too_many_uploads'));
return false;
}
// there has been an error
return false;
var upload = files[0];
// ensures that new users can upload image
if (Discourse.User.current('trust_level') === 0 && Discourse.SiteSettings.newuser_max_images === 0) {
bootbox.alert(I18n.t('post.errors.upload_not_allowed_for_new_user'));
return false;
}
// if the image was pasted, sets its name to a default one
if (upload instanceof Blob && !(upload instanceof File) && upload.type === "image/png") { upload.name = "blob.png"; }
// check that the uploaded file is authorized
if (!Discourse.Utilities.isAuthorizedUpload(upload)) {
var extensions = Discourse.SiteSettings.authorized_extensions.replace(/\|/g, ", ");
bootbox.alert(I18n.t('post.errors.upload_not_authorized', { authorized_extensions: extensions }));
return false;
}
// check file size
var fileSizeKB = upload.size / 1024;
var maxSizeKB = Discourse.Utilities.maxUploadSizeInKB(upload.name);
if (fileSizeKB > maxSizeKB) {
bootbox.alert(I18n.t('post.errors.upload_too_large', { max_size_kb: maxSizeKB }));
return false;
}
// everything went fine
return true;
},
/**
@ -209,8 +203,7 @@ Discourse.Utilities = {
**/
isAuthorizedUpload: function(file) {
var extensions = Discourse.SiteSettings.authorized_extensions;
if (!extensions) return false;
var regexp = new RegExp("\\.(" + extensions.replace(/\./g, "") + ")$", "i");
var regexp = new RegExp("(" + extensions + ")$", "i");
return file && file.name ? file.name.match(regexp) : false;
},
@ -221,7 +214,7 @@ Discourse.Utilities = {
@param {Upload} upload The upload we want the markdown from
**/
getUploadMarkdown: function(upload) {
if (this.isAnImage(upload.original_filename)) {
if (Discourse.Utilities.isAnImage(upload.original_filename)) {
return '<img src="' + upload.url + '" width="' + upload.width + '" height="' + upload.height + '">';
} else {
return '<a class="attachment" href="' + upload.url + '">' + upload.original_filename + '</a><span class="size">(' + I18n.toHumanSize(upload.filesize) + ')</span>';
@ -235,7 +228,17 @@ Discourse.Utilities = {
@param {String} path The path
**/
isAnImage: function(path) {
return path && path.match(/\.(png|jpg|jpeg|gif|bmp|tif)$/i);
return path && path.match(/\.(png|jpg|jpeg|gif|bmp|tif|tiff)$/i);
},
/**
Retrieve max upload size in KB depending on the file is an image or not
@method maxUploadSizeInKB
@param {String} path The path
**/
maxUploadSizeInKB: function(path) {
return Discourse.Utilities.isAnImage(path) ? Discourse.SiteSettings.max_image_size_kb : Discourse.SiteSettings.max_attachment_size_kb;
}
};

View File

@ -302,13 +302,11 @@ Discourse.ComposerView = Discourse.View.extend({
case 0: return;
// 413 == entity too large, returned usually from nginx
case 413:
bootbox.alert(I18n.t('post.errors.upload_too_large', {max_size_kb: Discourse.SiteSettings.max_upload_size_kb}));
var maxSizeKB = Discourse.Utilities.maxUploadSizeInKB(data.files[0].name);
bootbox.alert(I18n.t('post.errors.upload_too_large', { max_size_kb: maxSizeKB }));
return;
// 415 == media type not authorized
case 415:
var extensions = Discourse.SiteSettings.authorized_extensions.replace(/\|/g, ", ");
bootbox.alert(I18n.t('post.errors.upload_not_authorized', { authorized_extensions: extensions }));
return;
// 422 == there has been an error on the server (mostly due to FastImage)
case 422:
bootbox.alert(data.jqXHR.responseText);

View File

@ -4,7 +4,10 @@ class UploadsController < ApplicationController
def create
file = params[:file] || params[:files].first
return render status: 415, json: failed_json unless SiteSetting.authorized_file?(file)
unless SiteSetting.authorized_upload?(file)
text = I18n.t("upload.unauthorized", authorized_extensions: SiteSetting.authorized_extensions.gsub("|", ", "))
return render status: 415, text: text
end
upload = Upload.create_for(current_user.id, file)

View File

@ -53,7 +53,8 @@ class SiteSetting < ActiveRecord::Base
# auto-replace rules for title
setting(:title_prettify, true)
client_setting(:max_upload_size_kb, 2048)
client_setting(:max_image_size_kb, 2048)
client_setting(:max_attachment_size_kb, 1024)
client_setting(:authorized_extensions, '.jpg|.jpeg|.png|.gif')
# settings only available server side
@ -269,26 +270,36 @@ class SiteSetting < ActiveRecord::Base
top_menu_items[0].name
end
def self.anonymous_homepage
list = ['latest', 'hot', 'categories', 'category']
top_menu_items.map { |item| item.name }.select{ |item| list.include?(item) }.first
def self.anonymous_menu_items
@anonymous_menu_items ||= Set.new ['latest', 'hot', 'categories', 'category']
end
def self.authorized_file?(file)
file.original_filename =~ /\.(#{authorized_extensions.tr(". ", "")})$/i
def self.anonymous_homepage
top_menu_items.map { |item| item.name }
.select { |item| anonymous_menu_items.include?(item) }
.first
end
def self.authorized_uploads
authorized_extensions.tr(" ", "")
.split("|")
.map { |extension| (extension.start_with?(".") ? "" : ".") + extension }
end
def self.authorized_upload?(file)
authorized_uploads.count > 0 && file.original_filename =~ /(#{authorized_uploads.join("|")})$/i
end
def self.images
@images ||= ["jpg", "jpeg", "png", "gif", "tif", "tiff", "bmp"]
@images ||= Set.new [".jpg", ".jpeg", ".png", ".gif", ".tif", ".tiff", ".bmp"]
end
def self.authorized_images
authorized_uploads.select { |extension| images.include?(extension) }
end
def self.authorized_image?(file)
authorized_images = authorized_extensions
.tr(". ", "")
.split("|")
.select { |extension| images.include?(extension) }
.join("|")
file.original_filename =~ /\.(#{authorized_images})$/i
authorized_images.count > 0 && file.original_filename =~ /(#{authorized_images.join("|")})$/i
end
end

View File

@ -649,7 +649,7 @@ cs:
min_body_similar_length: "Minimální délka těla příspěvku, než budou hledána podobná témata"
category_colors: "Seznam hexadecimálních barev oddělený svislítkem (|) pro barvy kategorií"
max_upload_size_kb: "Maximální povolená velikost nahrávaných souborů v kB - nezapomeňte tento limit změnit v nginx (client_max_body_size) / apache a na proxy serverech."
max_image_size_kb: "Maximální povolená velikost nahrávaných souborů v kB - nezapomeňte tento limit změnit v nginx (client_max_body_size) / apache a na proxy serverech."
authorized_extensions: "Seznam povolených přípon souborů pro nahrávání, oddělený svislítkem (|)"
max_similar_results: "Kolik podobných témat se má zobrazovat uživateli, když vytváří nové téma"

View File

@ -592,7 +592,7 @@ de:
min_body_similar_length: "Minimale Länge eines Beitragstextes, bevor nach ähnlichen Themen gesucht wird."
category_colors: "Eine durch senkrechte Striche getrennte Liste hexadezimaler Farbwerte, die als Kategoriefarben erlaubt sind."
max_upload_size_kb: "Maximale Größe in Kilobytes (kB), die von Benutzer hochgeladene Bilder groß sein dürfen. Stelle sicher, dass dieser Wert auch in nginx (client_max_body_size) / apache und Proxies konfiguriert ist."
max_image_size_kb: "Maximale Größe in Kilobytes (kB), die von Benutzer hochgeladene Bilder groß sein dürfen. Stelle sicher, dass dieser Wert auch in nginx (client_max_body_size) / apache und Proxies konfiguriert ist."
max_similar_results: "Zahl der Themen, die ein Nutzer sieht während sei ein neues Thema erstellen."
title_prettify: "Verhindert gängige Fehler im Titel, wie reine Grossschreibung, Kleinbuchstaben am Anfang, mehrere ! und ?, überflüssiger . am Ende, etc."

View File

@ -623,7 +623,8 @@ en:
min_body_similar_length: "The minimum length of a post's body before it will be checked for similar topics"
category_colors: "A pipe (|) separated list of hexadecimal color values allowed for categories"
max_upload_size_kb: "The maximum size of files we allow users to upload in kB - be sure to configure the limit in nginx (client_max_body_size) / apache or proxy as well."
max_image_size_kb: "The maximum size of images we allow users to upload in kB - be sure to configure the limit in nginx (client_max_body_size) / apache or proxy as well."
max_attachment_size_kb: "The maximum size of files we allow users to upload in kB - be sure to configure the limit in nginx (client_max_body_size) / apache or proxy as well."
authorized_extensions: "A pipe (|) separated list of file extensions allowed for upload"
max_similar_results: "How many similar topics to show a user while they are composing a new topic"
@ -1075,6 +1076,7 @@ en:
deleted: 'deleted'
upload:
unauthorized: "Sorry, the file you are trying to upload is not authorized (authorized extensions: %{authorized_extensions})."
pasted_image_filename: "Pasted image"
image:
fetch_failure: "Sorry, there has been an error while fetching the image."

View File

@ -581,7 +581,8 @@ fr:
min_body_similar_length: "La taille minimale du message avant que l'on vérifie l'existence de discussions identiques"
category_colors: "Une liste de couleurs autorisées pour les catégories (au format hexadécimal, séparés par un |)"
max_upload_size_kb: "La taille maximum des fichiers que les utilisateurs peuvent envoyer en kB - assurez-vous de configurer également cette limite dans nginx (client_max_body_size) / apache ou votre proxy."
max_image_size_kb: "La taille maximum des images que les utilisateurs peuvent envoyer en kB - assurez-vous de configurer également cette limite dans nginx (client_max_body_size) / apache ou votre proxy."
max_attachment_size_kb: "La taille maximum des fichiers que les utilisateurs peuvent envoyer en kB - assurez-vous de configurer également cette limite dans nginx (client_max_body_size) / apache ou votre proxy."
max_similar_results: "Nombre de discussions similaires à afficher lorsqu'un utilisateur est en train de créer une nouvelle discussion"
title_prettify: "Corrige les coquilles les plus communes dans les titres (intégralité du titre en majuscule, première lettre en minuscule, de multiples ! et ?, un . inutile à la fin, etc.)"

View File

@ -422,7 +422,7 @@ id:
min_body_similar_length: "The minimum length of a post's body before it will be checked for similar topics"
category_colors: "A pipe (|) separated list of hexadecimal color values allowed for categories"
max_upload_size_kb: "The maximum size of files we allow users to upload in kB - be sure to configure the limit in nginx (client_max_body_size) / apache or proxy as well."
max_image_size_kb: "The maximum size of files we allow users to upload in kB - be sure to configure the limit in nginx (client_max_body_size) / apache or proxy as well."
max_similar_results: "How many similar topics to show a user while they are composing a new topic"
notification_types:

View File

@ -560,7 +560,7 @@ it:
min_body_similar_length: "Lunghezza minima del contenuto di un post perattivare il controllo su topic simili"
category_colors: "Lista di colori esadecimali per sfondo categorie, separati da pipe (\"|\")"
max_upload_size_kb: "Dimensione massima in kB per gli upload degli utenti - assicurati di configurare il limite in nginx (client_max_body_size) o apache."
max_image_size_kb: "Dimensione massima in kB per gli upload degli utenti - assicurati di configurare il limite in nginx (client_max_body_size) o apache."
max_similar_results: "Numero di topic simili da mostrare all'utente durante la creazione di un nuovo topic"
title_prettify: "Previeni typo e errori comuni nei titoli, inclusi tutto maiuscola, primo carattere minuscolo, ! e ? ripetuti, . aggiuntivi alla fine, etc etc."

View File

@ -622,7 +622,7 @@ nl:
min_body_similar_length: De minimale lengte die de inhoud van een bericht moet hebben voordat er wordt gezocht naar vergelijkbare topics
category_colors: "Een lijst, gescheiden door een pipe (|), van hexadecimal kleurwaardes die gebruikt kunnen worden voor categorien"
max_upload_size_kb: "De maximale bestandsgrootte die we toestaan voor uploads, in kB. Zorg er voor dat deze limiet ook ingesteld is in nginx (client_max_body_size) / apache of een proxy."
max_image_size_kb: "De maximale bestandsgrootte die we toestaan voor uploads, in kB. Zorg er voor dat deze limiet ook ingesteld is in nginx (client_max_body_size) / apache of een proxy."
authorized_extensions: "Een met pipes (|) gescheiden lijst van bestandsextensies die mogen worden geupload"
max_similar_results: "How many similar topics to show a user while they are composing a new topic"

View File

@ -767,7 +767,7 @@ pseudo:
ƀé čĥéčǩéď ƒóř šíɱíłář ťóƿíčš ]]'
category_colors: '[[ Á ƿíƿé (|) šéƿářáťéď łíšť óƒ ĥéхáďéčíɱáł čółóř νáłůéš áłłóŵéď
ƒóř čáťéǧóříéš ]]'
max_upload_size_kb: '[[ Ťĥé ɱáхíɱůɱ šížé óƒ ƒíłéš ŵé áłłóŵ ůšéřš ťó ůƿłóáď íɳ
max_image_size_kb: '[[ Ťĥé ɱáхíɱůɱ šížé óƒ ƒíłéš ŵé áłłóŵ ůšéřš ťó ůƿłóáď íɳ
ǩƁ - ƀé šůřé ťó čóɳƒíǧůřé ťĥé łíɱíť íɳ ɳǧíɳх (čłíéɳť_ɱáх_ƀóďý_šížé) / áƿáčĥé
óř ƿřóхý áš ŵéłł. ]]'
authorized_extensions: '[[ Á ƿíƿé (|) šéƿářáťéď łíšť óƒ ƒíłé éхťéɳšíóɳš áłłóŵéď

View File

@ -643,7 +643,7 @@ ru:
min_title_similar_length: Минимальная длина названия темы, при которой тема будет проверена на наличие похожих
min_body_similar_length: Минимальная длина тела сообщения, при которой оно будет проверено на наличие похожих тем
category_colors: Разделенный чертой (|) список дозволенных hexadecimal цветов для категорий
max_upload_size_kb: Максимальный размер файлов для загрузки пользователем в кб убедитесь, что вы настроили лимит также в nginx (client_max_body_size) / apache или proxy.
max_image_size_kb: Максимальный размер файлов для загрузки пользователем в кб убедитесь, что вы настроили лимит также в nginx (client_max_body_size) / apache или proxy.
authorized_extensions: Список расширений файлов, разрешенных к загрузке, разделенный вертикальной чертой (|)
max_similar_results: Количество похожих тем, показываемых пользователю во время создания новой темы
title_prettify: Предотвращать распространенные опечатки и ошибки, включая КАПС, первый строчный символ, множественные ! и ?, лишние . в конце предложения и т.д.

View File

@ -480,7 +480,7 @@ sv:
min_body_similar_length: "The minimum length of a post's body before it will be checked for similar topics"
category_colors: "A pipe (|) separated list of hexadecimal color values allowed for categories"
max_upload_size_kb: "The maximum size of files we allow users to upload in kB - be sure to configure the limit in nginx (client_max_body_size) / apache or proxy as well."
max_image_size_kb: "The maximum size of files we allow users to upload in kB - be sure to configure the limit in nginx (client_max_body_size) / apache or proxy as well."
max_similar_results: "How many similar topics to show a user while they are composing a new topic"
notification_types:

View File

@ -575,7 +575,7 @@ zh_CN:
min_body_similar_length: "开始检查相似主题的帖子内容的最小长度"
category_colors: "设置分类颜色的用'|'分隔的十六进制色彩值列表"
max_upload_size_kb: "允许用户上传的最大文件大小(以kB为单位) - 确保在nginx(client_max_body_size), apache或代理服务中进行限制文件大小的配置."
max_image_size_kb: "允许用户上传的最大文件大小(以kB为单位) - 确保在nginx(client_max_body_size), apache或代理服务中进行限制文件大小的配置."
max_similar_results: "当用户撰写新主题时,显示多少类似主题给用户"
title_prettify: "防止常见标题里的错别字和错误,包括全部大写,第一个字符小写,多个!和?,结尾多余的. 等等。"

View File

@ -558,7 +558,7 @@ zh_TW:
min_body_similar_length: "開始檢查相似主題的帖子內容的最小長度"
category_colors: "設置分類顔色的用'|'分隔的十六進制色彩值列表"
max_upload_size_kb: "允許用戶上傳的最大文件大小(以kB爲單位) - 確保在nginx(client_max_body_size), apache或代理服務中進行限制文件大小的配置."
max_image_size_kb: "允許用戶上傳的最大文件大小(以kB爲單位) - 確保在nginx(client_max_body_size), apache或代理服務中進行限制文件大小的配置."
max_similar_results: "當用戶撰寫新主題時,顯示多少類似主題給用戶"
title_prettify: "防止常見標題裏的錯別字和錯誤,包括全部大寫,第一個字符小寫,多個!和?,結尾多余的. 等等。"

View File

@ -17,7 +17,6 @@ describe UploadsController do
let(:logo) do
ActionDispatch::Http::UploadedFile.new({
filename: 'logo.png',
type: 'image/png',
tempfile: File.new("#{Rails.root}/spec/fixtures/images/logo.png")
})
end
@ -25,7 +24,6 @@ describe UploadsController do
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
@ -33,7 +31,6 @@ describe UploadsController do
let(:text_file) do
ActionDispatch::Http::UploadedFile.new({
filename: 'LICENSE.txt',
type: 'text/plain',
tempfile: File.new("#{Rails.root}/LICENSE.txt")
})
end
@ -42,11 +39,6 @@ describe UploadsController do
context 'with a file' do
it 'is successful' do
xhr :post, :create, file: logo
response.should be_success
end
context 'when authorized' do
before { SiteSetting.stubs(:authorized_extensions).returns(".txt") }

View File

@ -88,4 +88,26 @@ describe SiteSetting do
end
end
describe "authorized extensions" do
describe "authorized_uploads" do
it "trims space and adds leading dots" do
SiteSetting.stubs(:authorized_extensions).returns(" png | .jpeg|txt|bmp")
SiteSetting.authorized_uploads.should == [".png", ".jpeg", ".txt", ".bmp"]
end
end
describe "authorized_images" do
it "filters non-image out" do
SiteSetting.stubs(:authorized_extensions).returns(" png | .jpeg|txt|bmp")
SiteSetting.authorized_images.should == [".png", ".jpeg", ".bmp"]
end
end
end
end

View File

@ -42,7 +42,7 @@ test("ensures an authorized upload", function() {
test("prevents files that are too big from being uploaded", function() {
var image = { name: "image.png", size: 10 * 1024 };
Discourse.SiteSettings.max_upload_size_kb = 5;
Discourse.SiteSettings.max_image_size_kb = 5;
this.stub(bootbox, "alert");
ok(!validUpload([image]));
@ -61,7 +61,7 @@ var dummyBlob = function() {
};
test("allows valid uploads to go through", function() {
Discourse.SiteSettings.max_upload_size_kb = 15;
Discourse.SiteSettings.max_image_size_kb = 15;
this.stub(bootbox, "alert");
// image
@ -101,10 +101,10 @@ test("getUploadMarkdown", function() {
});
test("isAnImage", function() {
_.each(["png", "jpg", "jpeg", "bmp", "gif", "tif"], function(extension) {
_.each(["png", "jpg", "jpeg", "bmp", "gif", "tif", "tiff"], function(extension) {
var image = "image." + extension;
ok(utils.isAnImage(image));
ok(utils.isAnImage("http://foo.bar/path/to/" + image));
ok(utils.isAnImage(image), image + " is recognized as an image");
ok(utils.isAnImage("http://foo.bar/path/to/" + image), image + " is recognized as an image");
});
ok(!utils.isAnImage("file.txt"));
ok(!utils.isAnImage("http://foo.bar/path/to/file.txt"));