diff --git a/client/preprocessor.js b/client/preprocessor.js deleted file mode 100644 index 4a742dcf..00000000 --- a/client/preprocessor.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict'; - -var babel = require('babel-core'); - -module.exports = { - process: function(src, filename) { - // Ignore files other than .js, .es, .jsx or .es6 - if (!babel.canCompile(filename)) { - return ''; - } - // Ignore all files within node_modules - if (filename.indexOf('node_modules') === -1) { - return babel.transform(src, {filename: filename}).code; - } - return src; - } -}; \ No newline at end of file diff --git a/client/src/core-components/__tests__/form-test.js b/client/src/core-components/__tests__/form-test.js index 016ed26e..94530f15 100644 --- a/client/src/core-components/__tests__/form-test.js +++ b/client/src/core-components/__tests__/form-test.js @@ -1,13 +1,11 @@ // MOCKS const ValidationFactoryMock = require('lib-app/__mocks__/validations/validation-factory-mock'); -const TextEditorMock = require('core-components/__mocks__/text-editor-mock'); const FormField = ReactMock(); // COMPONENT const Form = requireUnit('core-components/form', { 'lib-app/validations/validator-factory': ValidationFactoryMock, 'core-components/form-field': FormField, - 'core-components/text-editor': TextEditorMock }); describe('Form component', function () { @@ -187,18 +185,6 @@ describe('Form component', function () { expect(form.props.onSubmit).to.not.have.been.called; }); - it('should transform TextEdit value to HTML', function () { - form.state.form.first = TextEditorMock.createEmpty(); - - TestUtils.Simulate.submit(ReactDOM.findDOMNode(form)); - expect(TextEditorMock.getHTMLFromEditorState).to.have.been.calledWith(form.state.form.first); - expect(form.props.onSubmit).to.have.been.calledWith({ - first: 'HTML_CODE', - second: 'value2', - third: 'value3' - }); - }); - it('should focus the first field with error', function () { ValidationFactoryMock.validators.defaultValidatorMock.performValidation = stub().returns('MOCK_ERROR'); ValidationFactoryMock.validators.customValidatorMock.performValidation = stub().returns('MOCK_ERROR_2'); diff --git a/client/src/core-components/form.js b/client/src/core-components/form.js index dd9ad80f..ea422d54 100644 --- a/client/src/core-components/form.js +++ b/client/src/core-components/form.js @@ -6,7 +6,6 @@ import {reactDFS, renderChildrenWithProps} from 'lib-core/react-dfs'; import ValidationFactory from 'lib-app/validations/validator-factory'; import FormField from 'core-components/form-field'; -import TextEditor from 'core-components/text-editor'; class Form extends React.Component { diff --git a/client/src/core-components/tooltip.js b/client/src/core-components/tooltip.js index 738b1051..c704b192 100644 --- a/client/src/core-components/tooltip.js +++ b/client/src/core-components/tooltip.js @@ -2,7 +2,7 @@ import React from 'react' import {Motion, spring} from 'react-motion'; class Tooltip extends React.Component { - + static propTypes = { children: React.PropTypes.node, content: React.PropTypes.node, @@ -105,4 +105,4 @@ class Tooltip extends React.Component { } } -export default Tooltip; \ No newline at end of file +export default Tooltip; diff --git a/client/src/data/fixtures/system-fixtures.js b/client/src/data/fixtures/system-fixtures.js index 50e1d5d8..a60dfba5 100644 --- a/client/src/data/fixtures/system-fixtures.js +++ b/client/src/data/fixtures/system-fixtures.js @@ -21,7 +21,7 @@ module.exports = [ 'session-prefix': 'opensupports-z6ctpq2winvfhchX2_', 'maintenance-mode': false, 'allow-attachments': true, - 'max-size': 500, + 'max-size': 1, 'departments': [ {id: 1, name: 'Sales Support', owners: 2}, {id: 2, name: 'Technical Issues', owners: 5}, diff --git a/client/src/data/languages/br.js b/client/src/data/languages/br.js index 12e23c8a..58c86ae1 100644 --- a/client/src/data/languages/br.js +++ b/client/src/data/languages/br.js @@ -321,6 +321,7 @@ export default { 'INVALID_EMAIL_OR_TICKET_NUMBER': 'Número de e-mail ou chamado inválido', 'INVALID_FILE': 'arquivo inválido', 'ERRORS_FOUND': 'Erros encontrados', + 'ERROR_IMAGE_SIZE': 'Nenhuma imagem pode ter um tamanho maior que {size} MB', //MESSAGES 'SIGNUP_SUCCESS': 'Você se registrou com sucesso em nosso sistema de suporte.', diff --git a/client/src/data/languages/cn.js b/client/src/data/languages/cn.js index ab259450..be3fc691 100644 --- a/client/src/data/languages/cn.js +++ b/client/src/data/languages/cn.js @@ -321,6 +321,7 @@ export default { 'INVALID_EMAIL_OR_TICKET_NUMBER': '電子郵件或機票號無效', 'INVALID_FILE': '無效文件', 'ERRORS_FOUND': '發現錯誤', + 'ERROR_IMAGE_SIZE': '没有图像的大小可以超过{size}MB', //MESSAGES 'SIGNUP_SUCCESS': '您已在我們的支持系統中成功註冊', diff --git a/client/src/data/languages/de.js b/client/src/data/languages/de.js index e6b172b6..b43b0bad 100644 --- a/client/src/data/languages/de.js +++ b/client/src/data/languages/de.js @@ -321,6 +321,7 @@ export default { 'INVALID_EMAIL_OR_TICKET_NUMBER': 'Ungültige E-Mail-Adresse oder Ticketnummer!', 'INVALID_FILE': 'Ungültige Datei!', 'ERRORS_FOUND': 'Fehler gefunden!', + 'ERROR_IMAGE_SIZE': 'Kein Bild darf größer als {size} MB sein', //MESSAGES 'SIGNUP_SUCCESS': 'Sie haben sich erfolgreich in unserem Support-System registriert.', diff --git a/client/src/data/languages/en.js b/client/src/data/languages/en.js index 8a980634..4b95f899 100644 --- a/client/src/data/languages/en.js +++ b/client/src/data/languages/en.js @@ -322,6 +322,7 @@ export default { 'INVALID_EMAIL_OR_TICKET_NUMBER': 'Invalid email or ticket number', 'INVALID_FILE': 'Invalid file', 'ERRORS_FOUND': 'Errors found', + 'ERROR_IMAGE_SIZE': 'No image can have a size greater than {size} MB', //MESSAGES 'SIGNUP_SUCCESS': 'You have registered successfully in our support system.', diff --git a/client/src/data/languages/es.js b/client/src/data/languages/es.js index 072bcd83..8f604cc3 100644 --- a/client/src/data/languages/es.js +++ b/client/src/data/languages/es.js @@ -346,6 +346,7 @@ export default { 'WILL_RECOVER_EMAIL_TEMPLATE': 'Esta plantilla de correo electrónico se recuperará a su valor predeterminado en este idioma.', 'SUCCESS_IMPORTING_CSV_DESCRIPTION': 'El archivo CSV se ha importado correctamente', 'SUCCESS_DELETING_ALL_USERS': 'Los usuarios se han eliminado correctamente', + 'ERROR_IMAGE_SIZE': 'Ninguna imagen puede tener un tamaño superior a {size} MB', 'LAST_7_DAYS': 'Últimos 7 dias', 'LAST_30_DAYS': 'Últimos 30 dias', diff --git a/client/src/data/languages/fr.js b/client/src/data/languages/fr.js index ee88f58e..65152a60 100644 --- a/client/src/data/languages/fr.js +++ b/client/src/data/languages/fr.js @@ -321,6 +321,7 @@ export default { 'INVALID_EMAIL_OR_TICKET_NUMBER': 'Numéro de courriel ou de ticket invalide', 'INVALID_FILE': 'Fichier invalide', 'ERRORS_FOUND': 'Des erreurs sont survenues', + 'ERROR_IMAGE_SIZE': 'Aucune image ne peut avoir une taille supérieure à {size} MB', //MESSAGES 'SIGNUP_SUCCESS': 'Vous êtes inscrit avec succès dans notre système de support.', diff --git a/client/src/data/languages/gr.js b/client/src/data/languages/gr.js index 11af28a9..b961a00a 100644 --- a/client/src/data/languages/gr.js +++ b/client/src/data/languages/gr.js @@ -322,6 +322,7 @@ 'INVALID_EMAIL_OR_TICKET_NUMBER': 'Μη έγκυρη ηλεκτρονική διεύθυνση ή αριθμός εισιτηρίου', 'INVALID_FILE': 'Μη έγκυρο αρχείο', 'ERRORS_FOUND': 'Βρέθηκαν Σφάλματα', + 'ERROR_IMAGE_SIZE': 'Καμία εικόνα δεν μπορεί να έχει μέγεθος μεγαλύτερο από {size} MB', //MESSAGES 'SIGNUP_SUCCESS': 'Έχετε εγγραφεί με επιτυχία στο σύστημα υποστήριξης μας.', diff --git a/client/src/data/languages/in.js b/client/src/data/languages/in.js index dfb3c2be..569bfe85 100644 --- a/client/src/data/languages/in.js +++ b/client/src/data/languages/in.js @@ -321,6 +321,7 @@ export default { 'INVALID_EMAIL_OR_TICKET_NUMBER': 'अमान्य ईमेल या टिकट नंबर', 'INVALID_FILE': 'अवैध फाइल', 'ERRORS_FOUND': 'त्रुटियां मिलीं', + 'ERROR_IMAGE_SIZE': 'कोई छवि {size} एमबी से अधिक आकार नहीं हो सकती है', //MESSAGES 'SIGNUP_SUCCESS': 'आप हमारे समर्थन प्रणाली में सफलतापूर्वक दर्ज कर लिया है।', diff --git a/client/src/data/languages/it.js b/client/src/data/languages/it.js index cf94a0eb..92aa5904 100644 --- a/client/src/data/languages/it.js +++ b/client/src/data/languages/it.js @@ -321,6 +321,7 @@ export default { 'INVALID_EMAIL_OR_TICKET_NUMBER': 'E-mail o numero di ticket non validi', 'INVALID_FILE': 'File non valido', 'ERRORS_FOUND': 'Trovati errori', + 'ERROR_IMAGE_SIZE': 'Nessuna immagine può avere una dimensione superiore a {size} MB', //MESSAGES 'SIGNUP_SUCCESS': 'È stato registrato con successo nel nostro sistema di supporto.', diff --git a/client/src/data/languages/jp.js b/client/src/data/languages/jp.js index 3d1751f2..27d5fe7c 100644 --- a/client/src/data/languages/jp.js +++ b/client/src/data/languages/jp.js @@ -321,6 +321,7 @@ export default { 'INVALID_EMAIL_OR_TICKET_NUMBER': '電子メールまたはチケット番号が無効です', 'INVALID_FILE': '無効なファイル', 'ERRORS_FOUND': 'エラーが見つかりました', + 'ERROR_IMAGE_SIZE': 'イメージのサイズが{size} MBを超えることはできません', //MESSAGES 'SIGNUP_SUCCESS': 'あなたは私たちのサポートシステムに正常に登録しました。', diff --git a/client/src/data/languages/nl.js b/client/src/data/languages/nl.js index c7e96fa0..1355f2d0 100644 --- a/client/src/data/languages/nl.js +++ b/client/src/data/languages/nl.js @@ -322,6 +322,7 @@ export default { 'INVALID_EMAIL_OR_TICKET_NUMBER': 'Ongeldig e-mailadres of incidentnummer', 'INVALID_FILE': 'Ongeldig bestand', 'ERRORS_FOUND': 'Er is een fout opgetreden', + 'ERROR_IMAGE_SIZE': 'Geen enkele afbeelding kan groter zijn dan {size} MB', //MESSAGES 'SIGNUP_SUCCESS': 'U hebt zich succesvol geregistreerd in ons ondersteuningssysteem.', diff --git a/client/src/data/languages/pt.js b/client/src/data/languages/pt.js index d2eb7d54..67eecace 100644 --- a/client/src/data/languages/pt.js +++ b/client/src/data/languages/pt.js @@ -321,6 +321,7 @@ export default { 'INVALID_EMAIL_OR_TICKET_NUMBER': 'Número de e-mail ou bilhete inválido', 'INVALID_FILE': 'arquivo inválido', 'ERRORS_FOUND': 'Erros encontrados', + 'ERROR_IMAGE_SIZE': 'Nenhuma imagem pode ter um tamanho maior que {size} MB', //MESSAGES 'SIGNUP_SUCCESS': 'Você se registrou com sucesso em nosso sistema de suporte.', diff --git a/client/src/data/languages/ru.js b/client/src/data/languages/ru.js index 1d3dce2b..834a3293 100644 --- a/client/src/data/languages/ru.js +++ b/client/src/data/languages/ru.js @@ -320,6 +320,7 @@ export default { 'INVALID_EMAIL_OR_TICKET_NUMBER': 'Неправильный номер эл. Почты или номера билета.', 'INVALID_FILE': 'неверный файл', 'ERRORS_FOUND': 'Ошибки найдены', + 'ERROR_IMAGE_SIZE': 'Изображение не может иметь размер больше {size} МБ', //MESSAGES 'SIGNUP_SUCCESS': 'Вы успешно зарегистрировались в нашей системе поддержки.', diff --git a/client/src/data/languages/tr.js b/client/src/data/languages/tr.js index 0e5fb187..4858f522 100644 --- a/client/src/data/languages/tr.js +++ b/client/src/data/languages/tr.js @@ -321,6 +321,7 @@ export default { 'INVALID_EMAIL_OR_TICKET_NUMBER': 'Geçersiz e-posta veya bilet numarası', 'INVALID_FILE': 'geçersiz dosya', 'ERRORS_FOUND': 'Hatalar bulundu', + 'ERROR_IMAGE_SIZE': 'Hiçbir resmin boyutu {size} MB\'den büyük olabilir', //MESSAGES 'SIGNUP_SUCCESS': 'Destek sistemimize başarılı bir şekilde kayıt oldunuz.', diff --git a/client/src/lib-app/session-store.js b/client/src/lib-app/session-store.js index bfcf14cb..540c994b 100644 --- a/client/src/lib-app/session-store.js +++ b/client/src/lib-app/session-store.js @@ -61,6 +61,7 @@ class SessionStore { this.setItem('user-system-enabled', configs['user-system-enabled']); this.setItem('allow-attachments', configs['allow-attachments']); this.setItem('maintenance-mode', configs['maintenance-mode']); + this.setItem('max-size', configs['max-size']); } getConfigs() { @@ -75,7 +76,8 @@ class SessionStore { registration: (this.getItem('registration') * 1), 'user-system-enabled': (this.getItem('user-system-enabled') * 1), 'allow-attachments': (this.getItem('allow-attachments') * 1), - 'maintenance-mode': (this.getItem('maintenance-mode') * 1) + 'maintenance-mode': (this.getItem('maintenance-mode') * 1), + 'max-size': this.getItem('max-size'), }; } diff --git a/client/src/lib-app/validations/image-size-validator.js b/client/src/lib-app/validations/image-size-validator.js new file mode 100644 index 00000000..cd8b44d3 --- /dev/null +++ b/client/src/lib-app/validations/image-size-validator.js @@ -0,0 +1,24 @@ +import _ from 'lodash'; + +import Validator from 'lib-app/validations/validator'; +import SessionStore from 'lib-app/session-store'; +import Base64ImageParser from 'lib-core/base64-image-parser'; + +class ImageSizeValidator extends Validator { + constructor(errorKey = 'ERROR_IMAGE_SIZE', validator = null) { + super(validator); + + this.maxSize = 1; + this.errorKey = errorKey; + } + + validate(value = '', form = {}) { + let images = Base64ImageParser.getImagesSrc(value).map(Base64ImageParser.dataURLtoFile); + + if(_.some(images, f => f.size > 1048576 * SessionStore.getItem('max-size'))) { + return this.getError(this.errorKey, {size: SessionStore.getItem('max-size')}); + } + } +} + +export default ImageSizeValidator; diff --git a/client/src/lib-app/validations/length-validator.js b/client/src/lib-app/validations/length-validator.js index dcc03bec..3b120e07 100644 --- a/client/src/lib-app/validations/length-validator.js +++ b/client/src/lib-app/validations/length-validator.js @@ -1,22 +1,20 @@ -import TextEditor from 'core-components/text-editor'; - import Validator from 'lib-app/validations/validator'; class LengthValidator extends Validator { constructor(length, errorKey = 'INVALID_VALUE', validator = null) { super(validator); - + this.minlength = length; this.errorKey = errorKey; } validate(value = '', form = {}) { - if (TextEditor.isEditorState(value)) { - value = value.getCurrentContent().getPlainText(); - } + let div = document.createElement("div"); + div.innerHTML = value; + let text = div.textContent || div.innerText || ""; - if (value.length < this.minlength) return this.getError(this.errorKey); + if (text.length < this.minlength) return this.getError(this.errorKey); } } -export default LengthValidator; \ No newline at end of file +export default LengthValidator; diff --git a/client/src/lib-app/validations/validator-factory.js b/client/src/lib-app/validations/validator-factory.js index fdd63284..c84278f6 100644 --- a/client/src/lib-app/validations/validator-factory.js +++ b/client/src/lib-app/validations/validator-factory.js @@ -3,13 +3,14 @@ import EmailValidator from 'lib-app/validations/email-validator'; import RepeatPasswordValidator from 'lib-app/validations/repeat-password-validator'; import LengthValidator from 'lib-app/validations/length-validator'; import ListValidator from 'lib-app/validations/list-validator'; +import ImageSizeValidator from 'lib-app/validations/image-size-validator'; let validators = { 'DEFAULT': new Validator(), 'NAME': new LengthValidator(2, 'ERROR_NAME'), 'TITLE': new LengthValidator(1, 'ERROR_TITLE'), 'EMAIL': new EmailValidator(), - 'TEXT_AREA': new LengthValidator(10, 'ERROR_CONTENT_SHORT'), + 'TEXT_AREA': new ImageSizeValidator(undefined, new LengthValidator(10, 'ERROR_CONTENT_SHORT')), 'PASSWORD': new LengthValidator(6, 'ERROR_PASSWORD'), 'REPEAT_PASSWORD': new RepeatPasswordValidator(), 'URL': new LengthValidator(5, 'ERROR_URL'), diff --git a/client/src/lib-app/validations/validator.js b/client/src/lib-app/validations/validator.js index 6b98ced3..7ed2af44 100644 --- a/client/src/lib-app/validations/validator.js +++ b/client/src/lib-app/validations/validator.js @@ -6,7 +6,7 @@ class Validator { constructor(validator = null) { this.previousValidator = validator; } - + performValidation(value, form) { let error; @@ -27,9 +27,9 @@ class Validator { if (value.length === 0) return this.getError('ERROR_EMPTY'); } - getError(errorKey) { - return i18n(errorKey); + getError(errorKey, params) { + return i18n(errorKey, params); } } -export default Validator \ No newline at end of file +export default Validator diff --git a/client/src/lib-test/preprocessor.js b/client/src/lib-test/preprocessor.js index 1904b33c..fb3b5bcd 100644 --- a/client/src/lib-test/preprocessor.js +++ b/client/src/lib-test/preprocessor.js @@ -4,6 +4,7 @@ var jsdom = require('jsdom').jsdom; global.document = jsdom(''); global.window = document.defaultView; +global.Node = global.window.Node; global.navigator = { userAgent: 'node.js' }; diff --git a/server/controllers/system/init-settings.php b/server/controllers/system/init-settings.php index 97a87009..bbfad962 100755 --- a/server/controllers/system/init-settings.php +++ b/server/controllers/system/init-settings.php @@ -77,7 +77,7 @@ class InitSettingsController extends Controller { 'maintenance-mode' => 0, 'layout' => 'boxed', 'allow-attachments' => !!Controller::request('allow-attachments'), - 'max-size' => 1024, + 'max-size' => 1, 'title' => Controller::request('title') ? Controller::request('title') : 'Support Center', 'url' => Controller::request('url') ? Controller::request('url') : ('http://' . $_SERVER['HTTP_HOST']), 'registration' => !!Controller::request('registration'), diff --git a/server/libs/Controller.php b/server/libs/Controller.php index 402d76c8..572386cb 100755 --- a/server/libs/Controller.php +++ b/server/libs/Controller.php @@ -109,6 +109,14 @@ abstract class Controller { $fileUploader = FileUploader::getInstance(); $fileUploader->setMaxSize($maxSize); + $allImagesValidSize = true; + + for($i=0;$i<$totalImages;$i++) { + $allImagesValidSize = $allImagesValidSize && $fileUploader->isSizeValid($_FILES["image_$i"]); + } + + if(!$allImagesValidSize) throw new Exception(ERRORS::INVALID_FILE); + $imagePaths = []; $url = Setting::getSetting('url')->getValue(); for($i=0;$i<$totalImages;$i++) { diff --git a/server/libs/FileUploader.php b/server/libs/FileUploader.php index 576a4b76..71048155 100755 --- a/server/libs/FileUploader.php +++ b/server/libs/FileUploader.php @@ -1,7 +1,7 @@ maxSize); + } + public function upload($file) { $this->setNewName($file['name']); - if($file['size'] > (1024 * $this->maxSize)) { + if(!$this->isSizeValid($file)) { return false; } diff --git a/tests/system/edit-settings.rb b/tests/system/edit-settings.rb index 73684b26..5923d364 100644 --- a/tests/system/edit-settings.rb +++ b/tests/system/edit-settings.rb @@ -10,7 +10,7 @@ describe'system/edit-settings' do "time-zone" => -3, "layout" => 'full-width', "allow-attachments" => 1, - "max-size" => 2048, + "max-size" => 2, "language" => 'en', "no-reply-email" => 'testemail@hotmail.com' }) @@ -27,7 +27,7 @@ describe'system/edit-settings' do (row['value']).should.equal('full-width') row = $database.getRow('setting', 'max-size', 'name') - (row['value']).should.equal('2048') + (row['value']).should.equal('2') row = $database.getRow('setting', 'language', 'name') (row['value']).should.equal('en')