diff --git a/client/src/app-components/ticket-event.js b/client/src/app-components/ticket-event.js index bdcd9b2e..719f417d 100644 --- a/client/src/app-components/ticket-event.js +++ b/client/src/app-components/ticket-event.js @@ -2,6 +2,7 @@ import React from 'react'; import classNames from 'classnames'; import i18n from 'lib-app/i18n'; +import API from 'lib-app/api-call'; import DateTransformer from 'lib-core/date-transformer'; import Icon from 'core-components/icon'; @@ -161,7 +162,7 @@ class TicketEvent extends React.Component { } return ( -
+
{node}
) @@ -222,9 +223,26 @@ class TicketEvent extends React.Component { const fileName = filePath.replace(/^.*[\\\/]/, ''); return ( - {fileName} + {fileName} ) } + + onFileClick(filePath) { + API.call({ + path: '/system/download', + plain: true, + data: { + file: filePath + } + }).then((result) => { + let contentType = 'application/octet-stream'; + let link = document.createElement('a'); + let blob = new Blob([result], {'type': contentType}); + link.href = window.URL.createObjectURL(blob); + link.download = filePath; + link.click(); + }); + } } export default TicketEvent; diff --git a/client/src/app-components/ticket-event.scss b/client/src/app-components/ticket-event.scss index 50105bbe..328d16e1 100644 --- a/client/src/app-components/ticket-event.scss +++ b/client/src/app-components/ticket-event.scss @@ -91,6 +91,14 @@ } } + &__file { + background-color: $very-light-grey; + cursor: pointer; + text-align: right; + padding: 5px 10px; + font-size: 12px; + } + &_staff { .ticket-event__icon { background-color: $primary-blue; diff --git a/client/src/app-components/ticket-viewer.js b/client/src/app-components/ticket-viewer.js index 6b6f1362..c00b875a 100644 --- a/client/src/app-components/ticket-viewer.js +++ b/client/src/app-components/ticket-viewer.js @@ -190,6 +190,7 @@ class TicketViewer extends React.Component {
+ {i18n('RESPOND_TICKET')}
@@ -234,10 +235,12 @@ class TicketViewer extends React.Component { loading: this.state.loading, onChange: (formState) => {this.setState({ commentValue: formState.content, + commentFile: formState.file, commentEdited: true })}, values: { - 'content': this.state.commentValue + 'content': this.state.commentValue, + 'file': this.state.commentFile } }; } diff --git a/client/src/app-components/ticket-viewer.scss b/client/src/app-components/ticket-viewer.scss index 9f03fbf7..2a6658ed 100644 --- a/client/src/app-components/ticket-viewer.scss +++ b/client/src/app-components/ticket-viewer.scss @@ -48,13 +48,6 @@ margin-top: 10px; } - &__file { - background-color: $very-light-grey; - text-align: right; - padding: 5px 10px; - font-size: 12px; - } - &__comments { position: relative; } diff --git a/client/src/app/main/dashboard/dashboard-create-ticket/create-ticket-form.js b/client/src/app/main/dashboard/dashboard-create-ticket/create-ticket-form.js index 83b012bc..f8898b8f 100644 --- a/client/src/app/main/dashboard/dashboard-create-ticket/create-ticket-form.js +++ b/client/src/app/main/dashboard/dashboard-create-ticket/create-ticket-form.js @@ -56,6 +56,9 @@ class CreateTicketForm extends React.Component { }}/>
+
+ +
{(!this.props.userLogged) ? this.renderCaptcha() : null} Create Ticket diff --git a/client/src/app/main/dashboard/dashboard-create-ticket/create-ticket-form.scss b/client/src/app/main/dashboard/dashboard-create-ticket/create-ticket-form.scss index 045c290c..bc7d906e 100644 --- a/client/src/app/main/dashboard/dashboard-create-ticket/create-ticket-form.scss +++ b/client/src/app/main/dashboard/dashboard-create-ticket/create-ticket-form.scss @@ -1,5 +1,9 @@ .create-ticket-form { + &__file { + text-align: left; + } + &__message { margin-top: 20px; } diff --git a/client/src/core-components/file-uploader.js b/client/src/core-components/file-uploader.js new file mode 100644 index 00000000..c18771ad --- /dev/null +++ b/client/src/core-components/file-uploader.js @@ -0,0 +1,40 @@ +import React from 'react'; + +import Button from 'core-components/button'; +import Icon from 'core-components/icon'; + +class FileUploader extends React.Component { + static propTypes = { + text: React.PropTypes.string, + value: React.PropTypes.object, + onChange: React.PropTypes.func + }; + + static defaultProps = { + text: 'Upload file' + }; + + render() { + return ( + + ); + } + + onChange(event) { + if(this.props.onChange) { + this.props.onChange({ + target: { + value: event.target.files[0] + } + }); + } + } +} + +export default FileUploader; \ No newline at end of file diff --git a/client/src/core-components/file-uploader.scss b/client/src/core-components/file-uploader.scss new file mode 100644 index 00000000..b6c766e7 --- /dev/null +++ b/client/src/core-components/file-uploader.scss @@ -0,0 +1,31 @@ +@import "../scss/vars"; + +.file-uploader { + + &__input { + width: 0.1px; + height: 0.1px; + opacity: 0; + overflow: hidden; + position: absolute; + z-index: -1; + } + + &__custom { + display: inline-block; + cursor: pointer; + color: white; + background-color: $primary-red; + line-height: 35px; + padding: 0 15px; + } + + &__icon { + margin-right: 15px; + } + + &__value { + margin-left: 10px; + color: $dark-grey; + } +} \ No newline at end of file diff --git a/client/src/core-components/form-field.js b/client/src/core-components/form-field.js index c913e959..d12dbe47 100644 --- a/client/src/core-components/form-field.js +++ b/client/src/core-components/form-field.js @@ -9,6 +9,7 @@ import Checkbox from 'core-components/checkbox'; import CheckboxGroup from 'core-components/checkbox-group'; import TextEditor from 'core-components/text-editor'; import InfoTooltip from 'core-components/info-tooltip'; +import FileUploader from 'core-components/file-uploader'; class FormField extends React.Component { static contextTypes = { @@ -24,7 +25,7 @@ class FormField extends React.Component { error: React.PropTypes.string, infoMessage: React.PropTypes.node, value: React.PropTypes.any, - field: React.PropTypes.oneOf(['input', 'textarea', 'select', 'checkbox', 'checkbox-group']), + field: React.PropTypes.oneOf(['input', 'textarea', 'select', 'checkbox', 'checkbox-group', 'file']), fieldProps: React.PropTypes.object }; @@ -84,7 +85,8 @@ class FormField extends React.Component { 'textarea': TextEditor, 'select': DropDown, 'checkbox': Checkbox, - 'checkbox-group': CheckboxGroup + 'checkbox-group': CheckboxGroup, + 'file': FileUploader }[this.props.field]; if(this.props.decorator) { @@ -142,7 +144,8 @@ class FormField extends React.Component { getDivTypes() { return [ 'textarea', - 'checkbox-group' + 'checkbox-group', + 'file' ]; } diff --git a/client/src/data/fixtures/system-fixtures.js b/client/src/data/fixtures/system-fixtures.js index b67d450f..ac2a5bbd 100644 --- a/client/src/data/fixtures/system-fixtures.js +++ b/client/src/data/fixtures/system-fixtures.js @@ -110,6 +110,14 @@ module.exports = [ }; } }, + { + path: '/system/download', + time: 100, + contentType: 'application/octet-stream', + response: function () { + return 'text content'; + } + }, { path: '/system/get-mail-templates', time: 100, diff --git a/client/src/data/fixtures/user-fixtures.js b/client/src/data/fixtures/user-fixtures.js index 484d7c95..2ff9801b 100644 --- a/client/src/data/fixtures/user-fixtures.js +++ b/client/src/data/fixtures/user-fixtures.js @@ -166,7 +166,7 @@ module.exports = [ name: 'Sales Support' }, date: '201504090001', - file: 'http://www.opensupports.com/some_file.zip', + file: 'some_file.txt', language: 'en', unread: false, closed: false, @@ -385,7 +385,7 @@ module.exports = [ name: 'Technical Issues' }, date: '201604161427', - file: 'http://www.opensupports.com/some_file.zip', + file: 'some_file.txt', language: 'en', unread: true, closed: false, @@ -504,7 +504,7 @@ module.exports = [ name: 'Sales Support' }, date: '201604150849', - file: 'http://www.opensupports.com/some_file.zip', + file: 'some_file.txt', language: 'en', unread: false, closed: false, @@ -607,7 +607,7 @@ module.exports = [ name: 'Sales Support' }, date: '201504091032', - file: 'http://www.opensupports.com/some_file.zip', + file: 'some_file.txt', language: 'en', unread: false, closed: false, diff --git a/client/src/lib-app/api-call.js b/client/src/lib-app/api-call.js index f65ba752..032c5bc0 100644 --- a/client/src/lib-app/api-call.js +++ b/client/src/lib-app/api-call.js @@ -12,13 +12,13 @@ function processData (data) { } module.exports = { - call: function ({path, data}) { + call: function ({path, data, plain}) { return new Promise(function (resolve, reject) { APIUtils.post(root + path, processData(data)) .then(function (result) { console.log(result); - if (result.status === 'success') { + if (plain || result.status === 'success') { resolve(result); } else if (reject) { reject(result); diff --git a/client/src/lib-app/fixtures-loader.js b/client/src/lib-app/fixtures-loader.js index 1850cb34..dc02c382 100644 --- a/client/src/lib-app/fixtures-loader.js +++ b/client/src/lib-app/fixtures-loader.js @@ -24,7 +24,7 @@ fixtures.add(require('data/fixtures/article-fixtures')); _.each(fixtures.getAll(), function (fixture) { mockjax({ - contentType: 'application/json', + contentType: fixture.contentType || 'application/json', url: 'http://localhost:3000/api' + fixture.path, responseTime: fixture.time || 500, response: function (settings) { diff --git a/client/src/lib-core/APIUtils.js b/client/src/lib-core/APIUtils.js index 2ba05eea..65b52ae6 100644 --- a/client/src/lib-core/APIUtils.js +++ b/client/src/lib-core/APIUtils.js @@ -9,7 +9,8 @@ const APIUtils = { url: path, method: method, data: data, - dataType: 'json' + processData: false, + contentType: false }) .done(resolve) .fail((jqXHR, textStatus) => { diff --git a/server/composer.json b/server/composer.json index 71e09ba2..a506cdee 100644 --- a/server/composer.json +++ b/server/composer.json @@ -4,7 +4,8 @@ "respect/validation": "^1.1", "phpmailer/phpmailer": "^5.2", "google/recaptcha": "~1.1", - "gabordemooij/redbean": "^4.3" + "gabordemooij/redbean": "^4.3", + "ifsnop/mysqldump-php": "2.*" }, "require-dev": { "phpunit/phpunit": "5.0.*" diff --git a/server/controllers/system.php b/server/controllers/system.php index 2f2c65c6..0ffd8dde 100644 --- a/server/controllers/system.php +++ b/server/controllers/system.php @@ -16,6 +16,8 @@ require_once 'system/add-api-key.php'; require_once 'system/delete-api-key.php'; require_once 'system/get-all-keys.php'; require_once 'system/delete-all-users.php'; +require_once 'system/backup-database.php'; +require_once 'system/download.php'; $systemControllerGroup = new ControllerGroup(); $systemControllerGroup->setGroupPath('/system'); @@ -37,5 +39,7 @@ $systemControllerGroup->addController(new AddAPIKeyController); $systemControllerGroup->addController(new DeleteAPIKeyController); $systemControllerGroup->addController(new GetAllKeyController); $systemControllerGroup->addController(new DeleteAllUsersController); +$systemControllerGroup->addController(new BackupDatabaseController); +$systemControllerGroup->addController(new DownloadController); $systemControllerGroup->finalize(); \ No newline at end of file diff --git a/server/controllers/system/backup-database.php b/server/controllers/system/backup-database.php new file mode 100644 index 00000000..82b1fccf --- /dev/null +++ b/server/controllers/system/backup-database.php @@ -0,0 +1,30 @@ + 'staff_3', + 'requestData' => [] + ]; + } + + public function handler() { + global $mysql_host; + global $mysql_database; + global $mysql_user; + global $mysql_password; + + $fileDownloader = FileDownloader::getInstance(); + $fileDownloader->setFileName('backup.sql'); + + $mysqlDump = new IMysqldump\Mysqldump('mysql:host='. $mysql_host .';dbname=' . $mysql_database, $mysql_user, $mysql_password); + $mysqlDump->start($fileDownloader->getFullFilePath()); + + if($fileDownloader->download()) { + $fileDownloader->eraseFile(); + } + } +} \ No newline at end of file diff --git a/server/controllers/system/download.php b/server/controllers/system/download.php new file mode 100644 index 00000000..278ddc1b --- /dev/null +++ b/server/controllers/system/download.php @@ -0,0 +1,54 @@ + 'user', + 'requestData' => [ + 'file' => [ + 'validation' => DataValidator::alnum('_.-')->noWhitespace(), + 'error' => ERRORS::INVALID_FILE + ] + ] + ]; + } + + public function handler() { + $fileName = Controller::request('file'); + + $loggedUser = Controller::getLoggedUser(); + $ticket = Ticket::getTicket($fileName, 'file'); + + if($ticket->isNull() || ($this->isNotAuthor($ticket, $loggedUser) && $this->isNotOwner($ticket, $loggedUser))) { + $ticketEvent = Ticketevent::getDataStore($fileName, 'file'); + + if($ticketEvent->isNull()) { + print ''; + return; + } + + $ticket = $ticketEvent->ticket; + + if($this->isNotAuthor($ticket, $loggedUser) && $this->isNotOwner($ticket, $loggedUser)) { + print ''; + return; + } + } + + $fileDownloader = FileDownloader::getInstance(); + $fileDownloader->setFileName($fileName); + $fileDownloader->download(); + } + + private function isNotAuthor($ticket, $loggedUser) { + return Controller::isStaffLogged() || $ticket->author->id !== $loggedUser->id; + } + + private function isNotOwner($ticket, $loggedUser) { + return !Controller::isStaffLogged() || !$ticket->owner || $ticket->owner->id !== $loggedUser->id; + } +} \ No newline at end of file diff --git a/server/controllers/system/init-settings.php b/server/controllers/system/init-settings.php index e31046f4..b5d7c6d3 100644 --- a/server/controllers/system/init-settings.php +++ b/server/controllers/system/init-settings.php @@ -38,11 +38,15 @@ class InitSettingsController extends Controller { 'maintenance-mode' => 0, 'layout' => 'boxed', 'allow-attachments' => 0, - 'max-size' => 0, + 'max-size' => 1024, 'title' => 'Support Center', 'url' => 'http://www.opensupports.com/support', 'registration' => true, - 'last-stat-day' => '20170101' //TODO: get current date + 'last-stat-day' => '20170101', //TODO: get current date + 'ticket-gap' => Hashing::generateRandomPrime(100000, 999999), + 'file-gap' => Hashing::generateRandomPrime(100000, 999999), + 'file-first-number' => Hashing::generateRandomNumber(100000, 999999), + 'file-quantity' => 0 ]); } diff --git a/server/controllers/ticket/comment.php b/server/controllers/ticket/comment.php index b51414c2..6a043757 100644 --- a/server/controllers/ticket/comment.php +++ b/server/controllers/ticket/comment.php @@ -50,6 +50,7 @@ class CommentController extends Controller { $comment = Ticketevent::getEvent(Ticketevent::COMMENT); $comment->setProperties(array( 'content' => $this->content, + 'file' => $this->uploadFile(), 'date' => Date::getCurrentDate() )); diff --git a/server/controllers/ticket/create.php b/server/controllers/ticket/create.php index ac9f4c53..fbcd9203 100644 --- a/server/controllers/ticket/create.php +++ b/server/controllers/ticket/create.php @@ -60,7 +60,7 @@ class CreateController extends Controller { 'language' => $this->language, 'author' => $author, 'department' => $department, - 'file' => '', + 'file' => $this->uploadFile(), 'date' => Date::getCurrentDate(), 'unread' => false, 'unreadStaff' => true, diff --git a/server/data/ERRORS.php b/server/data/ERRORS.php index 06211649..5f47e71a 100644 --- a/server/data/ERRORS.php +++ b/server/data/ERRORS.php @@ -37,4 +37,5 @@ class ERRORS { const INVALID_BODY = 'INVALID_BODY'; const INVALID_PERIOD = 'INVALID_PERIOD'; const NAME_ALREADY_USED = 'NAME_ALREADY_USED'; + const INVALID_FILE = 'INVALID_FILE'; } diff --git a/server/files/.gitkeep b/server/files/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/server/index.php b/server/index.php index a9056e1e..d8508ba3 100644 --- a/server/index.php +++ b/server/index.php @@ -18,6 +18,10 @@ include_once 'libs/Hashing.php'; include_once 'libs/MailSender.php'; include_once 'libs/Date.php'; include_once 'libs/DataStoreList.php'; +include_once 'libs/LinearCongruentialGenerator.php'; +include_once 'libs/FileManager.php'; +include_once 'libs/FileDownloader.php'; +include_once 'libs/FileUploader.php'; // LOAD DATA spl_autoload_register(function ($class) { diff --git a/server/libs/Controller.php b/server/libs/Controller.php index 3c8a3ca9..b82bdb0f 100644 --- a/server/libs/Controller.php +++ b/server/libs/Controller.php @@ -60,4 +60,26 @@ abstract class Controller { public static function getAppInstance() { return \Slim\Slim::getInstance(); } + + public function uploadFile() { + if(!isset($_FILES['file'])) return ''; + + $maxSize = Setting::getSetting('max-size')->getValue(); + $fileGap = Setting::getSetting('file-gap')->getValue(); + $fileFirst = Setting::getSetting('file-first-number')->getValue(); + $fileQuantity = Setting::getSetting('file-quantity'); + + $fileUploader = FileUploader::getInstance(); + $fileUploader->setMaxSize($maxSize); + $fileUploader->setGeneratorValues($fileGap, $fileFirst, $fileQuantity->getValue()); + + if($fileUploader->upload($_FILES['file'])) { + $fileQuantity->value++; + $fileQuantity->store(); + + return $fileUploader->getFileName(); + } else { + throw new Exception(ERRORS::INVALID_FILE); + } + } } \ No newline at end of file diff --git a/server/libs/FileDownloader.php b/server/libs/FileDownloader.php new file mode 100644 index 00000000..6f49ca64 --- /dev/null +++ b/server/libs/FileDownloader.php @@ -0,0 +1,40 @@ +getFullFilePath(); + + if(file_exists($fullFilePath) && is_file($fullFilePath)) { + header('Cache-control: private'); + header('Content-Type: application/octet-stream'); + header('Content-Length: '.filesize($fullFilePath)); + header('Content-Disposition: filename='. $this->getFileName()); + + flush(); + $file = fopen($fullFilePath, 'r'); + print fread($file, filesize($fullFilePath)); + fclose($file); + + return true; + } else { + return false; + } + } + + public function eraseFile() { + unlink($this->getLocalPath() . $this->getFileName()); + } +} \ No newline at end of file diff --git a/server/libs/FileManager.php b/server/libs/FileManager.php new file mode 100644 index 00000000..fd05bb13 --- /dev/null +++ b/server/libs/FileManager.php @@ -0,0 +1,26 @@ +localPath = $localPath; + } + + public function setFileName($fileName) { + $this->fileName = $fileName; + } + + public function getLocalPath() { + return $this->localPath; + } + + public function getFileName() { + return $this->fileName; + } + + public function getFullFilePath() { + return $this->getLocalPath() . $this->getFileName(); + } +} \ No newline at end of file diff --git a/server/libs/FileUploader.php b/server/libs/FileUploader.php index 55eb9bbc..8e01e089 100644 --- a/server/libs/FileUploader.php +++ b/server/libs/FileUploader.php @@ -1,5 +1,11 @@ setNewName($file['name']); + + if($file['size'] > (1024 * $this->maxSize)) { + return false; + } + + move_uploaded_file($file['tmp_name'], $this->getLocalPath() . $this->getFileName()); + + return true; } + + private function setNewName($fileName) { + $newName = $fileName; + $newName = strtolower($newName); + $newName = preg_replace('/\s+/', '_', $newName); + + if ($this->linearCongruentialGenerator instanceof LinearCongruentialGenerator) { + $newName = $this->linearCongruentialGenerator->generate($this->linearCongruentialGeneratorOffset) . '_' . $newName; + } + + $this->fileName = $newName; + } + + public function setGeneratorValues($gap, $first, $offset) { + $this->linearCongruentialGenerator = new LinearCongruentialGenerator(); + $this->linearCongruentialGeneratorOffset = $offset; + + $this->linearCongruentialGenerator->setGap($gap); + $this->linearCongruentialGenerator->setFirst($first); + } + + public function setMaxSize($maxSize) { + $this->maxSize = $maxSize; + } + + public function getFileName() { + return $this->fileName; + } + } \ No newline at end of file diff --git a/server/libs/Hashing.php b/server/libs/Hashing.php index aa402cf5..da076181 100644 --- a/server/libs/Hashing.php +++ b/server/libs/Hashing.php @@ -7,10 +7,36 @@ class Hashing { public static function verifyPassword($password, $hash) { return password_verify($password, $hash); } + public static function generateRandomToken() { return md5(uniqid(rand())); } - public static function getRandomTicketNumber($min,$max) { - return rand($min,$max); + + public static function generateRandomNumber($min, $max) { + return rand($min, $max); + } + + public static function generateRandomPrime($min, $max) { + $number = Hashing::generateRandomNumber($min, $max); + + while(!Hashing::isPrime($number)) { + $number = Hashing::generateRandomNumber($min, $max); + } + + return $number; + } + + public static function isPrime($number) { + $sqrt = sqrt($number); + $prime = true; + + for($i = 0; $i < $sqrt; $i++) { + if($sqrt % 2 === 0) { + $prime = false; + break; + } + } + + return $prime; } } \ No newline at end of file diff --git a/server/libs/LinearCongruentialGenerator.php b/server/libs/LinearCongruentialGenerator.php new file mode 100644 index 00000000..88df4141 --- /dev/null +++ b/server/libs/LinearCongruentialGenerator.php @@ -0,0 +1,31 @@ +min = $min; + $this->max = $max; + } + + public function setGap($gap) { + if(!Hashing::isPrime($gap)) throw new Exception('LinearCongruentialGenerator: gap must be prime'); + + $this->gap = $gap; + } + + public function setFirst($first) { + $this->first = $first; + } + + public function generate($offset) { + if($offset) return ($this->first - $this->min + $offset * $this->gap) % ($this->max - $this->min + 1) + $this->min; + else return $this->generateFirst(); + } + + public function generateFirst() { + return Hashing::generateRandomNumber($this->min, $this->max); + } +} \ No newline at end of file diff --git a/server/models/Ticket.php b/server/models/Ticket.php index 315434e8..88351ea6 100644 --- a/server/models/Ticket.php +++ b/server/models/Ticket.php @@ -46,17 +46,16 @@ class Ticket extends DataStore { } public function generateUniqueTicketNumber() { + $linearCongruentialGenerator = new LinearCongruentialGenerator(); $ticketQuantity = Ticket::count(); - $minValue = 100000; - $maxValue = 999999; - + if ($ticketQuantity === 0) { - $ticketNumber = Hashing::getRandomTicketNumber($minValue, $maxValue); + $ticketNumber = $linearCongruentialGenerator->generateFirst(); } else { - $firstTicketNumber = Ticket::getTicket(1)->ticketNumber; - $gap = 176611; //TODO: USE RANDOM PRIME INSTEAD - - $ticketNumber = ($firstTicketNumber - $minValue + $ticketQuantity * $gap) % ($maxValue - $minValue + 1) + $minValue; + $linearCongruentialGenerator->setGap(Setting::getSetting('ticket-gap')->value); + $linearCongruentialGenerator->setFirst(Ticket::getTicket(1)->ticketNumber); + + $ticketNumber = $linearCongruentialGenerator->generate($ticketQuantity); } return $ticketNumber; diff --git a/tests/init.rb b/tests/init.rb index aaf24e28..1ecc936f 100644 --- a/tests/init.rb +++ b/tests/init.rb @@ -58,3 +58,4 @@ require './system/get-stats.rb' require './system/add-api-key.rb' require './system/delete-api-key.rb' require './system/get-all-keys.rb' +require './system/file-upload-download.rb' diff --git a/tests/libs.rb b/tests/libs.rb index 8b832429..fef8e646 100644 --- a/tests/libs.rb +++ b/tests/libs.rb @@ -1,5 +1,12 @@ $agent = Mechanize.new +def plainRequest(path, data = {}) + uri = 'http://localhost:8080' + path + response = $agent.post(uri, data) + + return response +end + def request(path, data = {}) uri = 'http://localhost:8080' + path response = $agent.post(uri, data) diff --git a/tests/system/edit-settings.rb b/tests/system/edit-settings.rb index 0b2b77aa..69223599 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" => 2, + "max-size" => 2048, "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('2') + (row['value']).should.equal('2048') row = $database.getRow('setting', 'language', 'name') (row['value']).should.equal('en') diff --git a/tests/system/file-upload-download.rb b/tests/system/file-upload-download.rb new file mode 100644 index 00000000..44bc3c37 --- /dev/null +++ b/tests/system/file-upload-download.rb @@ -0,0 +1,74 @@ +describe 'File Upload and Download' do + request('/user/logout') + Scripts.login('creator@os4.com', 'creator') + + it 'should upload file when creating ticket' do + file = File.new('../server/files/upload.txt', 'w+') + file.puts('file content') + file.close + + result = request('/ticket/create', { + 'csrf_userid' => $csrf_userid, + 'csrf_token' => $csrf_token, + 'title' => 'Ticket with file', + 'content' => 'this is a ticket that contains a file', + 'language' => 'en', + 'departmentId' => 1, + 'file' => File.open( "../server/files/upload.txt") + }) + (result['status']).should.equal('success') + + ticket = $database.getLastRow('ticket') + + (ticket['file'].include? 'upload.txt').should.equal(true) + (File.exist? ('../server/files/' + ticket['file'])).should.equal(true) + end + + it 'should download file if author is logged' do + ticket = $database.getLastRow('ticket') + file = File.open("../server/files/" + ticket['file']) + + result = plainRequest('/system/download', { + 'csrf_userid' => $csrf_userid, + 'csrf_token' => $csrf_token, + 'file' => ticket['file'] + }) + + (result.body).should.equal(file.read) + end + + it 'should not download if author is not logged' do + request('/user/logout') + Scripts.login('staff@opensupports.com', 'staff', true) + + ticket = $database.getLastRow('ticket') + + result = plainRequest('/system/download', { + 'csrf_userid' => $csrf_userid, + 'csrf_token' => $csrf_token, + 'file' => ticket['file'] + }) + + (result.body).should.equal('') + end + + it 'should download if owner is logged' do + ticket = $database.getLastRow('ticket') + file = File.open("../server/files/" + ticket['file']) + + request('/staff/assign-ticket', { + 'csrf_userid' => $csrf_userid, + 'csrf_token' => $csrf_token, + 'ticketNumber' => ticket['ticket_number'] + }) + + result = plainRequest('/system/download', { + 'csrf_userid' => $csrf_userid, + 'csrf_token' => $csrf_token, + 'file' => ticket['file'] + }) + + (result.body).should.equal(file.read) + end + +end diff --git a/tests/system/get-stats.rb b/tests/system/get-stats.rb index 406c4516..04d31e83 100644 --- a/tests/system/get-stats.rb +++ b/tests/system/get-stats.rb @@ -25,10 +25,10 @@ describe'/system/get-stats' do $database.query("INSERT INTO log VALUES('', 'COMMENT', NULL, " + yesterday3 + ", NULL, NULL);") end for i in 0..8 - $database.query("INSERT INTO ticketevent VALUES('', 'CLOSE', NULL, " + yesterday3 + ", NULL, NULL, 1);") + $database.query("INSERT INTO ticketevent VALUES('', 'CLOSE', NULL, NULL, " + yesterday3 + ", NULL, NULL, 1);") end for i in 0..4 - $database.query("INSERT INTO ticketevent VALUES('', 'ASSIGN', NULL, " + yesterday3 + ", NULL, NULL, 1);") + $database.query("INSERT INTO ticketevent VALUES('', 'ASSIGN', NULL, NULL, " + yesterday3 + ", NULL, NULL, 1);") end #day 2 @@ -45,10 +45,10 @@ describe'/system/get-stats' do $database.query("INSERT INTO log VALUES('', 'COMMENT', NULL, " + yesterday2 + ", NULL, NULL);") end for i in 0..10 - $database.query("INSERT INTO ticketevent VALUES('', 'CLOSE', NULL, " + yesterday2 + ", NULL, NULL, 1);") + $database.query("INSERT INTO ticketevent VALUES('', 'CLOSE', NULL, NULL, " + yesterday2 + ", NULL, NULL, 1);") end for i in 0..2 - $database.query("INSERT INTO ticketevent VALUES('', 'ASSIGN', NULL, " + yesterday2 + ", NULL, NULL, 1);") + $database.query("INSERT INTO ticketevent VALUES('', 'ASSIGN', NULL, NULL, " + yesterday2 + ", NULL, NULL, 1);") end #day 3 @@ -65,10 +65,10 @@ describe'/system/get-stats' do $database.query("INSERT INTO log VALUES('', 'COMMENT', NULL, " + yesterday + ", NULL, NULL);") end for i in 0..3 - $database.query("INSERT INTO ticketevent VALUES('', 'CLOSE', NULL, " + yesterday + ", NULL, NULL, 1);") + $database.query("INSERT INTO ticketevent VALUES('', 'CLOSE', NULL, NULL, " + yesterday + ", NULL, NULL, 1);") end for i in 0..7 - $database.query("INSERT INTO ticketevent VALUES('', 'ASSIGN', NULL, " + yesterday + ", NULL, NULL, 1);") + $database.query("INSERT INTO ticketevent VALUES('', 'ASSIGN', NULL, NULL, " + yesterday + ", NULL, NULL, 1);") end @result = request('/system/get-stats', { diff --git a/tests/ticket/create.rb b/tests/ticket/create.rb index 481e0b50..fbc6be44 100644 --- a/tests/ticket/create.rb +++ b/tests/ticket/create.rb @@ -147,13 +147,15 @@ describe '/ticket/create' do csrf_token: $csrf_token }) + ticket_number_gap = $database.getRow('setting', 'ticket-gap', 'name')['value'].to_i + ticket0 = $database.getRow('ticket','Winter is coming','title')['ticket_number'].to_i ticket1 = $database.getRow('ticket','Winter is coming1','title')['ticket_number'].to_i ticket2 = $database.getRow('ticket','Winter is coming2','title')['ticket_number'].to_i ticket3 = $database.getRow('ticket','Winter is coming3','title')['ticket_number'].to_i - (ticket1).should.equal((ticket0 - 100000 + 1 * 176611) % 900000 + 100000) - (ticket2).should.equal((ticket0 - 100000 + 2 * 176611) % 900000 + 100000) - (ticket3).should.equal((ticket0 - 100000 + 3 * 176611) % 900000 + 100000) + (ticket1).should.equal((ticket0 - 100000 + 1 * ticket_number_gap) % 900000 + 100000) + (ticket2).should.equal((ticket0 - 100000 + 2 * ticket_number_gap) % 900000 + 100000) + (ticket3).should.equal((ticket0 - 100000 + 3 * ticket_number_gap) % 900000 + 100000) end end