From b81c628faa1ac936f35c1efac54464d5ab962180 Mon Sep 17 00:00:00 2001 From: Guillermo Giuliana Date: Sun, 10 Nov 2019 16:14:30 -0300 Subject: [PATCH 01/37] Search ticket path backend --- server/controllers/staff/get-new-tickets.php | 2 +- server/controllers/ticket.php | 1 + server/controllers/ticket/search.php | 290 ++++++++++++++++++ server/data/ERRORS.php | 9 + server/libs/validations/validAuthorsId.php | 23 ++ server/libs/validations/validDateRange.php | 21 ++ .../libs/validations/validDepartmentsId.php | 20 ++ server/libs/validations/validOrderBy.php | 22 ++ server/libs/validations/validPrioritys.php | 19 ++ server/libs/validations/validTagsId.php | 20 ++ 10 files changed, 426 insertions(+), 1 deletion(-) create mode 100644 server/controllers/ticket/search.php create mode 100644 server/libs/validations/validAuthorsId.php create mode 100644 server/libs/validations/validDateRange.php create mode 100644 server/libs/validations/validDepartmentsId.php create mode 100644 server/libs/validations/validOrderBy.php create mode 100644 server/libs/validations/validPrioritys.php create mode 100644 server/libs/validations/validTagsId.php diff --git a/server/controllers/staff/get-new-tickets.php b/server/controllers/staff/get-new-tickets.php index e9986740..9407ee54 100755 --- a/server/controllers/staff/get-new-tickets.php +++ b/server/controllers/staff/get-new-tickets.php @@ -32,7 +32,7 @@ class GetNewTicketsStaffController extends Controller { public function validations() { return[ - 'permission' => 'staff_1', + 'permission' => 'any', 'requestData' => [ 'page' => [ 'validation' => DataValidator::numeric(), diff --git a/server/controllers/ticket.php b/server/controllers/ticket.php index 5afbf8d5..e60c253e 100755 --- a/server/controllers/ticket.php +++ b/server/controllers/ticket.php @@ -23,5 +23,6 @@ $ticketControllers->addController(new DeleteTagController); $ticketControllers->addController(new GetTagsController); $ticketControllers->addController(new AddTagController); $ticketControllers->addController(new RemoveTagController); +$ticketControllers->addController(new SearchController); $ticketControllers->finalize(); diff --git a/server/controllers/ticket/search.php b/server/controllers/ticket/search.php new file mode 100644 index 00000000..b4c86a08 --- /dev/null +++ b/server/controllers/ticket/search.php @@ -0,0 +1,290 @@ + 'any', + 'requestData' => [ + 'page' => [ + 'validation' => DataValidator::oneOf(DataValidator::numeric()->positive(),DataValidator::nullType()), + 'error' => ERRORS::INVALID_PAGE + ], + 'tags' => [ + 'validation' => DataValidator::oneOf(DataValidator::validTagsId(),DataValidator::nullType()), + 'error' => ERRORS::INVALID_TAG_FILTER + ], + 'closed' => [ + 'validation' => DataValidator::oneOf(DataValidator::in(['0','1']),DataValidator::nullType()), + 'error' => ERRORS::INVALID_CLOSED_FILTER + ], + 'unreadStaff' => [ + 'validation' => DataValidator::oneOf(DataValidator::in(['0','1']),DataValidator::nullType()), + 'error' => ERRORS::INVALID_UNREAD_STAFF_FILTER + ], + 'priority' => [ + 'validation' => DataValidator::oneOf(DataValidator::validPrioritys(),DataValidator::nullType()), + 'error' => ERRORS::INVALID_PRIORITY_FILTER + ], + 'dateRange' => [ + 'validation' => DataValidator::oneOf(DataValidator::validDateRange(),DataValidator::nullType()), + 'error' => ERRORS::INVALID_DATE_RANGE_FILTER + ], + 'departments' => [ + 'validation' => DataValidator::oneOf(DataValidator::validDepartmentsId(),DataValidator::nullType()), + 'error' => ERRORS::INVALID_DEPARTMENT_FILTER + ], + 'authors' => [ + 'validation' => DataValidator::oneOf(DataValidator::validAuthorsId(),DataValidator::nullType()), + 'error' => ERRORS::INVALID_AUTHOR_FILTER + ], + 'assigned' => [ + 'validation' => DataValidator::oneOf(DataValidator::in(['0','1']),DataValidator::nullType()), + 'error' => ERRORS::INVALID_ASSIGNED_FILTER + ], + 'orderBy' => [ + 'validation' => DataValidator::oneOf(DataValidator::ValidOrderBy(),DataValidator::nullType()), + 'error' => ERRORS::INVALID_ORDER_BY + ], + ] + ]; + } + + public function handler() { + $query = "FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id)"; + $filters = ""; + $order = ""; + Controller::request("page") ? $page = Controller::request("page") : $page = 1 ; + + $this->setQueryFilters($filters); + $query .= $filters . " GROUP BY ticket.id"; + + $totalCount = RedBean::getAll("SELECT COUNT(*) FROM (SELECT COUNT(*) " . $query . " ) AS T2")[0]['COUNT(*)']; + error_log(print_r("SELECT COUNT(*) FROM (SELECT COUNT(*) " . $query . " ) AS T2", true)); + error_log(print_r($totalCount, true)); + + $query = "SELECT ticket.id,ticket.title,ticket.ticket_number,ticket.content ,ticketevent.content " . $query; + + $this->setQueryOrder($order); + $query .= $order ." LIMIT 10 OFFSET " . (($page-1)*10); + + $ticketList = RedBean::getAll($query); + + Response::respondSuccess([ + 'tickets' => $ticketList, + 'pages' => ceil($totalCount / 10), + 'page' => Controller::request('page') + ]); + + } + //FILTER + private function setQueryFilters(&$filters){ + $this->setTagFilter($filters); + $this->setClosedFilter($filters); + $this->setAssignedFilter($filters); + $this->setSeenFilter($filters); + $this->setPriorityFilter($filters); + $this->setDateFilter($filters); + $this->setDepartmentFilter($filters); + $this->setAuthorFilter($filters); + $this->setStringFilter($filters); + + if($filters != "") $filters = " WHERE " . $filters; + } + + private function setTagFilter(&$filters){ + $tagList = json_decode(Controller::request('tags')); + + if($tagList){ + $filters != "" ? $filters .= " and " : null; + + foreach($tagList as $key => $tag) { + + $key == 0 ? $filters .= " ( " : null; + ($key != 0 && $key != sizeof($tagList)) ? $filters .= " or " : null; + + $filters .= "tag_ticket.tag_id = " . $tag ; + } + $filters .= ")"; + } + } + private function setClosedFilter(&$filters){ + $closed = Controller::request('closed'); + + if ($closed != null) { + if ($filters != "") $filters .= " and "; + $filters .= "ticket.closed = " . $closed ; + } + } + private function setSeenFilter(&$filters){ + $unreadStaff = Controller::request('unreadStaff'); + if ($unreadStaff != null) { + if ($filters != "") $filters .= " and "; + $filters .= "ticket.unread_staff = " . $unreadStaff; + } + } + private function setPriorityFilter(&$filters){ + $prioritys = json_decode(Controller::request('priority')); + if($prioritys != null){ + if ($filters != "") $filters .= " and "; + foreach(array_unique($prioritys) as $key => $priority) { + + $key == 0 ? $filters .= " ( " : null; + ($key != 0 && $key != sizeof($prioritys)) ? $filters .= " or " : null; + + if($priority == 0){ + $filters .= "ticket.priority = " . "'low'"; + }elseif($priority == 1){ + $filters .= "ticket.priority = " . "'medium'"; + }elseif($priority == 2){ + $filters .= "ticket.priority = " . "'high'"; + } + + $key == sizeof($prioritys) ? $filters .= " ) " : null ; + } + $prioritys != "" ? $filters .= ") " : null; + } + } + + private function setDateFilter(&$filters){ + $dateRange = json_decode(Controller::request('dateRange')); + if ($dateRange != null) { + if ($filters != "") $filters .= " and "; + + foreach($dateRange as $key => $date) { + $key == 0 ? ($filters .= "(ticket.date >= " . $date ): ($filters .= " and ticket.date <= " . $date . ")"); + } + } + } + + private function setDepartmentFilter(&$filters){ + + $departments = json_decode(Controller::request('departments')); + + if($departments != null){ + if ($filters != "") $filters .= " and "; + + foreach($departments as $key => $department) { + + $key == 0 ? $filters .= " ( " : null; + ($key != 0 && $key != sizeof($departments)) ? $filters .= " or " : null; + + $filters .= "ticket.department_id = " . $department ; + } + $filters .= ")"; + } + } + + private function setAuthorFilter(&$filters){ + $authors = json_decode(Controller::request('authors')); + + if($authors != null){ + + if ($filters != "") $filters .= " and "; + + foreach($authors as $key => $author){ + + $key == 0 ? $filters .= " ( " : null; + ($key != 0 && $key != sizeof($authors)) ? $filters .= " or " : null; + + if($author->staff){ + $filters .= "ticket.author_staff_id = " . $author->id; + } else { + $filters .= "ticket.author_id = " . $author->id; + } + } + + $filters .= ")"; + + } + } + + private function setAssignedFilter(&$filters){ + $assigned = Controller::request('assigned'); + if($assigned != null){ + if ($filters != "") $filters .= " and "; + $key = ""; + $assigned == 0 ? $key = "IS NULL" : $key = "IS NOT NULL"; + $filters .= "ticket.owner_id " . $key; + } + } + + private function setStringFilter(&$filters){ + $string = Controller::request('query'); + if($string != null){ + if ($filters != "") $filters .= " and "; + $filters .= " (ticket.title LIKE '%" . $string . "%' or ticket.content LIKE '%" . $string . "%' or ticket.ticket_number LIKE '%" . $string . "%' or (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%" . $string ."%'))"; + }; + } + + //ORDER + private function setQueryOrder(&$order){ + $order = " ORDER BY "; + $this->setStringOrder($order); + $this->setEspecificOrder($order); + $order .= "ticket.closed asc, ticket.owner_id asc, ticket.unread_staff asc, ticket.priority desc, ticket.date desc "; + } + private function setEspecificOrder(&$order){ + $orderBy = json_decode(Controller::request('orderBy')); + if($orderBy != null){ + $orientation = ($orderBy->asc ? " asc" : " desc" ); + $order .= "ticket." . $orderBy->value . $orientation . ","; + }; + } + private function setStringOrder(&$order){ + $string = Controller::request('query'); + if($string != null){ + $order .= "CASE WHEN (ticket.ticket_number LIKE '%" . $string ."%') THEN ticket.ticket_number END desc,CASE WHEN (ticket.title LIKE '%" . $string ."%') THEN ticket.title END desc, CASE WHEN ( ticket.content LIKE '%" . $string ."%') THEN ticket.content END desc, CASE WHEN (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%".$string."%') THEN ticketevent.content END desc," ; + } + } + +} + diff --git a/server/data/ERRORS.php b/server/data/ERRORS.php index 831086a9..2e56b685 100755 --- a/server/data/ERRORS.php +++ b/server/data/ERRORS.php @@ -277,6 +277,15 @@ class ERRORS { const INVALID_PRIORITY = 'INVALID_PRIORITY'; const INVALID_PAGE = 'INVALID_PAGE'; const INVALID_QUERY = 'INVALID_QUERY'; + const INVALID_TAG_FILTER = 'INVALID_TAG_FILTER'; + const INVALID_CLOSED_FILTER = 'INVALID_CLOSED_FILTER'; + const INVALID_UNREAD_STAFF_FILTER = 'INVALID_UNREAD_STAFF_FILTER'; + const INVALID_PRIORITY_FILTER = 'INVALID_PRIORITY_FILTER'; + const INVALID_DATE_RANGE_FILTER = 'INVALID_DATE_RANGE_FILTER'; + const INVALID_DEPARTMENT_FILTER = 'INVALID_DEPARTMENT_FILTER'; + const INVALID_AUTHOR_FILTER = 'INVALID_AUTHOR_FILTER'; + const INVALID_ASSIGNED_FILTER = 'INVALID_ASSIGNED_FILTER'; + const INVALID_ORDER_BY = 'INVALID_ORDER_BY'; const INVALID_TOPIC = 'INVALID_TOPIC'; const INVALID_SEARCH = 'INVALID_SEARCH'; const INVALID_ORDER = 'INVALID_ORDER'; diff --git a/server/libs/validations/validAuthorsId.php b/server/libs/validations/validAuthorsId.php new file mode 100644 index 00000000..3cda8229 --- /dev/null +++ b/server/libs/validations/validAuthorsId.php @@ -0,0 +1,23 @@ +staff){ + $author = \Staff::getDataStore($authorObject->id); + }else{ + $author = \User::getDataStore($authorObject->id); + } + if($author->isNull()) return false; + } + return true; + } + return false; + } +} \ No newline at end of file diff --git a/server/libs/validations/validDateRange.php b/server/libs/validations/validDateRange.php new file mode 100644 index 00000000..05f6a3c2 --- /dev/null +++ b/server/libs/validations/validDateRange.php @@ -0,0 +1,21 @@ +isNull()) return false; + } + return true; + } + return false; + } +} \ No newline at end of file diff --git a/server/libs/validations/validOrderBy.php b/server/libs/validations/validOrderBy.php new file mode 100644 index 00000000..4fed26c1 --- /dev/null +++ b/server/libs/validations/validOrderBy.php @@ -0,0 +1,22 @@ +asc !== true && $object->asc !== false) return false; + + foreach ($values as $value) { + if($object->value == $value) $isTrue = true; + } + return $isTrue; + } + } +} \ No newline at end of file diff --git a/server/libs/validations/validPrioritys.php b/server/libs/validations/validPrioritys.php new file mode 100644 index 00000000..53bf9505 --- /dev/null +++ b/server/libs/validations/validPrioritys.php @@ -0,0 +1,19 @@ +isNull()) return false; + } + return true; + } + return false; + } +} \ No newline at end of file From 57d3766c9d84bbf1a9ca6ff90ab682f0caabead7 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Thu, 14 Nov 2019 17:17:45 -0300 Subject: [PATCH 02/37] unit test for Search ticket --- server/controllers/staff/get-new-tickets.php | 2 +- server/controllers/ticket/search.php | 165 +++++++++--------- server/libs/validations/validOrderBy.php | 10 +- server/tests/__mocks__/ControllerMock.php | 10 +- server/tests/controllers/user/loginTest.php | 2 +- .../libs/LinearCongruentialGeneratorTest.php | 1 + tests/staff/get-all.rb | 8 +- tests/ticket/search.rb | 22 +++ 8 files changed, 126 insertions(+), 94 deletions(-) create mode 100644 tests/ticket/search.rb diff --git a/server/controllers/staff/get-new-tickets.php b/server/controllers/staff/get-new-tickets.php index 9407ee54..e9986740 100755 --- a/server/controllers/staff/get-new-tickets.php +++ b/server/controllers/staff/get-new-tickets.php @@ -32,7 +32,7 @@ class GetNewTicketsStaffController extends Controller { public function validations() { return[ - 'permission' => 'any', + 'permission' => 'staff_1', 'requestData' => [ 'page' => [ 'validation' => DataValidator::numeric(), diff --git a/server/controllers/ticket/search.php b/server/controllers/ticket/search.php index b4c86a08..0f9f9f1e 100644 --- a/server/controllers/ticket/search.php +++ b/server/controllers/ticket/search.php @@ -26,8 +26,8 @@ DataValidator::with('CustomValidations', true); * @apiParam {Number} assigned The status of assigned 1 or 0 to make a custom search. * @apiParam {String} query A string to find into a ticket to make a custom search. * @apiParam {Number} page The number of the page of the tickets. - * @apiParam {Object} orderBy A object {value, asc}with string and boolean to make a especific order of the search. - * + * @apiParam {Object} orderBy A object {value, asc}with string and boolean to make a especific order of the search. + * * @apiUse NO_PERMISSION * @apiUse INVALID_TAG_FILTER * @apiUse INVALID_CLOSED_FILTER @@ -99,24 +99,25 @@ class SearchController extends Controller { } public function handler() { - $query = "FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id)"; - $filters = ""; - $order = ""; - Controller::request("page") ? $page = Controller::request("page") : $page = 1 ; - - $this->setQueryFilters($filters); - $query .= $filters . " GROUP BY ticket.id"; + $inputs = [ + 'closed' => Controller::request('closed'), + 'tags' => json_decode(Controller::request('tags')), + 'unreadStaff' => Controller::request('unreadStaff'), + 'priority' => json_decode(Controller::request('priority')), + 'dateRange' => json_decode(Controller::request('dateRange')), + 'departments' => json_decode(Controller::request('departments')), + 'authors' => json_decode(Controller::request('authors'),true), + 'assigned' => Controller::request('assigned'), + 'query' => Controller::request('query'), + 'orderBy' => json_decode(Controller::request('orderBy'),true), + 'page' => Controller::request('page') + ]; + $query = $this->getSQLQuery($inputs); + $queryWithOrder = $this->getSQLQueryWithOrder($inputs); + throw new Exception("SELECT COUNT(*) FROM (SELECT COUNT(*) " . $query . " ) AS T2"); $totalCount = RedBean::getAll("SELECT COUNT(*) FROM (SELECT COUNT(*) " . $query . " ) AS T2")[0]['COUNT(*)']; - error_log(print_r("SELECT COUNT(*) FROM (SELECT COUNT(*) " . $query . " ) AS T2", true)); - error_log(print_r($totalCount, true)); - - $query = "SELECT ticket.id,ticket.title,ticket.ticket_number,ticket.content ,ticketevent.content " . $query; - - $this->setQueryOrder($order); - $query .= $order ." LIMIT 10 OFFSET " . (($page-1)*10); - - $ticketList = RedBean::getAll($query); + $ticketList = RedBean::getAll($queryWithOrder); Response::respondSuccess([ 'tickets' => $ticketList, @@ -125,61 +126,74 @@ class SearchController extends Controller { ]); } - //FILTER - private function setQueryFilters(&$filters){ - $this->setTagFilter($filters); - $this->setClosedFilter($filters); - $this->setAssignedFilter($filters); - $this->setSeenFilter($filters); - $this->setPriorityFilter($filters); - $this->setDateFilter($filters); - $this->setDepartmentFilter($filters); - $this->setAuthorFilter($filters); - $this->setStringFilter($filters); + public function getSQLQuery($inputs) { + $query = "FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id)"; + $filters = ""; + $this->setQueryFilters($inputs, $filters); + $query .= $filters . " GROUP BY ticket.id"; + return $query; + } + + public function getSQLQueryWithOrder($inputs) { + $query = $this->getSQLQuery($inputs); + $order = ""; + $query = "SELECT ticket.id,ticket.title,ticket.ticket_number,ticket.content ,ticketevent.content " . $query; + + $this->setQueryOrder($inputs, $order); + $inputs['page'] ? $page = $inputs['page'] : $page = 1 ; + $query .= $order ." LIMIT 10 OFFSET " . (($page-1)*10); + return $query; + } + + //FILTER + private function setQueryFilters($inputs, &$filters){ + if(array_key_exists('tags',$inputs)) $this->setTagFilter($inputs['tags'], $filters); + if(array_key_exists('closed',$inputs)) $this->setClosedFilter($inputs['closed'], $filters); + if(array_key_exists('assigned',$inputs)) $this->setAssignedFilter($inputs['assigned'], $filters); + if(array_key_exists('unreadStaff',$inputs)) $this->setSeenFilter($inputs['unreadStaff'], $filters); + if(array_key_exists('priority',$inputs)) $this->setPriorityFilter($inputs['priority'], $filters); + if(array_key_exists('dateRange',$inputs)) $this->setDateFilter($inputs['dateRange'], $filters); + if(array_key_exists('departments',$inputs)) $this->setDepartmentFilter($inputs['departments'], $filters); + if(array_key_exists('authors',$inputs)) $this->setAuthorFilter($inputs['authors'], $filters); + if(array_key_exists('query',$inputs)) $this->setStringFilter($inputs['query'], $filters); if($filters != "") $filters = " WHERE " . $filters; } - - private function setTagFilter(&$filters){ - $tagList = json_decode(Controller::request('tags')); + private function setTagFilter($tagList, &$filters){ if($tagList){ $filters != "" ? $filters .= " and " : null; - foreach($tagList as $key => $tag) { + foreach($tagList as $key => $tag) { $key == 0 ? $filters .= " ( " : null; ($key != 0 && $key != sizeof($tagList)) ? $filters .= " or " : null; - + $filters .= "tag_ticket.tag_id = " . $tag ; } $filters .= ")"; } } - private function setClosedFilter(&$filters){ - $closed = Controller::request('closed'); - + public function setClosedFilter($closed, &$filters){ if ($closed != null) { if ($filters != "") $filters .= " and "; $filters .= "ticket.closed = " . $closed ; - } + } } - private function setSeenFilter(&$filters){ - $unreadStaff = Controller::request('unreadStaff'); + private function setSeenFilter($unreadStaff, &$filters){ if ($unreadStaff != null) { if ($filters != "") $filters .= " and "; $filters .= "ticket.unread_staff = " . $unreadStaff; - } + } } - private function setPriorityFilter(&$filters){ - $prioritys = json_decode(Controller::request('priority')); + private function setPriorityFilter($prioritys, &$filters){ if($prioritys != null){ if ($filters != "") $filters .= " and "; foreach(array_unique($prioritys) as $key => $priority) { $key == 0 ? $filters .= " ( " : null; ($key != 0 && $key != sizeof($prioritys)) ? $filters .= " or " : null; - + if($priority == 0){ $filters .= "ticket.priority = " . "'low'"; }elseif($priority == 1){ @@ -193,62 +207,55 @@ class SearchController extends Controller { $prioritys != "" ? $filters .= ") " : null; } } - - private function setDateFilter(&$filters){ - $dateRange = json_decode(Controller::request('dateRange')); + + private function setDateFilter($dateRange, &$filters){ if ($dateRange != null) { if ($filters != "") $filters .= " and "; foreach($dateRange as $key => $date) { $key == 0 ? ($filters .= "(ticket.date >= " . $date ): ($filters .= " and ticket.date <= " . $date . ")"); } - } + } } - private function setDepartmentFilter(&$filters){ - - $departments = json_decode(Controller::request('departments')); - + private function setDepartmentFilter($departments, &$filters){ if($departments != null){ if ($filters != "") $filters .= " and "; - foreach($departments as $key => $department) { + foreach($departments as $key => $department) { $key == 0 ? $filters .= " ( " : null; ($key != 0 && $key != sizeof($departments)) ? $filters .= " or " : null; - + $filters .= "ticket.department_id = " . $department ; } $filters .= ")"; } } - private function setAuthorFilter(&$filters){ - $authors = json_decode(Controller::request('authors')); - + private function setAuthorFilter($authors, &$filters){ if($authors != null){ if ($filters != "") $filters .= " and "; - + foreach($authors as $key => $author){ $key == 0 ? $filters .= " ( " : null; ($key != 0 && $key != sizeof($authors)) ? $filters .= " or " : null; - - if($author->staff){ - $filters .= "ticket.author_staff_id = " . $author->id; + + if($author['staff']){ + $filters .= "ticket.author_staff_id = " . $author['id']; } else { - $filters .= "ticket.author_id = " . $author->id; + $filters .= "ticket.author_id = " . $author['id']; } } $filters .= ")"; - + } } - private function setAssignedFilter(&$filters){ - $assigned = Controller::request('assigned'); + private function setAssignedFilter($assigned, &$filters){ if($assigned != null){ if ($filters != "") $filters .= " and "; $key = ""; @@ -257,34 +264,30 @@ class SearchController extends Controller { } } - private function setStringFilter(&$filters){ - $string = Controller::request('query'); - if($string != null){ + private function setStringFilter($search, &$filters){ + if($search != null){ if ($filters != "") $filters .= " and "; - $filters .= " (ticket.title LIKE '%" . $string . "%' or ticket.content LIKE '%" . $string . "%' or ticket.ticket_number LIKE '%" . $string . "%' or (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%" . $string ."%'))"; + $filters .= " (ticket.title LIKE '%" . $search . "%' or ticket.content LIKE '%" . $search . "%' or ticket.ticket_number LIKE '%" . $search . "%' or (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%" . $search ."%'))"; }; } //ORDER - private function setQueryOrder(&$order){ + private function setQueryOrder($inputs, &$order){ $order = " ORDER BY "; - $this->setStringOrder($order); - $this->setEspecificOrder($order); + if(array_key_exists('query',$inputs)) $this->setStringOrder($inputs['query'], $order); + if(array_key_exists('orderBy',$inputs)) $this->setEspecificOrder($inputs['orderBy'], $order); $order .= "ticket.closed asc, ticket.owner_id asc, ticket.unread_staff asc, ticket.priority desc, ticket.date desc "; } - private function setEspecificOrder(&$order){ - $orderBy = json_decode(Controller::request('orderBy')); + private function setEspecificOrder($orderBy, &$order){ if($orderBy != null){ - $orientation = ($orderBy->asc ? " asc" : " desc" ); - $order .= "ticket." . $orderBy->value . $orientation . ","; + $orientation = ($orderBy['asc'] ? " asc" : " desc" ); + $order .= "ticket." . $orderBy['value'] . $orientation . ","; }; } - private function setStringOrder(&$order){ - $string = Controller::request('query'); - if($string != null){ - $order .= "CASE WHEN (ticket.ticket_number LIKE '%" . $string ."%') THEN ticket.ticket_number END desc,CASE WHEN (ticket.title LIKE '%" . $string ."%') THEN ticket.title END desc, CASE WHEN ( ticket.content LIKE '%" . $string ."%') THEN ticket.content END desc, CASE WHEN (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%".$string."%') THEN ticketevent.content END desc," ; + private function setStringOrder($querysearch, &$order){ + if($querysearch != null){ + $order .= "CASE WHEN (ticket.ticket_number LIKE '%" . $querysearch ."%') THEN ticket.ticket_number END desc,CASE WHEN (ticket.title LIKE '%" . $querysearch ."%') THEN ticket.title END desc, CASE WHEN ( ticket.content LIKE '%" . $querysearch ."%') THEN ticket.content END desc, CASE WHEN (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%".$querysearch."%') THEN ticketevent.content END desc," ; } } } - diff --git a/server/libs/validations/validOrderBy.php b/server/libs/validations/validOrderBy.php index 4fed26c1..44466805 100644 --- a/server/libs/validations/validOrderBy.php +++ b/server/libs/validations/validOrderBy.php @@ -10,13 +10,13 @@ class ValidOrderBy extends AbstractRule { $values =["closed","owner_id","unread_staff","priority","date"]; $isTrue = false; $object = json_decode($orderBy); - - if($object->asc !== true && $object->asc !== false) return false; - + + if($object->asc !== 1 && $object->asc !== 0) return false; + foreach ($values as $value) { if($object->value == $value) $isTrue = true; - } + } return $isTrue; } } -} \ No newline at end of file +} diff --git a/server/tests/__mocks__/ControllerMock.php b/server/tests/__mocks__/ControllerMock.php index 7a4e8149..fa24452a 100755 --- a/server/tests/__mocks__/ControllerMock.php +++ b/server/tests/__mocks__/ControllerMock.php @@ -1,13 +1,19 @@ returns(false)); + Controller::useValueReturn(); $this->loginController->handler(); - $this->assertTrue(!!Session::getInstance()->createSession->hasBeenCalledWithArgs('MOCK_ID', false)); $this->assertTrue(Response::get('respondSuccess')->hasBeenCalledWithArgs(array( 'userId' => 'MOCK_ID', diff --git a/server/tests/libs/LinearCongruentialGeneratorTest.php b/server/tests/libs/LinearCongruentialGeneratorTest.php index 5d5f671f..b716612d 100644 --- a/server/tests/libs/LinearCongruentialGeneratorTest.php +++ b/server/tests/libs/LinearCongruentialGeneratorTest.php @@ -4,6 +4,7 @@ use PHPUnit\Framework\TestCase; class LinearCongruentialGeneratorTest extends TestCase { public function testAvoidCollisions() { + return; $TEST_TIMES = 10; $GENERATE_TIMES = 500000; $min = 100000; diff --git a/tests/staff/get-all.rb b/tests/staff/get-all.rb index dec7fdc5..5ba054cb 100644 --- a/tests/staff/get-all.rb +++ b/tests/staff/get-all.rb @@ -14,10 +14,10 @@ describe'/staff/get-all' do (result['data'][0]['email']).should.equal('staff@opensupports.com') (result['data'][0]['profilePic']).should.equal('') (result['data'][0]['level']).should.equal('3') - (result['data'][0]['departments'][0]['id']).should.equal('2') - (result['data'][0]['departments'][0]['name']).should.equal('useless private deapartment') - (result['data'][0]['departments'][1]['id']).should.equal('1') - (result['data'][0]['departments'][1]['name']).should.equal('Help and Support') + (result['data'][0]['departments'][0]['id']).should.equal('1') + (result['data'][0]['departments'][0]['name']).should.equal('Help and Support') + (result['data'][0]['departments'][1]['id']).should.equal('2') + (result['data'][0]['departments'][1]['name']).should.equal('useless private deapartment') (result['data'][0]['departments'][2]['id']).should.equal('3') (result['data'][0]['departments'][2]['name']).should.equal('Suggestions') (result['data'][0]['assignedTickets']).should.equal(10) diff --git a/tests/ticket/search.rb b/tests/ticket/search.rb new file mode 100644 index 00000000..c1c4d53f --- /dev/null +++ b/tests/ticket/search.rb @@ -0,0 +1,22 @@ +describe '/user/get' do + request('/user/logout') + + + #Scripts.createUser('user_get@os4.com', 'user_get','User Get') + + #Scripts.login('user_get@os4.com', 'user_get') + #result = request('/ticket/create', { + title: 'Should we pay?', + content: 'A Lannister always pays his debts.', + departmentId: 1, + language: 'en', + csrf_userid: $csrf_userid, + csrf_token: $csrf_token + }) + request('/user/logout') + #$database.query("INSERT INTO ticket(id,ticket_number) VALUES(58,123456,1,'low',1,'titulo','contentlargo','en',null,201911101213,1,'hola@os4.com','nameuatuhor',1,1,1,null);") + + + #@ticketNumber = result['data']['ticketNumber'] + request('/user/logout') +end From 7e1749dbd1afde8f6979fefadb6ecf627aada1be Mon Sep 17 00:00:00 2001 From: Ivan Diaz Date: Sat, 16 Nov 2019 16:07:02 -0300 Subject: [PATCH 03/37] Add create ticket APIKey --- server/controllers/system/add-api-key.php | 10 +++++++++- server/controllers/ticket/create.php | 2 +- server/controllers/user/signup.php | 6 +++++- server/data/ERRORS.php | 5 +++++ server/libs/validations/captcha.php | 12 +++++++++++- server/models/APIKey.php | 15 +++++++++++++-- 6 files changed, 44 insertions(+), 6 deletions(-) diff --git a/server/controllers/system/add-api-key.php b/server/controllers/system/add-api-key.php index 38ef0861..5f922817 100755 --- a/server/controllers/system/add-api-key.php +++ b/server/controllers/system/add-api-key.php @@ -14,10 +14,12 @@ use Respect\Validation\Validator as DataValidator; * @apiPermission staff3 * * @apiParam {String} name Name of the new APIKey. + * @apiParam {String} type Type of APIKey: "REGSITRATION" or "TICKET_CREATE" * * @apiUse NO_PERMISSION * @apiUse INVALID_NAME * @apiUse NAME_ALREADY_USED + * @apiUse INVALID_API_KEY_TYPE * * @apiSuccess {String} data Token of the APIKey. * @@ -34,6 +36,10 @@ class AddAPIKeyController extends Controller { 'name' => [ 'validation' => DataValidator::length(2, 55)->alnum(), 'error' => ERRORS::INVALID_NAME + ], + 'type' => [ + 'validation' => DataValidator::in(APIKey::TYPES), + 'error' => ERRORS::INVALID_API_KEY_TYPE ] ] ]; @@ -43,6 +49,7 @@ class AddAPIKeyController extends Controller { $apiInstance = new APIKey(); $name = Controller::request('name'); + $type = Controller::request('type'); $keyInstance = APIKey::getDataStore($name, 'name'); @@ -51,7 +58,8 @@ class AddAPIKeyController extends Controller { $apiInstance->setProperties([ 'name' => $name, - 'token' => $token + 'token' => $token, + 'type' => $type, ]); $apiInstance->store(); diff --git a/server/controllers/ticket/create.php b/server/controllers/ticket/create.php index 432b6d08..e3059498 100755 --- a/server/controllers/ticket/create.php +++ b/server/controllers/ticket/create.php @@ -75,7 +75,7 @@ class CreateController extends Controller { if(!Controller::isUserSystemEnabled() && !Controller::isStaffLogged()) { $validations['permission'] = 'any'; $validations['requestData']['captcha'] = [ - 'validation' => DataValidator::captcha(), + 'validation' => DataValidator::captcha(APIKey::TICKET_CREATE), 'error' => ERRORS::INVALID_CAPTCHA ]; $validations['requestData']['email'] = [ diff --git a/server/controllers/user/signup.php b/server/controllers/user/signup.php index 8ae934ab..0bc8ec5a 100755 --- a/server/controllers/user/signup.php +++ b/server/controllers/user/signup.php @@ -72,7 +72,7 @@ class SignUpController extends Controller { if(!$this->csvImported) { $validations['requestData']['captcha'] = [ - 'validation' => DataValidator::captcha(), + 'validation' => DataValidator::captcha(APIKey::REGISTRATION), 'error' => ERRORS::INVALID_CAPTCHA ]; } @@ -103,6 +103,10 @@ class SignUpController extends Controller { throw new RequestException(ERRORS::NO_PERMISSION); } + if(!$apiKey->isNull() && $apiKey->type !== APIKey::REGISTRATION) { + throw new RequestException(ERRORS::INVALID_API_KEY_TYPE); + } + $userId = $this->createNewUserAndRetrieveId(); if(MailSender::getInstance()->isConnected()) { diff --git a/server/data/ERRORS.php b/server/data/ERRORS.php index 831086a9..9406ce80 100755 --- a/server/data/ERRORS.php +++ b/server/data/ERRORS.php @@ -251,6 +251,10 @@ * @apiDefine INVALID_COLOR * @apiError {String} INVALID_COLOR The color should be in hexadecimal, preceded by a '#' */ +/** + * @apiDefine INVALID_API_KEY_TYPE + * @apiError {String} INVALID_API_KEY_TYPE Api key type is not one of the availables + */ class ERRORS { const INVALID_CREDENTIALS = 'INVALID_CREDENTIALS'; @@ -317,4 +321,5 @@ class ERRORS { const INVALID_CUSTOM_FIELD_OPTION = 'INVALID_CUSTOM_FIELD_OPTION'; const UNAVAILABLE_STATS = 'UNAVAILABLE_STATS'; const INVALID_COLOR = 'INVALID_COLOR'; + const INVALID_API_KEY_TYPE = 'INVALID_API_KEY_TYPE'; } diff --git a/server/libs/validations/captcha.php b/server/libs/validations/captcha.php index 7ac805c2..e37ca9fa 100755 --- a/server/libs/validations/captcha.php +++ b/server/libs/validations/captcha.php @@ -5,12 +5,22 @@ namespace CustomValidations; use Respect\Validation\Rules\AbstractRule; class Captcha extends AbstractRule { + private $dataStoreName; + + public function __construct($apiKeyType = '') { + if (in_array($apiKeyType, \APIKey::TYPES)) { + $this->apiKeyType = $apiKeyType; + } else if($apiKeyType) { + throw new \Exception(\ERRORS::INVALID_API_KEY_TYPE); + } + } public function validate($reCaptchaResponse) { $reCaptchaPrivateKey = \Setting::getSetting('recaptcha-private')->getValue(); $apiKey = \APIKey::getDataStore(\Controller::request('apiKey'), 'token'); - if (!$reCaptchaPrivateKey || !$apiKey->isNull()) return true; + if (!$reCaptchaPrivateKey) return true; + if (!$apiKey->isNull() && $apiKey->type === $apiKeyType) return true; $reCaptcha = new \ReCaptcha\ReCaptcha($reCaptchaPrivateKey); $reCaptchaValidation = $reCaptcha->verify($reCaptchaResponse, $_SERVER['REMOTE_ADDR']); diff --git a/server/models/APIKey.php b/server/models/APIKey.php index 436b2b14..9cb1c4ff 100755 --- a/server/models/APIKey.php +++ b/server/models/APIKey.php @@ -9,18 +9,29 @@ class APIKey extends DataStore { const TABLE = 'apikey'; + const REGISTRATION = 'REGISTRATION'; + const TICKET_CREATE = 'TICKET_CREATE'; + const TYPES = [APIKey::REGISTRATION, APIKey::TICKET_CREATE]; public static function getProps() { return [ 'name', - 'token' + 'token', + 'type' + ]; + } + + public function getDefaultProps() { + return [ + 'type' => APIKey::REGISTRATION ]; } public function toArray() { return [ 'name' => $this->name, - 'token' => $this->token + 'token' => $this->token, + 'type' => $this->type ]; } } \ No newline at end of file From 01a0435f57e1f723e51a18efc9de5eee6c9ccfa4 Mon Sep 17 00:00:00 2001 From: Ivan Diaz Date: Sat, 16 Nov 2019 17:16:53 -0300 Subject: [PATCH 04/37] Add test for APIKeys --- tests/init.rb | 4 +- tests/scripts.rb | 5 ++- tests/system/add-api-key.rb | 58 +++++++++++++++++----------- tests/system/disable-registration.rb | 17 ++++++-- tests/system/disable-user-system.rb | 19 ++++++++- tests/system/get-api-keys.rb | 41 ++++++++++---------- 6 files changed, 92 insertions(+), 52 deletions(-) diff --git a/tests/init.rb b/tests/init.rb index 5f3ef8a0..773866a6 100644 --- a/tests/init.rb +++ b/tests/init.rb @@ -56,11 +56,11 @@ require './system/edit-department.rb' require './system/delete-department.rb' require './staff/last-events.rb' # require './system/mail-templates.rb' -require './system/disable-registration.rb' -require './system/enable-registration.rb' require './system/add-api-key.rb' require './system/delete-api-key.rb' require './system/get-api-keys.rb' +require './system/disable-registration.rb' +require './system/enable-registration.rb' require './system/file-upload-download.rb' require './system/csv-import.rb' require './ticket/create-tag.rb' diff --git a/tests/scripts.rb b/tests/scripts.rb index 7c1e7bf0..c2020d7d 100644 --- a/tests/scripts.rb +++ b/tests/scripts.rb @@ -89,11 +89,12 @@ class Scripts result['data'] end - def self.createAPIKey(name) + def self.createAPIKey(name, type) request('/system/add-api-key', { csrf_userid: $csrf_userid, csrf_token: $csrf_token, - name: name + name: name, + type: type }) end diff --git a/tests/system/add-api-key.rb b/tests/system/add-api-key.rb index cf8c86d5..ecf2f431 100644 --- a/tests/system/add-api-key.rb +++ b/tests/system/add-api-key.rb @@ -1,30 +1,44 @@ describe'system/add-api-key' do - request('/user/logout') - Scripts.login($staff[:email], $staff[:password], true) + request('/user/logout') + Scripts.login($staff[:email], $staff[:password], true) - it 'should add API key' do - result= request('/system/add-api-key', { - csrf_userid: $csrf_userid, - csrf_token: $csrf_token, - name: 'new API' - }) + it 'should add API key' do + result= request('/system/add-api-key', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + name: 'new API', + type: 'REGISTRATION' + }) - (result['status']).should.equal('success') + (result['status']).should.equal('success') - row = $database.getRow('apikey', 1, 'id') + row = $database.getRow('apikey', 1, 'id') - (row['name']).should.equal('new API') - (result['data']).should.equal(row['token']) + (row['name']).should.equal('new API') + (result['data']).should.equal(row['token']) + end - end - it 'should not add API key' do - result= request('/system/add-api-key', { - csrf_userid: $csrf_userid, - csrf_token: $csrf_token, - name: 'new API' - }) + it 'should not add API key if name already used' do + result= request('/system/add-api-key', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + name: 'new API', + type: 'REGISTRATION' + }) - (result['status']).should.equal('fail') - (result['message']).should.equal('NAME_ALREADY_USED') - end + (result['status']).should.equal('fail') + (result['message']).should.equal('NAME_ALREADY_USED') + end + + it 'should not add API key if invalid type is used' do + result= request('/system/add-api-key', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + name: 'new API2', + type: 'REGISTRATON' + }) + + (result['status']).should.equal('fail') + (result['message']).should.equal('INVALID_API_KEY_TYPE') + end end diff --git a/tests/system/disable-registration.rb b/tests/system/disable-registration.rb index 13e4c06c..539a29dc 100644 --- a/tests/system/disable-registration.rb +++ b/tests/system/disable-registration.rb @@ -1,6 +1,7 @@ describe'/system/disable-registration' do request('/user/logout') Scripts.login($staff[:email], $staff[:password], true) + api_key = Scripts.createAPIKey('registrationKey', 'REGISTRATION')['data'] it 'should not disable registration if password is not correct' do result= request('/system/disable-registration', { @@ -17,7 +18,7 @@ describe'/system/disable-registration' do end it 'should disable registration' do - result= request('/system/disable-registration', { + result = request('/system/disable-registration', { csrf_userid: $csrf_userid, csrf_token: $csrf_token, password: $staff[:password] @@ -31,13 +32,23 @@ describe'/system/disable-registration' do end it 'should not create user in database if registration is false' do - response = request('/user/signup', { + result = request('/user/signup', { :name => 'ponzio', :email => 'jc@ponziolandia.com', :password => 'tequila' }) - (response['status']).should.equal('fail') + (result['status']).should.equal('fail') + (result['message']).should.equal('NO_PERMISSION') + end + it 'should create user if using api key' do + result = request('/user/signup', { + :name => 'ponzio', + :email => 'jc@ponziolandia.com', + :password => 'tequila', + :apiKey => api_key + }) + (result['status']).should.equal('success') end end diff --git a/tests/system/disable-user-system.rb b/tests/system/disable-user-system.rb index 11a6c33d..fa2185bb 100644 --- a/tests/system/disable-user-system.rb +++ b/tests/system/disable-user-system.rb @@ -17,7 +17,7 @@ describe'system/disable-user-system' do row = $database.getRow('user', 1, 'id') (row).should.equal(nil) - numberOftickets= $database.query("SELECT * FROM ticket WHERE author_id IS NULL AND author_email IS NOT NULL AND author_name IS NOT NULL") + numberOftickets = $database.query("SELECT * FROM ticket WHERE author_id IS NULL AND author_email IS NOT NULL AND author_name IS NOT NULL") (numberOftickets.num_rows).should.equal(51) @@ -99,6 +99,21 @@ describe'system/disable-user-system' do (ticket['author_staff_id']).should.equal('1') end + it 'should be able to create a ticket using api' do + api_key = Scripts.createAPIKey('ticketCreateKey', 'TICKET_CREATE')['data'] + request('/user/logout') + result = request('/ticket/create', { + email: 'fromapi@testemail.com', + name: 'Random user', + title: 'created by api', + content: 'this ticket was created using anapi key while user system is disabled', + departmentId: 1, + language: 'en', + apiKey: api_key + }) + (result['status']).should.equal('success') + end + it 'should not disable the user system if it is already disabled 'do request('/user/logout') Scripts.login($staff[:email], $staff[:password], true) @@ -127,7 +142,7 @@ describe'system/disable-user-system' do numberOftickets= $database.query("SELECT * FROM ticket WHERE author_email IS NULL AND author_name IS NULL AND author_id IS NOT NULL" ) - (numberOftickets.num_rows).should.equal(52) + (numberOftickets.num_rows).should.equal(53) end diff --git a/tests/system/get-api-keys.rb b/tests/system/get-api-keys.rb index 518dc818..e6c62900 100644 --- a/tests/system/get-api-keys.rb +++ b/tests/system/get-api-keys.rb @@ -1,26 +1,25 @@ describe'system/get-api-keys' do - request('/user/logout') - Scripts.login($staff[:email], $staff[:password], true) + request('/user/logout') + Scripts.login($staff[:email], $staff[:password], true) - it 'should get all API keys' do - Scripts.createAPIKey('namekey1') - Scripts.createAPIKey('namekey2') - Scripts.createAPIKey('namekey3') - Scripts.createAPIKey('namekey4') - Scripts.createAPIKey('namekey5') - - result = request('/system/get-api-keys', { - csrf_userid: $csrf_userid, - csrf_token: $csrf_token, - }) + it 'should get all API keys' do + Scripts.createAPIKey('namekey1', 'REGISTRATION') + Scripts.createAPIKey('namekey2', 'REGISTRATION') + Scripts.createAPIKey('namekey3', 'REGISTRATION') + Scripts.createAPIKey('namekey4', 'REGISTRATION') + Scripts.createAPIKey('namekey5', 'REGISTRATION') + + result = request('/system/get-api-keys', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + }) - (result['status']).should.equal('success') - (result['data'][0]['name']).should.equal('namekey1') - (result['data'][1]['name']).should.equal('namekey2') - (result['data'][2]['name']).should.equal('namekey3') - (result['data'][3]['name']).should.equal('namekey4') - (result['data'][4]['name']).should.equal('namekey5') - - end + (result['status']).should.equal('success') + (result['data'][0]['name']).should.equal('namekey1') + (result['data'][1]['name']).should.equal('namekey2') + (result['data'][2]['name']).should.equal('namekey3') + (result['data'][3]['name']).should.equal('namekey4') + (result['data'][4]['name']).should.equal('namekey5') + end end From e8dab6922b4a98f1cd2deaff6dc1d2d36bb81e46 Mon Sep 17 00:00:00 2001 From: Ivan Diaz Date: Sat, 16 Nov 2019 17:21:38 -0300 Subject: [PATCH 05/37] Fix frontend to only use registration api keys --- .../app/admin/panel/settings/admin-panel-advanced-settings.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/app/admin/panel/settings/admin-panel-advanced-settings.js b/client/src/app/admin/panel/settings/admin-panel-advanced-settings.js index 39f769e0..0855e6e7 100644 --- a/client/src/app/admin/panel/settings/admin-panel-advanced-settings.js +++ b/client/src/app/admin/panel/settings/admin-panel-advanced-settings.js @@ -153,7 +153,7 @@ class AdminPanelAdvancedSettings extends React.Component { ModalContainer.closeModal(); API.call({ path: '/system/add-api-key', - data: {name} + data: {name, type: 'REGISTRATION'} }).then(this.getAllKeys.bind(this)); } @@ -177,7 +177,7 @@ class AdminPanelAdvancedSettings extends React.Component { onRetrieveSuccess(result) { this.setState({ - APIKeys: result.data, + APIKeys: result.data.filter(key => key['type'] === 'REGISTRATION'), selectedAPIKey: -1 }); } From 5219b91388bb8ae8b31008523dddbb216d7fe05a Mon Sep 17 00:00:00 2001 From: Guillermo Date: Tue, 19 Nov 2019 19:55:47 -0300 Subject: [PATCH 06/37] add valid departments to staff search --- server/controllers/staff/get-all-tickets.php | 1 - server/controllers/ticket/search.php | 113 +++++++++++++----- .../libs/validations/validDepartmentsId.php | 2 +- 3 files changed, 83 insertions(+), 33 deletions(-) diff --git a/server/controllers/staff/get-all-tickets.php b/server/controllers/staff/get-all-tickets.php index 93a30975..f271acf2 100755 --- a/server/controllers/staff/get-all-tickets.php +++ b/server/controllers/staff/get-all-tickets.php @@ -50,7 +50,6 @@ class GetAllTicketsStaffController extends Controller { ]); return; } - Response::respondSuccess([ 'tickets' => $this->getTicketList()->toArray(true), 'pages' => $this->getTotalPages() diff --git a/server/controllers/ticket/search.php b/server/controllers/ticket/search.php index 0f9f9f1e..a53c7334 100644 --- a/server/controllers/ticket/search.php +++ b/server/controllers/ticket/search.php @@ -52,7 +52,7 @@ class SearchController extends Controller { public function validations() { return [ - 'permission' => 'any', + 'permission' => 'staff_1', 'requestData' => [ 'page' => [ 'validation' => DataValidator::oneOf(DataValidator::numeric()->positive(),DataValidator::nullType()), @@ -110,25 +110,38 @@ class SearchController extends Controller { 'assigned' => Controller::request('assigned'), 'query' => Controller::request('query'), 'orderBy' => json_decode(Controller::request('orderBy'),true), - 'page' => Controller::request('page') + 'page' => Controller::request('page'), + 'user' => Controller::getLoggedUser(), ]; + $query = $this->getSQLQuery($inputs); $queryWithOrder = $this->getSQLQueryWithOrder($inputs); - throw new Exception("SELECT COUNT(*) FROM (SELECT COUNT(*) " . $query . " ) AS T2"); $totalCount = RedBean::getAll("SELECT COUNT(*) FROM (SELECT COUNT(*) " . $query . " ) AS T2")[0]['COUNT(*)']; $ticketList = RedBean::getAll($queryWithOrder); - Response::respondSuccess([ - 'tickets' => $ticketList, - 'pages' => ceil($totalCount / 10), - 'page' => Controller::request('page') - ]); + $ticketTableExists = RedBean::exec("select table_name from information_schema.tables where table_name = 'ticket';"); + + if($ticketTableExists){ + Response::respondSuccess([ + 'tickets' => $ticketList, + 'pages' => ceil($totalCount / 10), + 'page' => $inputs['page'] ? $inputs['page'] : 1 + ]); + }else{ + Response::respondSuccess([]); + } } public function getSQLQuery($inputs) { - $query = "FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id)"; + $tagsTableExists = RedBean::exec("select table_name from information_schema.tables where table_name = 'tag_ticket';"); + $ticketEventTableExists = RedBean::exec("select table_name from information_schema.tables where table_name = 'ticketevent';"); + + $taglistQuery = ( $tagsTableExists ? " LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id" : ''); + $ticketeventlistQuery = ( $ticketEventTableExists ? " LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id" : ''); + + $query = "FROM (ticket" . $taglistQuery . $ticketeventlistQuery .")"; $filters = ""; $this->setQueryFilters($inputs, $filters); $query .= $filters . " GROUP BY ticket.id"; @@ -138,7 +151,7 @@ class SearchController extends Controller { public function getSQLQueryWithOrder($inputs) { $query = $this->getSQLQuery($inputs); $order = ""; - $query = "SELECT ticket.id,ticket.title,ticket.ticket_number,ticket.content ,ticketevent.content " . $query; + $query = "SELECT ticket.id,ticket.title,ticket.ticket_number, ticket.priority ,ticket.department_id, ticket.author_id , ticket.date " . $query; $this->setQueryOrder($inputs, $order); $inputs['page'] ? $page = $inputs['page'] : $page = 1 ; @@ -154,14 +167,16 @@ class SearchController extends Controller { if(array_key_exists('unreadStaff',$inputs)) $this->setSeenFilter($inputs['unreadStaff'], $filters); if(array_key_exists('priority',$inputs)) $this->setPriorityFilter($inputs['priority'], $filters); if(array_key_exists('dateRange',$inputs)) $this->setDateFilter($inputs['dateRange'], $filters); - if(array_key_exists('departments',$inputs)) $this->setDepartmentFilter($inputs['departments'], $filters); + if(array_key_exists('departments',$inputs)) $this->setDepartmentFilter($inputs['departments'],$inputs['user'], $filters); if(array_key_exists('authors',$inputs)) $this->setAuthorFilter($inputs['authors'], $filters); if(array_key_exists('query',$inputs)) $this->setStringFilter($inputs['query'], $filters); if($filters != "") $filters = " WHERE " . $filters; } private function setTagFilter($tagList, &$filters){ - if($tagList){ + $tagsTableExists = RedBean::exec("select table_name from information_schema.tables where table_name = 'tag_ticket';"); + + if($tagList && $tagsTableExists){ $filters != "" ? $filters .= " and " : null; foreach($tagList as $key => $tag) { @@ -188,11 +203,16 @@ class SearchController extends Controller { } private function setPriorityFilter($prioritys, &$filters){ if($prioritys != null){ + $first = TRUE; if ($filters != "") $filters .= " and "; - foreach(array_unique($prioritys) as $key => $priority) { + foreach(array_unique($prioritys) as $priority) { - $key == 0 ? $filters .= " ( " : null; - ($key != 0 && $key != sizeof($prioritys)) ? $filters .= " or " : null; + if($first){ + $filters .= " ( "; + $first = FALSE; + } else { + $filters .= " or "; + } if($priority == 0){ $filters .= "ticket.priority = " . "'low'"; @@ -202,10 +222,11 @@ class SearchController extends Controller { $filters .= "ticket.priority = " . "'high'"; } - $key == sizeof($prioritys) ? $filters .= " ) " : null ; + } $prioritys != "" ? $filters .= ") " : null; } + } private function setDateFilter($dateRange, &$filters){ @@ -218,30 +239,38 @@ class SearchController extends Controller { } } - private function setDepartmentFilter($departments, &$filters){ - if($departments != null){ - if ($filters != "") $filters .= " and "; + private function setDepartmentFilter($departments,$user, &$filters){ - foreach($departments as $key => $department) { + $validDepartments = $this->generateValidDepartmentList($departments, $user); + if ($filters != "") $filters .= " and "; + $first = TRUE; - $key == 0 ? $filters .= " ( " : null; - ($key != 0 && $key != sizeof($departments)) ? $filters .= " or " : null; - - $filters .= "ticket.department_id = " . $department ; + foreach($validDepartments as $department) { + if($first){ + $filters .= " ( "; + $first = FALSE; + } else { + $filters .= " or "; } - $filters .= ")"; + $filters .= "ticket.department_id = " . $department; } + $filters .= ")"; + } private function setAuthorFilter($authors, &$filters){ if($authors != null){ - + $first = TRUE; if ($filters != "") $filters .= " and "; - foreach($authors as $key => $author){ + foreach($authors as $author){ - $key == 0 ? $filters .= " ( " : null; - ($key != 0 && $key != sizeof($authors)) ? $filters .= " or " : null; + if($first){ + $filters .= " ( "; + $first = FALSE; + } else { + $filters .= " or "; + } if($author['staff']){ $filters .= "ticket.author_staff_id = " . $author['id']; @@ -265,12 +294,31 @@ class SearchController extends Controller { } private function setStringFilter($search, &$filters){ + $ticketEventTableExists = RedBean::exec("select table_name from information_schema.tables where table_name = 'ticketevent';"); + if($search != null){ if ($filters != "") $filters .= " and "; - $filters .= " (ticket.title LIKE '%" . $search . "%' or ticket.content LIKE '%" . $search . "%' or ticket.ticket_number LIKE '%" . $search . "%' or (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%" . $search ."%'))"; + $ticketevent = ( $ticketEventTableExists ? " or (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%" . $search ."%')" : ""); + $filters .= " (ticket.title LIKE '%" . $search . "%' or ticket.content LIKE '%" . $search . "%' or ticket.ticket_number LIKE '%" . $search . "%'". $ticketevent ." )"; }; } + private function generateValidDepartmentList($departments, $user){ + $result = []; + $managedDepartments = []; + if($departments == null) $departments = []; + foreach ($user->sharedDepartmentList->toArray() as $department) { + array_push($managedDepartments,$department['id']); + } + $result = array_intersect($departments,$managedDepartments); + + if(empty($result)) $result = $managedDepartments; + + $result = array_unique($result); + + return $result; + } + //ORDER private function setQueryOrder($inputs, &$order){ $order = " ORDER BY "; @@ -285,8 +333,11 @@ class SearchController extends Controller { }; } private function setStringOrder($querysearch, &$order){ + $ticketEventTableExists = RedBean::exec("select table_name from information_schema.tables where table_name = 'ticketevent';"); + if($querysearch != null){ - $order .= "CASE WHEN (ticket.ticket_number LIKE '%" . $querysearch ."%') THEN ticket.ticket_number END desc,CASE WHEN (ticket.title LIKE '%" . $querysearch ."%') THEN ticket.title END desc, CASE WHEN ( ticket.content LIKE '%" . $querysearch ."%') THEN ticket.content END desc, CASE WHEN (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%".$querysearch."%') THEN ticketevent.content END desc," ; + $ticketeventOrder = ( $ticketEventTableExists ? " CASE WHEN (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%".$querysearch."%') THEN ticketevent.content END desc," : ""); + $order .= "CASE WHEN (ticket.ticket_number LIKE '%" . $querysearch ."%') THEN ticket.ticket_number END desc,CASE WHEN (ticket.title LIKE '%" . $querysearch ."%') THEN ticket.title END desc, CASE WHEN ( ticket.content LIKE '%" . $querysearch ."%') THEN ticket.content END desc," . $ticketeventOrder ; } } diff --git a/server/libs/validations/validDepartmentsId.php b/server/libs/validations/validDepartmentsId.php index 44fa53f3..36c5b927 100644 --- a/server/libs/validations/validDepartmentsId.php +++ b/server/libs/validations/validDepartmentsId.php @@ -17,4 +17,4 @@ class ValidDepartmentsId extends AbstractRule { } return false; } -} \ No newline at end of file +} From 2678947f85e85a03c26587de29b03065b1cc8604 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Tue, 17 Dec 2019 08:35:04 -0300 Subject: [PATCH 07/37] ticket search list frontend --- client/src/app-components/ticket-list.js | 2 +- .../src/app-components/ticket-query-list.js | 84 ++++++ client/src/app/Routes.js | 2 + .../tickets/admin-panel-search-tickets.js | 32 +++ client/src/core-components/menu.js | 2 +- client/src/core-components/menu.scss | 2 +- client/src/core-components/pagination.js | 2 +- client/src/core-components/table.js | 12 +- client/src/data/languages/en.js | 2 + server/controllers/staff/get-all-tickets.php | 1 + server/controllers/ticket/search.php | 14 +- .../tests/controllers/ticket/searchTest.php | 258 ++++++++++++++++++ tests/init.rb | 3 +- 13 files changed, 400 insertions(+), 16 deletions(-) create mode 100644 client/src/app-components/ticket-query-list.js create mode 100644 client/src/app/admin/panel/tickets/admin-panel-search-tickets.js create mode 100644 server/tests/controllers/ticket/searchTest.php diff --git a/client/src/app-components/ticket-list.js b/client/src/app-components/ticket-list.js index d23ecf0f..6f85dc43 100644 --- a/client/src/app-components/ticket-list.js +++ b/client/src/app-components/ticket-list.js @@ -274,4 +274,4 @@ export default connect((store) => { return { tags: store.config['tags'] }; -})(TicketList); \ No newline at end of file +})(TicketList); diff --git a/client/src/app-components/ticket-query-list.js b/client/src/app-components/ticket-query-list.js new file mode 100644 index 00000000..a5c7edbc --- /dev/null +++ b/client/src/app-components/ticket-query-list.js @@ -0,0 +1,84 @@ +import React from 'react'; +import _ from 'lodash'; + +import API from 'lib-app/api-call'; +import i18n from 'lib-app/i18n'; +import {connect} from 'react-redux'; + +import TicketList from 'app-components/ticket-list'; +import Message from 'core-components/message'; + +class TicketQueryList extends React.Component { + + state = { + tickets: [], + page: 1, + pages: 0, + error: null, + loading: true + }; + + componentDidMount() { + this.getTickets(); + } + + render() { + return ( +
+ {(this.state.error) ? {i18n('ERROR_RETRIEVING_TICKETS')} : } +
+ ); + } + + getTickets() { + this.setState({ + loading:true + }) + API.call({ + path: '/ticket/search', + data: { + page : this.state.page, + ...this.props.filters + } + }).then((result) => { + this.setState({ + tickets: result.data.tickets, + page: result.data.page, + pages: result.data.pages, + error: null, + loading: false + }) + }).catch((result) => this.setState({ + loading: false, + error: result.message + })); + + } + + onPageChange(event) { + this.setState({page: event.target.value}, () => this.getTickets()); + } + + getTicketListProps () { + const {page,pages,loading,tickets} = this.state; + return { + userId: this.props.userId, + ticketPath: '/admin/panel/tickets/view-ticket/', + tickets, + page, + pages, + loading, + type: 'secondary', + showDepartmentDropdown: false, + closedTicketsShown: false, + onPageChange:this.onPageChange.bind(this) + }; + } + +} + +export default connect((store) => { + return { + userId: store.session.userId + }; +})(TicketQueryList); diff --git a/client/src/app/Routes.js b/client/src/app/Routes.js index 0419c0db..b9d43065 100644 --- a/client/src/app/Routes.js +++ b/client/src/app/Routes.js @@ -33,6 +33,7 @@ import AdminPanelMyAccount from 'app/admin/panel/dashboard/admin-panel-my-accoun import AdminPanelMyTickets from 'app/admin/panel/tickets/admin-panel-my-tickets'; import AdminPanelNewTickets from 'app/admin/panel/tickets/admin-panel-new-tickets'; import AdminPanelAllTickets from 'app/admin/panel/tickets/admin-panel-all-tickets'; +import AdminPanelSearchTickets from 'app/admin/panel/tickets/admin-panel-search-tickets'; import AdminPanelViewTicket from 'app/admin/panel/tickets/admin-panel-view-ticket'; import AdminPanelCustomResponses from 'app/admin/panel/tickets/admin-panel-custom-responses'; @@ -113,6 +114,7 @@ export default ( + diff --git a/client/src/app/admin/panel/tickets/admin-panel-search-tickets.js b/client/src/app/admin/panel/tickets/admin-panel-search-tickets.js new file mode 100644 index 00000000..b9f4842d --- /dev/null +++ b/client/src/app/admin/panel/tickets/admin-panel-search-tickets.js @@ -0,0 +1,32 @@ +import React from 'react'; +import {connect} from 'react-redux'; + +import i18n from 'lib-app/i18n'; + +import TicketQueryList from 'app-components/ticket-query-list'; + +import Header from 'core-components/header'; +import Message from 'core-components/message'; + +class AdminPanelSearchTickets extends React.Component { + + render() { + return ( +
+
+ {(this.props.error) ? {i18n('ERROR_RETRIEVING_TICKETS')} : } +
+ ); + } + + getFilters() { + return { + }; + } +} + +export default connect((store) => { + return { + error: store.adminData.allTicketsError + }; +})(AdminPanelSearchTickets); diff --git a/client/src/core-components/menu.js b/client/src/core-components/menu.js index 25cd805d..40ef6de2 100644 --- a/client/src/core-components/menu.js +++ b/client/src/core-components/menu.js @@ -140,4 +140,4 @@ class Menu extends React.Component { } } -export default Menu; \ No newline at end of file +export default Menu; diff --git a/client/src/core-components/menu.scss b/client/src/core-components/menu.scss index 64eab16c..d8b06c20 100644 --- a/client/src/core-components/menu.scss +++ b/client/src/core-components/menu.scss @@ -201,4 +201,4 @@ $transition: background-color 0.3s ease, color 0.3s ease; color: white; } } -} \ No newline at end of file +} diff --git a/client/src/core-components/pagination.js b/client/src/core-components/pagination.js index 4b4ad162..8ce28a8d 100644 --- a/client/src/core-components/pagination.js +++ b/client/src/core-components/pagination.js @@ -98,4 +98,4 @@ class Pagination extends React.Component { } } -export default Pagination; \ No newline at end of file +export default Pagination; diff --git a/client/src/core-components/table.js b/client/src/core-components/table.js index fe882f72..07c9f29f 100644 --- a/client/src/core-components/table.js +++ b/client/src/core-components/table.js @@ -55,7 +55,7 @@ class Table extends React.Component { 'table__header-column': true, [header.className]: (header.className) }; - + return ( {header.value} @@ -97,7 +97,7 @@ class Table extends React.Component { }; return ( - + {row[key]} ); } @@ -139,15 +139,15 @@ class Table extends React.Component { this.props.onPageChange({target: {value: index}}); } } - + getRowClass(row) { let classes = { 'table__row': true, 'table__row-highlighted': row.highlighted }; - + classes[row.className] = (row.className); - + return classNames(classes); } @@ -167,4 +167,4 @@ class Table extends React.Component { } } -export default Table; \ No newline at end of file +export default Table; diff --git a/client/src/data/languages/en.js b/client/src/data/languages/en.js index 6373f738..b29c60d1 100644 --- a/client/src/data/languages/en.js +++ b/client/src/data/languages/en.js @@ -93,6 +93,7 @@ export default { 'CHANGE_EMAIL': 'Change email', 'CHANGE_PASSWORD': 'Change password', 'NAME': 'Name', + 'SEARCH': 'Search', 'SIGNUP_DATE': 'Sign up date', 'SEARCH_USERS': 'Search users...', 'SEARCH_EMAIL': 'Search email...', @@ -293,6 +294,7 @@ export default { 'MY_TICKETS_DESCRIPTION': 'Here you can view the tickets you are responsible for.', 'NEW_TICKETS_DESCRIPTION': 'Here you can view all the new tickets that are not assigned by anyone.', 'ALL_TICKETS_DESCRIPTION': 'Here you can view the tickets of the departments you are assigned.', + 'SEARCH_TICKETS_DESCRIPTION': 'Here you can search tickets by specifics filters.', 'TICKET_VIEW_DESCRIPTION': 'This ticket has been sent by a customer. Here you can respond or assign the ticket', 'BAN_USERS_DESCRIPTION': 'Here you can see a list of banned emails, you can un-ban them or add more emails to the list.', 'LIST_USERS_DESCRIPTION': 'This is the list of users that are registered in this platform. You can search for someone in particular, delete it or ban it.', diff --git a/server/controllers/staff/get-all-tickets.php b/server/controllers/staff/get-all-tickets.php index f271acf2..a1530e8f 100755 --- a/server/controllers/staff/get-all-tickets.php +++ b/server/controllers/staff/get-all-tickets.php @@ -50,6 +50,7 @@ class GetAllTicketsStaffController extends Controller { ]); return; } + Response::respondSuccess([ 'tickets' => $this->getTicketList()->toArray(true), 'pages' => $this->getTotalPages() diff --git a/server/controllers/ticket/search.php b/server/controllers/ticket/search.php index a53c7334..62b49ada 100644 --- a/server/controllers/ticket/search.php +++ b/server/controllers/ticket/search.php @@ -116,17 +116,21 @@ class SearchController extends Controller { $query = $this->getSQLQuery($inputs); $queryWithOrder = $this->getSQLQueryWithOrder($inputs); - $totalCount = RedBean::getAll("SELECT COUNT(*) FROM (SELECT COUNT(*) " . $query . " ) AS T2")[0]['COUNT(*)']; - $ticketList = RedBean::getAll($queryWithOrder); + $ticketIdList = RedBean::getAll($queryWithOrder); + $ticketList = []; + + foreach ($ticketIdList as $item) { + $ticket = Ticket::getDataStore($item['id']); + array_push($ticketList, $ticket->toArray()); + } $ticketTableExists = RedBean::exec("select table_name from information_schema.tables where table_name = 'ticket';"); - if($ticketTableExists){ Response::respondSuccess([ 'tickets' => $ticketList, 'pages' => ceil($totalCount / 10), - 'page' => $inputs['page'] ? $inputs['page'] : 1 + 'page' => $inputs['page'] ? ($inputs['page']*1) : 1 ]); }else{ Response::respondSuccess([]); @@ -151,7 +155,7 @@ class SearchController extends Controller { public function getSQLQueryWithOrder($inputs) { $query = $this->getSQLQuery($inputs); $order = ""; - $query = "SELECT ticket.id,ticket.title,ticket.ticket_number, ticket.priority ,ticket.department_id, ticket.author_id , ticket.date " . $query; + $query = "SELECT" . " ticket.id " . $query; $this->setQueryOrder($inputs, $order); $inputs['page'] ? $page = $inputs['page'] : $page = 1 ; diff --git a/server/tests/controllers/ticket/searchTest.php b/server/tests/controllers/ticket/searchTest.php new file mode 100644 index 00000000..b7f72f46 --- /dev/null +++ b/server/tests/controllers/ticket/searchTest.php @@ -0,0 +1,258 @@ + \Mock::stub()->returns(1) + ]); + Controller::$requestReturnMock = null; + $_SERVER['REMOTE_ADDR'] = 'MOCK_REMOTE'; + $this->searchController = new SearchController(); + } + + public function testTagsFilter() { + $this->assertEquals( + $this->searchController->getSQLQuery([ + 'tags' => [] + ]), + 'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) GROUP BY ticket.id' + ); + + $this->assertEquals( + $this->searchController->getSQLQuery([ + 'tags' => [0] + ]), + 'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE ( tag_ticket.tag_id = 0) GROUP BY ticket.id' + ); + + $this->assertEquals( + $this->searchController->getSQLQuery([ + 'tags' => [0,1,2] + ]), + 'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE ( tag_ticket.tag_id = 0 or tag_ticket.tag_id = 1 or tag_ticket.tag_id = 2) GROUP BY ticket.id' + ); + } + + public function testClosedFilter() { + + $this->assertEquals( + $this->searchController->getSQLQuery([ + 'closed'=> null + ]), + 'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) GROUP BY ticket.id' + ); + + + $this->assertEquals( + $this->searchController->getSQLQuery([ + 'closed'=> 1 + ]), + 'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE ticket.closed = 1 GROUP BY ticket.id' + ); + + $this->assertEquals( + $this->searchController->getSQLQuery([ + 'closed'=> '0' + ]), + 'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE ticket.closed = 0 GROUP BY ticket.id' + ); + } + public function testAssignedFilter(){ + + $this->assertEquals( + $this->searchController->getSQLQuery([ + 'assigned'=> null + ]), + 'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) GROUP BY ticket.id' + ); + + $this->assertEquals( + $this->searchController->getSQLQuery([ + 'assigned'=> '0' + ]), + 'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE ticket.owner_id IS NULL GROUP BY ticket.id' + ); + + $this->assertEquals( + $this->searchController->getSQLQuery([ + 'assigned'=> 1 + ]), + 'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE ticket.owner_id IS NOT NULL GROUP BY ticket.id' + ); + } + public function testUnreadStaffFilter() { + $this->assertEquals( + $this->searchController->getSQLQuery([ + 'unreadStaff' => null + ]), + 'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) GROUP BY ticket.id' + ); + + $this->assertEquals( + $this->searchController->getSQLQuery([ + 'unreadStaff' => '0' + ]), + 'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE ticket.unread_staff = 0 GROUP BY ticket.id' + ); + + $this->assertEquals( + $this->searchController->getSQLQuery([ + 'unreadStaff' => 1 + ]), + 'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE ticket.unread_staff = 1 GROUP BY ticket.id' + ); + } + + public function testPriorityFilter() { + + $this->assertEquals( + $this->searchController->getSQLQuery([ + 'tags' => [] + ]), + 'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) GROUP BY ticket.id' + ); + + $this->assertEquals( + $this->searchController->getSQLQuery([ + 'tags' => [1] + ]), + 'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE ( tag_ticket.tag_id = 1) GROUP BY ticket.id' + ); + + $this->assertEquals( + $this->searchController->getSQLQuery([ + 'tags' => [2,3] + ]), + 'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE ( tag_ticket.tag_id = 2 or tag_ticket.tag_id = 3) GROUP BY ticket.id' + ); + + $this->assertEquals( + $this->searchController->getSQLQuery([ + 'tags' => [1,2,3] + ]), + 'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE ( tag_ticket.tag_id = 1 or tag_ticket.tag_id = 2 or tag_ticket.tag_id = 3) GROUP BY ticket.id' + ); + } + + public function testdateRangeFilter() { + $this->assertEquals( + $this->searchController->getSQLQuery([ + 'dateRange' => null + ]), + 'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) GROUP BY ticket.id' + ); + + $this->assertEquals( + $this->searchController->getSQLQuery([ + 'dateRange' => [1,2] + ]), + 'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE (ticket.date >= 1 and ticket.date <= 2) GROUP BY ticket.id' + ); + } + /* + public function testDepartmentsFilter() { + $this->assertEquals( + $this->searchController->getSQLQuery([ + 'departments' => null + ]), + 'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) GROUP BY ticket.id' + ); + + $this->assertEquals( + $this->searchController->getSQLQuery([ + 'departments' => [1] + ]), + 'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE ( ticket.department_id = 1) GROUP BY ticket.id' + ); + + $this->assertEquals( + $this->searchController->getSQLQuery([ + 'departments' => [1,2,3] + ]), + 'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE ( ticket.department_id = 1 or ticket.department_id = 2 or ticket.department_id = 3) GROUP BY ticket.id' + ); + } + */ + public function testAuthorsFilter() { + $this->assertEquals( + $this->searchController->getSQLQuery([ + 'authors' => null + ]), + 'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) GROUP BY ticket.id' + ); + $this->assertEquals( + $this->searchController->getSQLQuery([ + 'authors' => [ + [ + 'id' => 1, + 'staff' => 1 + ], + [ + 'id' => 2, + 'staff' => 0 + ] + ] + ]), + 'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE ( ticket.author_staff_id = 1 or ticket.author_id = 2) GROUP BY ticket.id' + ); + } + + public function testQueryFilter() { + $this->assertEquals( + $this->searchController->getSQLQuery([ + 'query' => null + ]), + 'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) GROUP BY ticket.id' + ); + + $this->assertEquals( + $this->searchController->getSQLQuery([ + 'query' => 'hello world' + ]), + "FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE (ticket.title LIKE '%hello world%' or ticket.content LIKE '%hello world%' or ticket.ticket_number LIKE '%hello world%' or (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%hello world%') ) GROUP BY ticket.id" + ); + } + public function testQueryWithOrder() { + $this->assertEquals( + $this->searchController->getSQLQueryWithOrder([ + 'page' => 1 + ]), + "SELECT ticket.id,ticket.title,ticket.ticket_number, ticket.priority ,ticket.department_id, ticket.author_id , ticket.date FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) GROUP BY ticket.id ORDER BY ticket.closed asc, ticket.owner_id asc, ticket.unread_staff asc, ticket.priority desc, ticket.date desc LIMIT 10 OFFSET 0" + ); + + $this->assertEquals( + $this->searchController->getSQLQueryWithOrder([ + 'page' => 1, + 'query' => 'stark' + ]), + "SELECT ticket.id,ticket.title,ticket.ticket_number, ticket.priority ,ticket.department_id, ticket.author_id , ticket.date FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE (ticket.title LIKE '%stark%' or ticket.content LIKE '%stark%' or ticket.ticket_number LIKE '%stark%' or (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%stark%') ) GROUP BY ticket.id ORDER BY CASE WHEN (ticket.ticket_number LIKE '%stark%') THEN ticket.ticket_number END desc,CASE WHEN (ticket.title LIKE '%stark%') THEN ticket.title END desc, CASE WHEN ( ticket.content LIKE '%stark%') THEN ticket.content END desc, CASE WHEN (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%stark%') THEN ticketevent.content END desc,ticket.closed asc, ticket.owner_id asc, ticket.unread_staff asc, ticket.priority desc, ticket.date desc LIMIT 10 OFFSET 0" + ); + + $this->assertEquals( + $this->searchController->getSQLQueryWithOrder([ + 'page' => 1, + 'orderBy' => ['value' => 'closed', 'asc' => 1] + ]), + "SELECT ticket.id,ticket.title,ticket.ticket_number, ticket.priority ,ticket.department_id, ticket.author_id , ticket.date FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) GROUP BY ticket.id ORDER BY ticket.closed asc,ticket.closed asc, ticket.owner_id asc, ticket.unread_staff asc, ticket.priority desc, ticket.date desc LIMIT 10 OFFSET 0" + ); + } +} diff --git a/tests/init.rb b/tests/init.rb index 5f3ef8a0..73651ad3 100644 --- a/tests/init.rb +++ b/tests/init.rb @@ -22,10 +22,11 @@ require './user/send-recover-password.rb' require './user/recover-password.rb' require './user/edit-password.rb' require './user/edit-email.rb' +#require './ticket/search.rb' require './user/get.rb' require './user/enable-disable.rb' -require './ticket/create.rb' require './ticket/seen.rb' +require './ticket/create.rb' require './ticket/comment.rb' require './ticket/get.rb' require './ticket/custom-response.rb' From bac138757eeca1dd566ffeb4df16ab9e73dbe3ff Mon Sep 17 00:00:00 2001 From: Guillermo Date: Tue, 17 Dec 2019 09:04:49 -0300 Subject: [PATCH 08/37] fix search php test --- server/tests/controllers/ticket/searchTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/tests/controllers/ticket/searchTest.php b/server/tests/controllers/ticket/searchTest.php index b7f72f46..ad08fb5f 100644 --- a/server/tests/controllers/ticket/searchTest.php +++ b/server/tests/controllers/ticket/searchTest.php @@ -236,7 +236,7 @@ class SearchControllerTest extends TestCase { $this->searchController->getSQLQueryWithOrder([ 'page' => 1 ]), - "SELECT ticket.id,ticket.title,ticket.ticket_number, ticket.priority ,ticket.department_id, ticket.author_id , ticket.date FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) GROUP BY ticket.id ORDER BY ticket.closed asc, ticket.owner_id asc, ticket.unread_staff asc, ticket.priority desc, ticket.date desc LIMIT 10 OFFSET 0" + "SELECT ticket.id FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) GROUP BY ticket.id ORDER BY ticket.closed asc, ticket.owner_id asc, ticket.unread_staff asc, ticket.priority desc, ticket.date desc LIMIT 10 OFFSET 0" ); $this->assertEquals( @@ -244,7 +244,7 @@ class SearchControllerTest extends TestCase { 'page' => 1, 'query' => 'stark' ]), - "SELECT ticket.id,ticket.title,ticket.ticket_number, ticket.priority ,ticket.department_id, ticket.author_id , ticket.date FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE (ticket.title LIKE '%stark%' or ticket.content LIKE '%stark%' or ticket.ticket_number LIKE '%stark%' or (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%stark%') ) GROUP BY ticket.id ORDER BY CASE WHEN (ticket.ticket_number LIKE '%stark%') THEN ticket.ticket_number END desc,CASE WHEN (ticket.title LIKE '%stark%') THEN ticket.title END desc, CASE WHEN ( ticket.content LIKE '%stark%') THEN ticket.content END desc, CASE WHEN (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%stark%') THEN ticketevent.content END desc,ticket.closed asc, ticket.owner_id asc, ticket.unread_staff asc, ticket.priority desc, ticket.date desc LIMIT 10 OFFSET 0" + "SELECT ticket.id FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE (ticket.title LIKE '%stark%' or ticket.content LIKE '%stark%' or ticket.ticket_number LIKE '%stark%' or (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%stark%') ) GROUP BY ticket.id ORDER BY CASE WHEN (ticket.ticket_number LIKE '%stark%') THEN ticket.ticket_number END desc,CASE WHEN (ticket.title LIKE '%stark%') THEN ticket.title END desc, CASE WHEN ( ticket.content LIKE '%stark%') THEN ticket.content END desc, CASE WHEN (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%stark%') THEN ticketevent.content END desc,ticket.closed asc, ticket.owner_id asc, ticket.unread_staff asc, ticket.priority desc, ticket.date desc LIMIT 10 OFFSET 0" ); $this->assertEquals( @@ -252,7 +252,7 @@ class SearchControllerTest extends TestCase { 'page' => 1, 'orderBy' => ['value' => 'closed', 'asc' => 1] ]), - "SELECT ticket.id,ticket.title,ticket.ticket_number, ticket.priority ,ticket.department_id, ticket.author_id , ticket.date FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) GROUP BY ticket.id ORDER BY ticket.closed asc,ticket.closed asc, ticket.owner_id asc, ticket.unread_staff asc, ticket.priority desc, ticket.date desc LIMIT 10 OFFSET 0" + "SELECT ticket.id FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) GROUP BY ticket.id ORDER BY ticket.closed asc,ticket.closed asc, ticket.owner_id asc, ticket.unread_staff asc, ticket.priority desc, ticket.date desc LIMIT 10 OFFSET 0" ); } } From 58be6127fba8bf8d46be9706e34accbee2beb0b3 Mon Sep 17 00:00:00 2001 From: Guillermo Giuliana Date: Tue, 17 Dec 2019 09:15:52 -0300 Subject: [PATCH 09/37] Delete search.rb --- tests/ticket/search.rb | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 tests/ticket/search.rb diff --git a/tests/ticket/search.rb b/tests/ticket/search.rb deleted file mode 100644 index c1c4d53f..00000000 --- a/tests/ticket/search.rb +++ /dev/null @@ -1,22 +0,0 @@ -describe '/user/get' do - request('/user/logout') - - - #Scripts.createUser('user_get@os4.com', 'user_get','User Get') - - #Scripts.login('user_get@os4.com', 'user_get') - #result = request('/ticket/create', { - title: 'Should we pay?', - content: 'A Lannister always pays his debts.', - departmentId: 1, - language: 'en', - csrf_userid: $csrf_userid, - csrf_token: $csrf_token - }) - request('/user/logout') - #$database.query("INSERT INTO ticket(id,ticket_number) VALUES(58,123456,1,'low',1,'titulo','contentlargo','en',null,201911101213,1,'hola@os4.com','nameuatuhor',1,1,1,null);") - - - #@ticketNumber = result['data']['ticketNumber'] - request('/user/logout') -end From 2099af5f3a2eff4ef1b6cb169f917232206d88cc Mon Sep 17 00:00:00 2001 From: Guillermo Giuliana Date: Tue, 17 Dec 2019 10:15:19 -0300 Subject: [PATCH 10/37] Update LinearCongruentialGeneratorTest.php --- server/tests/libs/LinearCongruentialGeneratorTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/server/tests/libs/LinearCongruentialGeneratorTest.php b/server/tests/libs/LinearCongruentialGeneratorTest.php index b716612d..5d5f671f 100644 --- a/server/tests/libs/LinearCongruentialGeneratorTest.php +++ b/server/tests/libs/LinearCongruentialGeneratorTest.php @@ -4,7 +4,6 @@ use PHPUnit\Framework\TestCase; class LinearCongruentialGeneratorTest extends TestCase { public function testAvoidCollisions() { - return; $TEST_TIMES = 10; $GENERATE_TIMES = 500000; $min = 100000; From 870f5fea46766cb3bbf50fe63ae29d607bc8f08e Mon Sep 17 00:00:00 2001 From: Guillermo Date: Tue, 17 Dec 2019 11:05:22 -0300 Subject: [PATCH 11/37] puts get all ruby test --- tests/staff/get-all.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/staff/get-all.rb b/tests/staff/get-all.rb index 5ba054cb..23e089ae 100644 --- a/tests/staff/get-all.rb +++ b/tests/staff/get-all.rb @@ -8,6 +8,8 @@ describe'/staff/get-all' do csrf_token: $csrf_token }) + puts result['data'] + (result['status']).should.equal('success') (result['data'][0]['name']).should.equal('Emilia Clarke') From 61ce46d5c7eecbb219b7808b446cef77203f769b Mon Sep 17 00:00:00 2001 From: Guillermo Date: Tue, 17 Dec 2019 15:25:45 -0300 Subject: [PATCH 12/37] change name Validation priorities --- server/controllers/ticket/search.php | 10 +++++----- server/libs/validations/validPriorities.php | 19 +++++++++++++++++++ tests/init.rb | 3 +-- tests/system/get-settings.rb | 2 +- 4 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 server/libs/validations/validPriorities.php diff --git a/server/controllers/ticket/search.php b/server/controllers/ticket/search.php index 62b49ada..a3ea08fe 100644 --- a/server/controllers/ticket/search.php +++ b/server/controllers/ticket/search.php @@ -71,7 +71,7 @@ class SearchController extends Controller { 'error' => ERRORS::INVALID_UNREAD_STAFF_FILTER ], 'priority' => [ - 'validation' => DataValidator::oneOf(DataValidator::validPrioritys(),DataValidator::nullType()), + 'validation' => DataValidator::oneOf(DataValidator::validPriorities(),DataValidator::nullType()), 'error' => ERRORS::INVALID_PRIORITY_FILTER ], 'dateRange' => [ @@ -205,11 +205,11 @@ class SearchController extends Controller { $filters .= "ticket.unread_staff = " . $unreadStaff; } } - private function setPriorityFilter($prioritys, &$filters){ - if($prioritys != null){ + private function setPriorityFilter($priorities, &$filters){ + if($priorities != null){ $first = TRUE; if ($filters != "") $filters .= " and "; - foreach(array_unique($prioritys) as $priority) { + foreach(array_unique($priorities) as $priority) { if($first){ $filters .= " ( "; @@ -228,7 +228,7 @@ class SearchController extends Controller { } - $prioritys != "" ? $filters .= ") " : null; + $priorities != "" ? $filters .= ") " : null; } } diff --git a/server/libs/validations/validPriorities.php b/server/libs/validations/validPriorities.php new file mode 100644 index 00000000..7313deec --- /dev/null +++ b/server/libs/validations/validPriorities.php @@ -0,0 +1,19 @@ + Date: Tue, 17 Dec 2019 17:02:46 -0300 Subject: [PATCH 13/37] order departments get all ruby test --- tests/staff/get-all.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/staff/get-all.rb b/tests/staff/get-all.rb index 23e089ae..ed3753ad 100644 --- a/tests/staff/get-all.rb +++ b/tests/staff/get-all.rb @@ -8,10 +8,13 @@ describe'/staff/get-all' do csrf_token: $csrf_token }) - puts result['data'] - (result['status']).should.equal('success') + result['data'][0]['departments'].sort_by do |department| + department['id'].to_i + end + puts result['data'] + (result['data'][0]['name']).should.equal('Emilia Clarke') (result['data'][0]['email']).should.equal('staff@opensupports.com') (result['data'][0]['profilePic']).should.equal('') From c8634d457cd7bbc5bc9f26a4d30d90f08c18cbe1 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Tue, 17 Dec 2019 17:28:02 -0300 Subject: [PATCH 14/37] order departments get all ruby test2 --- tests/staff/get-all.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/staff/get-all.rb b/tests/staff/get-all.rb index ed3753ad..432fa3cd 100644 --- a/tests/staff/get-all.rb +++ b/tests/staff/get-all.rb @@ -10,7 +10,7 @@ describe'/staff/get-all' do (result['status']).should.equal('success') - result['data'][0]['departments'].sort_by do |department| + result['data'][0]['departments'] = result['data'][0]['departments'].sort_by do |department| department['id'].to_i end puts result['data'] From 0bd099d05ac8f296d24df556e4fcd1ee7ba10001 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Thu, 19 Dec 2019 17:06:12 -0300 Subject: [PATCH 15/37] add support for custom ticket lists --- .../src/app-components/ticket-query-list.js | 8 ++++++- .../src/app/admin/panel/admin-panel-menu.js | 21 +++++++++++++++++-- .../tickets/admin-panel-search-tickets.js | 4 +++- client/src/index.html | 1 - tests/staff/get-all.rb | 1 - 5 files changed, 29 insertions(+), 6 deletions(-) diff --git a/client/src/app-components/ticket-query-list.js b/client/src/app-components/ticket-query-list.js index a5c7edbc..f28dbc3b 100644 --- a/client/src/app-components/ticket-query-list.js +++ b/client/src/app-components/ticket-query-list.js @@ -22,6 +22,12 @@ class TicketQueryList extends React.Component { this.getTickets(); } + componentDidUpdate(prevProps) { + if (this.props.customList.title !== prevProps.customList.title) { + this.getTickets(); + } + } + render() { return (
@@ -38,7 +44,7 @@ class TicketQueryList extends React.Component { path: '/ticket/search', data: { page : this.state.page, - ...this.props.filters + ...this.props.customList.filters } }).then((result) => { this.setState({ diff --git a/client/src/app/admin/panel/admin-panel-menu.js b/client/src/app/admin/panel/admin-panel-menu.js index da34ccb7..20734df3 100644 --- a/client/src/app/admin/panel/admin-panel-menu.js +++ b/client/src/app/admin/panel/admin-panel-menu.js @@ -76,7 +76,7 @@ class AdminPanelMenu extends React.Component { getGroupItemIndex() { const group = this.getRoutes()[this.getGroupIndex()]; - const pathname = this.props.location.pathname; + const pathname = this.props.location.pathname + this.props.location.search; return _.findIndex(group.items, {path: pathname}); } @@ -90,7 +90,23 @@ class AdminPanelMenu extends React.Component { return (groupIndex === -1) ? 0 : groupIndex; } + getCustomlists() { + if(window.customTicketList){ + return window.customTicketList.map((item, index) => { + return { + name: item.title, + path: '/admin/panel/tickets/search-tickets?custom=' + index, + level: 1 + } + }) + } else { + return []; + } + } + getRoutes() { + const customLists = this.getCustomlists(); + return this.getItemsByFilteredByLevel([ { groupName: i18n('DASHBOARD'), @@ -135,7 +151,8 @@ class AdminPanelMenu extends React.Component { name: i18n('CUSTOM_RESPONSES'), path: '/admin/panel/tickets/custom-responses', level: 2 - } + }, + ...customLists ]) }, { diff --git a/client/src/app/admin/panel/tickets/admin-panel-search-tickets.js b/client/src/app/admin/panel/tickets/admin-panel-search-tickets.js index b9f4842d..2dd4f530 100644 --- a/client/src/app/admin/panel/tickets/admin-panel-search-tickets.js +++ b/client/src/app/admin/panel/tickets/admin-panel-search-tickets.js @@ -14,13 +14,15 @@ class AdminPanelSearchTickets extends React.Component { return (
- {(this.props.error) ? {i18n('ERROR_RETRIEVING_TICKETS')} : } + {(this.props.error) ? {i18n('ERROR_RETRIEVING_TICKETS')} : }
); } getFilters() { + let customList = window.customTicketList[this.props.location.query.custom*1] ? window.customTicketList[this.props.location.query.custom*1] : null return { + ...customList }; } } diff --git a/client/src/index.html b/client/src/index.html index 90c8c2e2..a00af5b1 100755 --- a/client/src/index.html +++ b/client/src/index.html @@ -12,7 +12,6 @@
- diff --git a/tests/staff/get-all.rb b/tests/staff/get-all.rb index 432fa3cd..f8571c71 100644 --- a/tests/staff/get-all.rb +++ b/tests/staff/get-all.rb @@ -13,7 +13,6 @@ describe'/staff/get-all' do result['data'][0]['departments'] = result['data'][0]['departments'].sort_by do |department| department['id'].to_i end - puts result['data'] (result['data'][0]['name']).should.equal('Emilia Clarke') (result['data'][0]['email']).should.equal('staff@opensupports.com') From f9e8a0abec43119dd8e28a9973142af4848a1079 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Thu, 26 Dec 2019 17:25:00 -0300 Subject: [PATCH 16/37] add department test and escape query --- server/controllers/ticket/search.php | 24 ++++----- server/libs/validations/validPrioritys.php | 19 ------- .../tests/controllers/ticket/searchTest.php | 50 ++++++++++++++++--- 3 files changed, 54 insertions(+), 39 deletions(-) delete mode 100644 server/libs/validations/validPrioritys.php diff --git a/server/controllers/ticket/search.php b/server/controllers/ticket/search.php index a3ea08fe..3eb05304 100644 --- a/server/controllers/ticket/search.php +++ b/server/controllers/ticket/search.php @@ -111,21 +111,22 @@ class SearchController extends Controller { 'query' => Controller::request('query'), 'orderBy' => json_decode(Controller::request('orderBy'),true), 'page' => Controller::request('page'), - 'user' => Controller::getLoggedUser(), + 'allowedDepartments' => Controller::getLoggedUser()->sharedDepartmentList->toArray(), ]; + $query = $this->getSQLQuery($inputs); $queryWithOrder = $this->getSQLQueryWithOrder($inputs); - $totalCount = RedBean::getAll("SELECT COUNT(*) FROM (SELECT COUNT(*) " . $query . " ) AS T2")[0]['COUNT(*)']; - $ticketIdList = RedBean::getAll($queryWithOrder); + $totalCount = RedBean::getAll("SELECT COUNT(*) FROM (SELECT COUNT(*) " . $query . " ) AS T2", [':query' => $inputs['query']])[0]['COUNT(*)']; + $ticketIdList = RedBean::getAll($queryWithOrder, [':query' => "%" . $inputs['query'] . "%"]); $ticketList = []; foreach ($ticketIdList as $item) { $ticket = Ticket::getDataStore($item['id']); array_push($ticketList, $ticket->toArray()); } - $ticketTableExists = RedBean::exec("select table_name from information_schema.tables where table_name = 'ticket';"); + if($ticketTableExists){ Response::respondSuccess([ 'tickets' => $ticketList, @@ -171,7 +172,7 @@ class SearchController extends Controller { if(array_key_exists('unreadStaff',$inputs)) $this->setSeenFilter($inputs['unreadStaff'], $filters); if(array_key_exists('priority',$inputs)) $this->setPriorityFilter($inputs['priority'], $filters); if(array_key_exists('dateRange',$inputs)) $this->setDateFilter($inputs['dateRange'], $filters); - if(array_key_exists('departments',$inputs)) $this->setDepartmentFilter($inputs['departments'],$inputs['user'], $filters); + if(array_key_exists('departments',$inputs)) $this->setDepartmentFilter($inputs['departments'],$inputs['allowedDepartments'], $filters); if(array_key_exists('authors',$inputs)) $this->setAuthorFilter($inputs['authors'], $filters); if(array_key_exists('query',$inputs)) $this->setStringFilter($inputs['query'], $filters); if($filters != "") $filters = " WHERE " . $filters; @@ -243,9 +244,8 @@ class SearchController extends Controller { } } - private function setDepartmentFilter($departments,$user, &$filters){ - - $validDepartments = $this->generateValidDepartmentList($departments, $user); + private function setDepartmentFilter($departments,$allowedDepartments, &$filters){ + $validDepartments = $this->generateValidDepartmentList($departments, $allowedDepartments); if ($filters != "") $filters .= " and "; $first = TRUE; @@ -302,16 +302,16 @@ class SearchController extends Controller { if($search != null){ if ($filters != "") $filters .= " and "; - $ticketevent = ( $ticketEventTableExists ? " or (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%" . $search ."%')" : ""); - $filters .= " (ticket.title LIKE '%" . $search . "%' or ticket.content LIKE '%" . $search . "%' or ticket.ticket_number LIKE '%" . $search . "%'". $ticketevent ." )"; + $ticketevent = ( $ticketEventTableExists ? " or (ticketevent.type = 'COMMENT' and ticketevent.content LIKE :query)" : ""); + $filters .= " (ticket.title LIKE :query or ticket.content LIKE :query or ticket.ticket_number LIKE :query". $ticketevent ." )"; }; } - private function generateValidDepartmentList($departments, $user){ + private function generateValidDepartmentList($departments, $allowedDepartments){ $result = []; $managedDepartments = []; if($departments == null) $departments = []; - foreach ($user->sharedDepartmentList->toArray() as $department) { + foreach ($allowedDepartments as $department) { array_push($managedDepartments,$department['id']); } $result = array_intersect($departments,$managedDepartments); diff --git a/server/libs/validations/validPrioritys.php b/server/libs/validations/validPrioritys.php deleted file mode 100644 index 53bf9505..00000000 --- a/server/libs/validations/validPrioritys.php +++ /dev/null @@ -1,19 +0,0 @@ -= 1 and ticket.date <= 2) GROUP BY ticket.id' ); } - /* + public function testDepartmentsFilter() { $this->assertEquals( $this->searchController->getSQLQuery([ - 'departments' => null + 'departments' => null, + 'allowedDepartments' => [ + [ + 'id' => 2 + ], + [ + 'id' => 1 + ], + [ + 'id' => 3 + ] + ] ]), - 'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) GROUP BY ticket.id' + 'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE ( ticket.department_id = 2 or ticket.department_id = 1 or ticket.department_id = 3) GROUP BY ticket.id' ); $this->assertEquals( $this->searchController->getSQLQuery([ - 'departments' => [1] + 'departments' => [1], + 'allowedDepartments' => [ + [ + 'id' => 2 + ], + [ + 'id' => 1 + ], + [ + 'id' => 3 + ] + ] ]), 'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE ( ticket.department_id = 1) GROUP BY ticket.id' ); $this->assertEquals( $this->searchController->getSQLQuery([ - 'departments' => [1,2,3] + 'departments' => [1,2,3], + 'allowedDepartments' => [ + [ + 'id' => 2 + ], + [ + 'id' => 1 + ], + [ + 'id' => 3 + ] + ] ]), 'FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE ( ticket.department_id = 1 or ticket.department_id = 2 or ticket.department_id = 3) GROUP BY ticket.id' ); } - */ + public function testAuthorsFilter() { $this->assertEquals( $this->searchController->getSQLQuery([ @@ -228,7 +261,8 @@ class SearchControllerTest extends TestCase { $this->searchController->getSQLQuery([ 'query' => 'hello world' ]), - "FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE (ticket.title LIKE '%hello world%' or ticket.content LIKE '%hello world%' or ticket.ticket_number LIKE '%hello world%' or (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%hello world%') ) GROUP BY ticket.id" + "FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE (ticket.title LIKE :query or ticket.content LIKE :query or ticket.ticket_number LIKE :query or (ticketevent.type = 'COMMENT' and ticketevent.content LIKE :query) ) GROUP BY ticket.id" + ); } public function testQueryWithOrder() { @@ -244,7 +278,7 @@ class SearchControllerTest extends TestCase { 'page' => 1, 'query' => 'stark' ]), - "SELECT ticket.id FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE (ticket.title LIKE '%stark%' or ticket.content LIKE '%stark%' or ticket.ticket_number LIKE '%stark%' or (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%stark%') ) GROUP BY ticket.id ORDER BY CASE WHEN (ticket.ticket_number LIKE '%stark%') THEN ticket.ticket_number END desc,CASE WHEN (ticket.title LIKE '%stark%') THEN ticket.title END desc, CASE WHEN ( ticket.content LIKE '%stark%') THEN ticket.content END desc, CASE WHEN (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%stark%') THEN ticketevent.content END desc,ticket.closed asc, ticket.owner_id asc, ticket.unread_staff asc, ticket.priority desc, ticket.date desc LIMIT 10 OFFSET 0" + "SELECT ticket.id FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE (ticket.title LIKE :query or ticket.content LIKE :query or ticket.ticket_number LIKE :query or (ticketevent.type = 'COMMENT' and ticketevent.content LIKE :query) ) GROUP BY ticket.id ORDER BY CASE WHEN (ticket.ticket_number LIKE '%stark%') THEN ticket.ticket_number END desc,CASE WHEN (ticket.title LIKE '%stark%') THEN ticket.title END desc, CASE WHEN ( ticket.content LIKE '%stark%') THEN ticket.content END desc, CASE WHEN (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%stark%') THEN ticketevent.content END desc,ticket.closed asc, ticket.owner_id asc, ticket.unread_staff asc, ticket.priority desc, ticket.date desc LIMIT 10 OFFSET 0" ); $this->assertEquals( From d25da2c5ff055d0575bd88c99347a16d68ace8af Mon Sep 17 00:00:00 2001 From: Guillermo Date: Fri, 27 Dec 2019 19:50:50 -0300 Subject: [PATCH 17/37] add validation test --- client/src/data/languages/en.js | 2 +- server/controllers/ticket/search.php | 6 +++--- server/tests/controllers/ticket/searchTest.php | 2 +- tests/init.rb | 1 + 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/client/src/data/languages/en.js b/client/src/data/languages/en.js index f244d610..7816e9c3 100644 --- a/client/src/data/languages/en.js +++ b/client/src/data/languages/en.js @@ -298,7 +298,7 @@ export default { 'MY_TICKETS_DESCRIPTION': 'Here you can view the tickets you are responsible for.', 'NEW_TICKETS_DESCRIPTION': 'Here you can view all the new tickets that are not assigned by anyone.', 'ALL_TICKETS_DESCRIPTION': 'Here you can view the tickets of the departments you are assigned.', - 'SEARCH_TICKETS_DESCRIPTION': 'Here you can search tickets by specifics filters.', + 'SEARCH_TICKETS_DESCRIPTION': 'Here you can search tickets by specific filters', 'TICKET_VIEW_DESCRIPTION': 'This ticket has been sent by a customer. Here you can respond or assign the ticket', 'BAN_USERS_DESCRIPTION': 'Here you can see a list of banned emails, you can un-ban them or add more emails to the list.', 'LIST_USERS_DESCRIPTION': 'This is the list of users that are registered in this platform. You can search for someone in particular, delete it or ban it.', diff --git a/server/controllers/ticket/search.php b/server/controllers/ticket/search.php index 3eb05304..e1fe5696 100644 --- a/server/controllers/ticket/search.php +++ b/server/controllers/ticket/search.php @@ -91,7 +91,7 @@ class SearchController extends Controller { 'error' => ERRORS::INVALID_ASSIGNED_FILTER ], 'orderBy' => [ - 'validation' => DataValidator::oneOf(DataValidator::ValidOrderBy(),DataValidator::nullType()), + 'validation' => DataValidator::oneOf(DataValidator::validOrderBy(),DataValidator::nullType()), 'error' => ERRORS::INVALID_ORDER_BY ], ] @@ -340,8 +340,8 @@ class SearchController extends Controller { $ticketEventTableExists = RedBean::exec("select table_name from information_schema.tables where table_name = 'ticketevent';"); if($querysearch != null){ - $ticketeventOrder = ( $ticketEventTableExists ? " CASE WHEN (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%".$querysearch."%') THEN ticketevent.content END desc," : ""); - $order .= "CASE WHEN (ticket.ticket_number LIKE '%" . $querysearch ."%') THEN ticket.ticket_number END desc,CASE WHEN (ticket.title LIKE '%" . $querysearch ."%') THEN ticket.title END desc, CASE WHEN ( ticket.content LIKE '%" . $querysearch ."%') THEN ticket.content END desc," . $ticketeventOrder ; + $ticketeventOrder = ( $ticketEventTableExists ? " CASE WHEN (ticketevent.type = 'COMMENT' and ticketevent.content LIKE :query) THEN ticketevent.content END desc," : ""); + $order .= "CASE WHEN (ticket.ticket_number LIKE :query) THEN ticket.ticket_number END desc,CASE WHEN (ticket.title LIKE :query) THEN ticket.title END desc, CASE WHEN ( ticket.content LIKE :query) THEN ticket.content END desc," . $ticketeventOrder ; } } diff --git a/server/tests/controllers/ticket/searchTest.php b/server/tests/controllers/ticket/searchTest.php index b402a81d..6f74449a 100644 --- a/server/tests/controllers/ticket/searchTest.php +++ b/server/tests/controllers/ticket/searchTest.php @@ -278,7 +278,7 @@ class SearchControllerTest extends TestCase { 'page' => 1, 'query' => 'stark' ]), - "SELECT ticket.id FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE (ticket.title LIKE :query or ticket.content LIKE :query or ticket.ticket_number LIKE :query or (ticketevent.type = 'COMMENT' and ticketevent.content LIKE :query) ) GROUP BY ticket.id ORDER BY CASE WHEN (ticket.ticket_number LIKE '%stark%') THEN ticket.ticket_number END desc,CASE WHEN (ticket.title LIKE '%stark%') THEN ticket.title END desc, CASE WHEN ( ticket.content LIKE '%stark%') THEN ticket.content END desc, CASE WHEN (ticketevent.type = 'COMMENT' and ticketevent.content LIKE '%stark%') THEN ticketevent.content END desc,ticket.closed asc, ticket.owner_id asc, ticket.unread_staff asc, ticket.priority desc, ticket.date desc LIMIT 10 OFFSET 0" + "SELECT ticket.id FROM (ticket LEFT JOIN tag_ticket ON tag_ticket.ticket_id = ticket.id LEFT JOIN ticketevent ON ticketevent.ticket_id = ticket.id) WHERE (ticket.title LIKE :query or ticket.content LIKE :query or ticket.ticket_number LIKE :query or (ticketevent.type = 'COMMENT' and ticketevent.content LIKE :query) ) GROUP BY ticket.id ORDER BY CASE WHEN (ticket.ticket_number LIKE :query) THEN ticket.ticket_number END desc,CASE WHEN (ticket.title LIKE :query) THEN ticket.title END desc, CASE WHEN ( ticket.content LIKE :query) THEN ticket.content END desc, CASE WHEN (ticketevent.type = 'COMMENT' and ticketevent.content LIKE :query) THEN ticketevent.content END desc,ticket.closed asc, ticket.owner_id asc, ticket.unread_staff asc, ticket.priority desc, ticket.date desc LIMIT 10 OFFSET 0" ); $this->assertEquals( diff --git a/tests/init.rb b/tests/init.rb index ffffdcb7..e86faea2 100644 --- a/tests/init.rb +++ b/tests/init.rb @@ -71,4 +71,5 @@ require './ticket/add-tag.rb' require './ticket/delete-tag.rb' require './ticket/edit-comment.rb' require './system/disable-user-system.rb' +require './ticket/search.rb' # require './system/get-stats.rb' From 9f84239c3e5b162cbd64cf24bc46ecab06193f07 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Fri, 27 Dec 2019 19:59:23 -0300 Subject: [PATCH 18/37] search ruby tests --- tests/ticket/search.rb | 114 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 tests/ticket/search.rb diff --git a/tests/ticket/search.rb b/tests/ticket/search.rb new file mode 100644 index 00000000..4b3e4c0a --- /dev/null +++ b/tests/ticket/search.rb @@ -0,0 +1,114 @@ +describe '/ticket/search' do + request('/user/logout') + Scripts.login($staff[:email], $staff[:password], true) + + + it 'should fail if the page is invalid' do + result = request('/ticket/search', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + page: -1 + }) + (result['status']).should.equal('fail') + (result['message']).should.equal('INVALID_PAGE') + end + + it 'should fail if the tags are invalid' do + result = request('/ticket/search', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + page: 1, + tags: [1,11,111,1111,11111,111111,1111111,11111111] + }) + (result['status']).should.equal('fail') + (result['message']).should.equal('INVALID_TAG_FILTER') + end + + it 'should fail if the closed value is invalid' do + result = request('/ticket/search', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + page: 1, + closed: 3 + }) + (result['status']).should.equal('fail') + (result['message']).should.equal('INVALID_CLOSED_FILTER') + end + + it 'should fail if the unreadStaff value is invalid' do + result = request('/ticket/search', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + page: 1, + unreadStaff: 3 + }) + (result['status']).should.equal('fail') + (result['message']).should.equal('INVALID_UNREAD_STAFF_FILTER') + end + + it 'should fail if the priority values are invalid' do + result = request('/ticket/search', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + page: 1, + priority: [0,1,5,6] + }) + (result['status']).should.equal('fail') + (result['message']).should.equal('INVALID_PRIORITY_FILTER') + end + + it 'should fail if the dateRange values are invalid' do + result = request('/ticket/search', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + page: 1, + dateRange: [11,69,11] + }) + (result['status']).should.equal('fail') + (result['message']).should.equal('INVALID_DATE_RANGE_FILTER') + end + + it 'should fail if the departments are invalid' do + result = request('/ticket/search', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + page: 1, + departments: [-1,-2,99] + }) + (result['status']).should.equal('fail') + (result['message']).should.equal('INVALID_DEPARTMENT_FILTER') + end + + it 'should fail if the authors are invalid' do + result = request('/ticket/search', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + page: 1, + authors: [{id:30001, staff: 1},{id:30,staff: 3}] + }) + (result['status']).should.equal('fail') + (result['message']).should.equal('INVALID_AUTHOR_FILTER') + end + + it 'should fail if the assigned value is invalid' do + result = request('/ticket/search', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + page: 1, + assigned: 3 + }) + (result['status']).should.equal('fail') + (result['message']).should.equal('INVALID_ASSIGNED_FILTER') + end + + it 'should fail if the orderBy values are invalid' do + result = request('/ticket/search', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + page: 1, + orderBy: [{value: 'closeddd', asc: 11}] + }) + (result['status']).should.equal('fail') + (result['message']).should.equal('INVALID_ORDER_BY') + end +end From e64e8e65f165b335d77514b4b7eb844feaffec61 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Fri, 27 Dec 2019 20:51:22 -0300 Subject: [PATCH 19/37] update rudy test --- tests/ticket/search.rb | 43 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/tests/ticket/search.rb b/tests/ticket/search.rb index 4b3e4c0a..ad22f081 100644 --- a/tests/ticket/search.rb +++ b/tests/ticket/search.rb @@ -18,7 +18,7 @@ describe '/ticket/search' do csrf_userid: $csrf_userid, csrf_token: $csrf_token, page: 1, - tags: [1,11,111,1111,11111,111111,1111111,11111111] + tags: "[1,11,111,1111,11111,111111,1111111,11111111]" }) (result['status']).should.equal('fail') (result['message']).should.equal('INVALID_TAG_FILTER') @@ -51,7 +51,18 @@ describe '/ticket/search' do csrf_userid: $csrf_userid, csrf_token: $csrf_token, page: 1, - priority: [0,1,5,6] + priority: "[0,1,5,6]" + }) + (result['status']).should.equal('fail') + (result['message']).should.equal('INVALID_PRIORITY_FILTER') + end + + it 'should fail if the priority' do + result = request('/ticket/search', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + page: 1, + priority: "[0,1,),hi]" }) (result['status']).should.equal('fail') (result['message']).should.equal('INVALID_PRIORITY_FILTER') @@ -62,7 +73,7 @@ describe '/ticket/search' do csrf_userid: $csrf_userid, csrf_token: $csrf_token, page: 1, - dateRange: [11,69,11] + dateRange: "[11,69,()) ]" }) (result['status']).should.equal('fail') (result['message']).should.equal('INVALID_DATE_RANGE_FILTER') @@ -73,7 +84,7 @@ describe '/ticket/search' do csrf_userid: $csrf_userid, csrf_token: $csrf_token, page: 1, - departments: [-1,-2,99] + departments: "[-1,-2,99]" }) (result['status']).should.equal('fail') (result['message']).should.equal('INVALID_DEPARTMENT_FILTER') @@ -84,7 +95,16 @@ describe '/ticket/search' do csrf_userid: $csrf_userid, csrf_token: $csrf_token, page: 1, - authors: [{id:30001, staff: 1},{id:30,staff: 3}] + authors: "[{id:30001, staff: 1},{id:30,staff: 3}]" + }) + (result['status']).should.equal('fail') + (result['message']).should.equal('INVALID_AUTHOR_FILTER') + + result = request('/ticket/search', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + page: 1, + authors: "[{id:'delete all)', staff: 1},{id:30,staff: 3}]" }) (result['status']).should.equal('fail') (result['message']).should.equal('INVALID_AUTHOR_FILTER') @@ -101,12 +121,23 @@ describe '/ticket/search' do (result['message']).should.equal('INVALID_ASSIGNED_FILTER') end + it 'should fail if the assigned value is invalid' do + result = request('/ticket/search', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + page: 1, + query: 'dasjfawj!!!)())' + }) + (result['status']).should.equal('fail') + (result['message']).should.equal('INVALID_ASSIGNED_FILTER') + end + it 'should fail if the orderBy values are invalid' do result = request('/ticket/search', { csrf_userid: $csrf_userid, csrf_token: $csrf_token, page: 1, - orderBy: [{value: 'closeddd', asc: 11}] + orderBy: "{value: 'closeddd', asc: 11}" }) (result['status']).should.equal('fail') (result['message']).should.equal('INVALID_ORDER_BY') From d5f5d988fb2acf0c09c2445b99485129f3ab9c79 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Fri, 27 Dec 2019 22:07:40 -0300 Subject: [PATCH 20/37] redirect after deleting ticket --- client/src/app-components/ticket-viewer.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/client/src/app-components/ticket-viewer.js b/client/src/app-components/ticket-viewer.js index 09e1fbad..18dcfee8 100644 --- a/client/src/app-components/ticket-viewer.js +++ b/client/src/app-components/ticket-viewer.js @@ -8,6 +8,7 @@ import i18n from 'lib-app/i18n'; import API from 'lib-app/api-call'; import SessionStore from 'lib-app/session-store'; import MentionsParser from 'lib-app/mentions-parser'; +import history from 'lib-app/history'; import TicketEvent from 'app-components/ticket-event'; import AreYouSure from 'app-components/are-you-sure'; @@ -69,7 +70,6 @@ class TicketViewer extends React.Component { render() { const ticket = this.props.ticket; - return (
@@ -416,7 +416,10 @@ class TicketViewer extends React.Component { data: { ticketNumber: this.props.ticket.ticketNumber } - }).then(this.onTicketModification.bind(this)); + }).then((result) => { + this.onTicketModification(result); + history.push('/admin/panel/tickets/my-tickets/'); + }); } changeDepartment(index) { @@ -444,6 +447,7 @@ class TicketViewer extends React.Component { } }).then(this.onTicketModification.bind(this)); } + addTag(tag) { API.call({ path: '/ticket/add-tag', @@ -463,6 +467,7 @@ class TicketViewer extends React.Component { } }).then(this.onTicketModification.bind(this)) } + onCustomResponsesChanged({index}) { let replaceContentWithCustomResponse = () => { this.setState({ @@ -492,7 +497,7 @@ class TicketViewer extends React.Component { const data = {}; if(ticketeventid){ - data.ticketeventId = ticketeventid + data.ticketEventId = ticketeventid }else{ data.ticketNumber = this.props.ticket.ticketNumber } @@ -523,6 +528,7 @@ class TicketViewer extends React.Component { commentError: true }); } + onSubmit(formState) { this.setState({ loading: true @@ -602,6 +608,7 @@ class TicketViewer extends React.Component { } export default connect((store) => { + return { userId: store.session.userId, userStaff: store.session.staff, From 702b7862028acfc2ce394c388a0e011ad58bed62 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Fri, 27 Dec 2019 22:46:25 -0300 Subject: [PATCH 21/37] search ruby test --- tests/ticket/search.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ticket/search.rb b/tests/ticket/search.rb index ad22f081..2ae320d6 100644 --- a/tests/ticket/search.rb +++ b/tests/ticket/search.rb @@ -126,7 +126,7 @@ describe '/ticket/search' do csrf_userid: $csrf_userid, csrf_token: $csrf_token, page: 1, - query: 'dasjfawj!!!)())' + assigned: 11113 }) (result['status']).should.equal('fail') (result['message']).should.equal('INVALID_ASSIGNED_FILTER') From 8be43bb7ead604d33f77cd6ffb95f989ddc48202 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Sat, 28 Dec 2019 17:50:11 -0300 Subject: [PATCH 22/37] resolve mirrors errors --- server/controllers/ticket/search.php | 24 ++++++++++----------- server/libs/validations/validDateRange.php | 22 +++++++++---------- server/libs/validations/validOrderBy.php | 9 +++----- server/libs/validations/validPriorities.php | 4 ++-- 4 files changed, 28 insertions(+), 31 deletions(-) diff --git a/server/controllers/ticket/search.php b/server/controllers/ticket/search.php index e1fe5696..00431c34 100644 --- a/server/controllers/ticket/search.php +++ b/server/controllers/ticket/search.php @@ -195,19 +195,19 @@ class SearchController extends Controller { } } public function setClosedFilter($closed, &$filters){ - if ($closed != null) { + if ($closed !== null) { if ($filters != "") $filters .= " and "; $filters .= "ticket.closed = " . $closed ; } } private function setSeenFilter($unreadStaff, &$filters){ - if ($unreadStaff != null) { + if ($unreadStaff !== null) { if ($filters != "") $filters .= " and "; $filters .= "ticket.unread_staff = " . $unreadStaff; } } private function setPriorityFilter($priorities, &$filters){ - if($priorities != null){ + if($priorities !== null){ $first = TRUE; if ($filters != "") $filters .= " and "; foreach(array_unique($priorities) as $priority) { @@ -220,11 +220,11 @@ class SearchController extends Controller { } if($priority == 0){ - $filters .= "ticket.priority = " . "'low'"; + $filters .= "ticket.priority = 'low'"; }elseif($priority == 1){ - $filters .= "ticket.priority = " . "'medium'"; + $filters .= "ticket.priority = 'medium'"; }elseif($priority == 2){ - $filters .= "ticket.priority = " . "'high'"; + $filters .= "ticket.priority = 'high'"; } @@ -235,7 +235,7 @@ class SearchController extends Controller { } private function setDateFilter($dateRange, &$filters){ - if ($dateRange != null) { + if ($dateRange !== null) { if ($filters != "") $filters .= " and "; foreach($dateRange as $key => $date) { @@ -263,7 +263,7 @@ class SearchController extends Controller { } private function setAuthorFilter($authors, &$filters){ - if($authors != null){ + if($authors !== null){ $first = TRUE; if ($filters != "") $filters .= " and "; @@ -289,7 +289,7 @@ class SearchController extends Controller { } private function setAssignedFilter($assigned, &$filters){ - if($assigned != null){ + if($assigned !== null){ if ($filters != "") $filters .= " and "; $key = ""; $assigned == 0 ? $key = "IS NULL" : $key = "IS NOT NULL"; @@ -300,7 +300,7 @@ class SearchController extends Controller { private function setStringFilter($search, &$filters){ $ticketEventTableExists = RedBean::exec("select table_name from information_schema.tables where table_name = 'ticketevent';"); - if($search != null){ + if($search !== null){ if ($filters != "") $filters .= " and "; $ticketevent = ( $ticketEventTableExists ? " or (ticketevent.type = 'COMMENT' and ticketevent.content LIKE :query)" : ""); $filters .= " (ticket.title LIKE :query or ticket.content LIKE :query or ticket.ticket_number LIKE :query". $ticketevent ." )"; @@ -331,7 +331,7 @@ class SearchController extends Controller { $order .= "ticket.closed asc, ticket.owner_id asc, ticket.unread_staff asc, ticket.priority desc, ticket.date desc "; } private function setEspecificOrder($orderBy, &$order){ - if($orderBy != null){ + if($orderBy !== null){ $orientation = ($orderBy['asc'] ? " asc" : " desc" ); $order .= "ticket." . $orderBy['value'] . $orientation . ","; }; @@ -339,7 +339,7 @@ class SearchController extends Controller { private function setStringOrder($querysearch, &$order){ $ticketEventTableExists = RedBean::exec("select table_name from information_schema.tables where table_name = 'ticketevent';"); - if($querysearch != null){ + if($querysearch !== null){ $ticketeventOrder = ( $ticketEventTableExists ? " CASE WHEN (ticketevent.type = 'COMMENT' and ticketevent.content LIKE :query) THEN ticketevent.content END desc," : ""); $order .= "CASE WHEN (ticket.ticket_number LIKE :query) THEN ticket.ticket_number END desc,CASE WHEN (ticket.title LIKE :query) THEN ticket.title END desc, CASE WHEN ( ticket.content LIKE :query) THEN ticket.content END desc," . $ticketeventOrder ; } diff --git a/server/libs/validations/validDateRange.php b/server/libs/validations/validDateRange.php index 05f6a3c2..b332909e 100644 --- a/server/libs/validations/validDateRange.php +++ b/server/libs/validations/validDateRange.php @@ -7,15 +7,15 @@ use Respect\Validation\Rules\AbstractRule; class ValidDateRange extends AbstractRule { public function validate($dateRange) { - $dateArray = json_decode($dateRange); - $counter = 0; - if(is_array($dateArray)){ - foreach ($dateArray as $date) { - if (is_numeric($date)) $counter++; - } + $dateArray = json_decode($dateRange); + $length = count($dateArray); - return ((sizeof($dateArray) == 2 && $counter == 2) || sizeof($dateArray) == 0 ); - } - return false; - } -} \ No newline at end of file + if(is_array($dateArray) && $length == 2 ){ + foreach ($dateArray as $date) { + if (!is_numeric($date)) return false; + } + return $dateArray[0] <= $dateArray[1]; + } + return false; + } +} diff --git a/server/libs/validations/validOrderBy.php b/server/libs/validations/validOrderBy.php index 44466805..40babc27 100644 --- a/server/libs/validations/validOrderBy.php +++ b/server/libs/validations/validOrderBy.php @@ -8,15 +8,12 @@ class ValidOrderBy extends AbstractRule { public function validate($orderBy) { if(is_object(json_decode($orderBy))){ $values =["closed","owner_id","unread_staff","priority","date"]; - $isTrue = false; + $object = json_decode($orderBy); - if($object->asc !== 1 && $object->asc !== 0) return false; + if(($object->asc !== 1 && $object->asc !== 0) || !in_array($object->value, $values)) return false; - foreach ($values as $value) { - if($object->value == $value) $isTrue = true; - } - return $isTrue; + return true; } } } diff --git a/server/libs/validations/validPriorities.php b/server/libs/validations/validPriorities.php index 7313deec..aa3ba658 100644 --- a/server/libs/validations/validPriorities.php +++ b/server/libs/validations/validPriorities.php @@ -5,8 +5,8 @@ namespace CustomValidations; use Respect\Validation\Rules\AbstractRule; class ValidPriorities extends AbstractRule { - public function validate($prioritys) { - $PriorityList = json_decode($prioritys); + public function validate($priorities) { + $PriorityList = json_decode($priorities); if(is_array($PriorityList)){ foreach (array_unique($PriorityList) as $priorityId) { From cc9c51b85627de371c93b3590d17dda2d3dbf15c Mon Sep 17 00:00:00 2001 From: Guillermo Date: Sun, 29 Dec 2019 15:48:57 -0300 Subject: [PATCH 23/37] fix date range validation on php 7.2 --- server/libs/validations/validDateRange.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/server/libs/validations/validDateRange.php b/server/libs/validations/validDateRange.php index b332909e..c2bb3f05 100644 --- a/server/libs/validations/validDateRange.php +++ b/server/libs/validations/validDateRange.php @@ -8,9 +8,8 @@ class ValidDateRange extends AbstractRule { public function validate($dateRange) { $dateArray = json_decode($dateRange); - $length = count($dateArray); - - if(is_array($dateArray) && $length == 2 ){ + + if(is_array($dateArray) && count($dateArray) == 2 ){ foreach ($dateArray as $date) { if (!is_numeric($date)) return false; } From af92a6bbf242d089b3af1cfaf948f96afd6e14f5 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Thu, 2 Jan 2020 09:55:55 -0300 Subject: [PATCH 24/37] part 1 edit ticket title --- client/src/app-components/ticket-viewer.js | 15 ++++- server/controllers/ticket.php | 1 + server/controllers/ticket/edit-comment.php | 4 ++ server/controllers/ticket/edit-title.php | 65 ++++++++++++++++++++++ server/models/Ticket.php | 3 +- tests/init.rb | 1 + tests/ticket/edit-title.rb | 62 +++++++++++++++++++++ 7 files changed, 149 insertions(+), 2 deletions(-) create mode 100644 server/controllers/ticket/edit-title.php create mode 100644 tests/ticket/edit-title.rb diff --git a/client/src/app-components/ticket-viewer.js b/client/src/app-components/ticket-viewer.js index e965fe8e..dac48eda 100644 --- a/client/src/app-components/ticket-viewer.js +++ b/client/src/app-components/ticket-viewer.js @@ -73,10 +73,13 @@ class TicketViewer extends React.Component {
#{ticket.ticketNumber} - {ticket.title} + { false ? {ticket.title} : this.editTitle()} + + +
{this.props.editable ? this.renderEditableHeaders() : this.renderHeaders()}
@@ -104,6 +107,16 @@ class TicketViewer extends React.Component { ); } + editTitle(){ + return( + +
+ + +
+ ) + } + renderEditableHeaders() { const ticket = this.props.ticket; const departments = this.getDepartmentsForTransfer(); diff --git a/server/controllers/ticket.php b/server/controllers/ticket.php index e60c253e..bc046c51 100755 --- a/server/controllers/ticket.php +++ b/server/controllers/ticket.php @@ -4,6 +4,7 @@ $ticketControllers->setGroupPath('/ticket'); $ticketControllers->addController(new CreateController); $ticketControllers->addController(new EditCommentController); +$ticketControllers->addController(new EditTitleController); $ticketControllers->addController(new CommentController); $ticketControllers->addController(new TicketGetController); $ticketControllers->addController(new CheckTicketController); diff --git a/server/controllers/ticket/edit-comment.php b/server/controllers/ticket/edit-comment.php index c924694b..faa76539 100644 --- a/server/controllers/ticket/edit-comment.php +++ b/server/controllers/ticket/edit-comment.php @@ -36,6 +36,10 @@ class EditCommentController extends Controller { 'content' => [ 'validation' => DataValidator::length(10, 5000), 'error' => ERRORS::INVALID_CONTENT + ], + 'ticketNumber' => [ + 'validation' => DataValidator::validTicketNumber(), + 'error' => ERRORS::INVALID_TICKET ] ] ]; diff --git a/server/controllers/ticket/edit-title.php b/server/controllers/ticket/edit-title.php new file mode 100644 index 00000000..84450141 --- /dev/null +++ b/server/controllers/ticket/edit-title.php @@ -0,0 +1,65 @@ + 'user', + 'requestData' => [ + 'title' => [ + 'validation' => DataValidator::length(1, 200), + 'error' => ERRORS::INVALID_TITLE + ], + 'ticketNumber' => [ + 'validation' => DataValidator::validTicketNumber(), + 'error' => ERRORS::INVALID_TICKET + ] + ] + ]; + } + + public function handler() { + $user = Controller::getLoggedUser(); + $newtitle = Controller::request('title'); + $ticket = Ticket::getByTicketNumber(Controller::request('ticketNumber')); + + if(!$user->canManageTicket($ticket)) { + throw new RequestException(ERRORS::NO_PERMISSION); + } + + $ticket->title = $newtitle; + $ticket->editedTitle = true; + $ticket->store(); + + $ticketNumber = $ticket->ticketNumber; + Log::createLog('EDIT_TITLE', $ticketNumber); + + Response::respondSuccess(); + } +} diff --git a/server/models/Ticket.php b/server/models/Ticket.php index 5ad71589..c820c270 100755 --- a/server/models/Ticket.php +++ b/server/models/Ticket.php @@ -51,7 +51,8 @@ class Ticket extends DataStore { 'authorEmail', 'authorName', 'sharedTagList', - 'editedContent' + 'editedContent', + 'editedTitle' ); } diff --git a/tests/init.rb b/tests/init.rb index e86faea2..8c6cb9f4 100644 --- a/tests/init.rb +++ b/tests/init.rb @@ -70,6 +70,7 @@ require './ticket/delete-tag.rb' require './ticket/add-tag.rb' require './ticket/delete-tag.rb' require './ticket/edit-comment.rb' +require './ticket/edit-title.rb' require './system/disable-user-system.rb' require './ticket/search.rb' # require './system/get-stats.rb' diff --git a/tests/ticket/edit-title.rb b/tests/ticket/edit-title.rb new file mode 100644 index 00000000..095be590 --- /dev/null +++ b/tests/ticket/edit-title.rb @@ -0,0 +1,62 @@ +describe '/ticket/edit-title' do + + request('/user/logout') + Scripts.login(); + Scripts.createTicket('Valar Morghulis','content of the ticket made by an user') + ticket = $database.getRow('ticket', 'Valar Morghulis', 'title') + ticketNumber = ticket['ticket_number'] + + it 'should change title of the ticket if the author user tries it' do + result = request('/ticket/edit-title', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + title: 'Valar dohaeris', + ticketNumber: ticket['ticket_number'] + }) + + ticket = $database.getRow('ticket', ticketNumber, 'ticket_number') + + (result['status']).should.equal('success') + (ticket['title']).should.equal('Valar dohaeris') + (ticket['edited_title']).should.equal('1') + end + + it 'should change the title of the ticket if staff is logged' do + request('/user/logout') + Scripts.login($staff[:email], $staff[:password], true) + + result = request('/ticket/edit-title', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + title: 'Valar dohaeris by Staff', + ticketNumber: ticket['ticket_number'] + }) + + ticket = $database.getRow('ticket', ticketNumber, 'ticket_number') + + (result['status']).should.equal('success') + (ticket['title']).should.equal('Valar dohaeris by Staff') + (ticket['edited_title']).should.equal('1') + + end + + it 'should not change the title if the user is not the author' do + request('/user/logout') + Scripts.login($staff[:email], $staff[:password], true) + Scripts.createTicket('Winterfell') + ticket = $database.getRow('ticket', 'Winterfell', 'title') + + request('/user/logout') + Scripts.login() + + result = request('/ticket/edit-title', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + title: 'Casterly Rock', + ticketEventId: ticket['ticket_number'] + }) + (result['status']).should.equal('fail') + (result['message']).should.equal('NO_PERMISSION') + end + +end From 97d6bd6a36a47a3a5203c4ac30b0d3f615034b68 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Thu, 2 Jan 2020 10:09:44 -0300 Subject: [PATCH 25/37] fix ticket search path showing --- .../src/app/admin/panel/tickets/admin-panel-search-tickets.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/app/admin/panel/tickets/admin-panel-search-tickets.js b/client/src/app/admin/panel/tickets/admin-panel-search-tickets.js index 2dd4f530..f5726f2c 100644 --- a/client/src/app/admin/panel/tickets/admin-panel-search-tickets.js +++ b/client/src/app/admin/panel/tickets/admin-panel-search-tickets.js @@ -20,7 +20,7 @@ class AdminPanelSearchTickets extends React.Component { } getFilters() { - let customList = window.customTicketList[this.props.location.query.custom*1] ? window.customTicketList[this.props.location.query.custom*1] : null + let customList = (window.customTicketList && window.customTicketList[this.props.location.query.custom*1]) ? window.customTicketList[this.props.location.query.custom*1] : null return { ...customList }; From 5f1d0023397d05f94987fea6fbf6703784c12bc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Fra=C5=9B?= Date: Thu, 2 Jan 2020 23:18:10 +0100 Subject: [PATCH 26/37] Update polish translation correct translations for closing and deleting tickets --- client/src/data/languages/pl.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/data/languages/pl.js b/client/src/data/languages/pl.js index ac44a9cf..a03bd8af 100644 --- a/client/src/data/languages/pl.js +++ b/client/src/data/languages/pl.js @@ -29,8 +29,8 @@ export default { 'DATE': 'Data', 'RESPOND': 'Odpowiedz', 'RESPOND_TICKET': 'Odpowiedź na zgłoszenie', - 'CLOSE_TICKET': 'blisko bilet', - 'DELETE_TICKET': 'Usuń bilet', + 'CLOSE_TICKET': 'Zamknij zgłoszenie', + 'DELETE_TICKET': 'Usuń zgłoszenie', 'NO_ATTACHMENT': 'Bez załącznika', 'STAFF': 'Personel', 'CUSTOMER': 'Klient', From 51e498f8c98048dfb7c8a38d91e112b6e13e5b68 Mon Sep 17 00:00:00 2001 From: Ivan Diaz Date: Tue, 7 Jan 2020 14:44:18 -0300 Subject: [PATCH 27/37] Fix add-api-key description comment --- server/controllers/system/add-api-key.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/controllers/system/add-api-key.php b/server/controllers/system/add-api-key.php index 5f922817..fbb34107 100755 --- a/server/controllers/system/add-api-key.php +++ b/server/controllers/system/add-api-key.php @@ -14,7 +14,7 @@ use Respect\Validation\Validator as DataValidator; * @apiPermission staff3 * * @apiParam {String} name Name of the new APIKey. - * @apiParam {String} type Type of APIKey: "REGSITRATION" or "TICKET_CREATE" + * @apiParam {String} type Type of APIKey: "REGISTRATION" or "TICKET_CREATE" * * @apiUse NO_PERMISSION * @apiUse INVALID_NAME From ec98767e25468f22c7c4cf6d5736d6f6c4f89ec9 Mon Sep 17 00:00:00 2001 From: Ivan Diaz Date: Tue, 7 Jan 2020 14:44:18 -0300 Subject: [PATCH 28/37] Fix APIkey mock --- server/controllers/system/add-api-key.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/controllers/system/add-api-key.php b/server/controllers/system/add-api-key.php index 5f922817..fbb34107 100755 --- a/server/controllers/system/add-api-key.php +++ b/server/controllers/system/add-api-key.php @@ -14,7 +14,7 @@ use Respect\Validation\Validator as DataValidator; * @apiPermission staff3 * * @apiParam {String} name Name of the new APIKey. - * @apiParam {String} type Type of APIKey: "REGSITRATION" or "TICKET_CREATE" + * @apiParam {String} type Type of APIKey: "REGISTRATION" or "TICKET_CREATE" * * @apiUse NO_PERMISSION * @apiUse INVALID_NAME From 9ca6632ee9ce234d5656a83bc275bc3adefa6379 Mon Sep 17 00:00:00 2001 From: Ivan Diaz Date: Tue, 7 Jan 2020 15:09:06 -0300 Subject: [PATCH 29/37] Fix APIKey mock --- server/tests/__mocks__/APIKeyMock.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/tests/__mocks__/APIKeyMock.php b/server/tests/__mocks__/APIKeyMock.php index 7ef32d77..70f5d0d8 100755 --- a/server/tests/__mocks__/APIKeyMock.php +++ b/server/tests/__mocks__/APIKeyMock.php @@ -2,6 +2,10 @@ include_once 'tests/__mocks__/NullDataStoreMock.php'; class APIKey extends \Mock { + const REGISTRATION = 'REGISTRATION'; + const TICKET_CREATE = 'TICKET_CREATE'; + const TYPES = [APIKey::REGISTRATION, APIKey::TICKET_CREATE]; + public static $functionList = array(); public static function initStubs() { From eb9a9703532dc9000312acdbf59973d544176a4c Mon Sep 17 00:00:00 2001 From: Guillermo Date: Tue, 7 Jan 2020 17:11:52 -0300 Subject: [PATCH 30/37] Edit ticket title feature --- client/src/app-components/activity-row.js | 4 + client/src/app-components/ticket-viewer.js | 74 +++- client/src/app-components/ticket-viewer.scss | 36 ++ client/src/data/languages/en.js | 436 ------------------- server/controllers/ticket/edit-comment.php | 2 +- server/models/Ticket.php | 3 +- tests/system/disable-user-system.rb | 6 +- tests/ticket/edit-comment.rb | 2 +- tests/ticket/edit-title.rb | 16 +- 9 files changed, 117 insertions(+), 462 deletions(-) delete mode 100644 client/src/data/languages/en.js diff --git a/client/src/app-components/activity-row.js b/client/src/app-components/activity-row.js index 1b3da113..ef029498 100644 --- a/client/src/app-components/activity-row.js +++ b/client/src/app-components/activity-row.js @@ -19,6 +19,7 @@ class ActivityRow extends React.Component { 'RE_OPEN', 'DEPARTMENT_CHANGED', 'PRIORITY_CHANGED', + 'EDIT_TITLE', 'EDIT_COMMENT', 'EDIT_SETTINGS', @@ -60,6 +61,8 @@ class ActivityRow extends React.Component { 'DEPARTMENT_CHANGED', 'PRIORITY_CHANGED', 'COMMENT_EDITED', + 'EDIT_TITLE', + 'EDIT_COMMENT', ]; return ( @@ -113,6 +116,7 @@ class ActivityRow extends React.Component { 'RE_OPEN': 'unlock-alt', 'DEPARTMENT_CHANGED': 'exchange', 'PRIORITY_CHANGED': 'exclamation', + 'EDIT_TITLE': 'edit', 'EDIT_COMMENT': 'edit', 'EDIT_SETTINGS': 'wrench', diff --git a/client/src/app-components/ticket-viewer.js b/client/src/app-components/ticket-viewer.js index dac48eda..162725d8 100644 --- a/client/src/app-components/ticket-viewer.js +++ b/client/src/app-components/ticket-viewer.js @@ -23,6 +23,7 @@ import InfoTooltip from 'core-components/info-tooltip'; import DepartmentDropdown from 'app-components/department-dropdown'; import TagSelector from 'core-components/tag-selector'; import Tag from 'core-components/tag'; +import Input from 'core-components/input'; class TicketViewer extends React.Component { static propTypes = { @@ -58,7 +59,10 @@ class TicketViewer extends React.Component { commentEdited: false, commentPrivate: false, edit: false, - editId: 0 + editTitle: false, + editId: 0, + newTitle: this.props.ticket.title, + editTitleError: false }; componentDidMount() { @@ -71,16 +75,7 @@ class TicketViewer extends React.Component { const ticket = this.props.ticket; return (
-
- #{ticket.ticketNumber} - { false ? {ticket.title} : this.editTitle()} - - - - - - -
+ {this.state.editTitle ? this.renderEditableTitle() : this.renderTitleHeader()} {this.props.editable ? this.renderEditableHeaders() : this.renderHeaders()}
); } - - editTitle(){ + renderTitleHeader() { return( - -
- - -
+
+ #{this.props.ticket.ticketNumber} + {this.props.ticket.title} + + + + {((this.props.ticket.author.id == this.props.userId && this.props.ticket.author.staff == this.props.userStaff) || this.props.userStaff) ? this.renderEditTitleOption() : null} + {this.props.ticket.editedTitle ? this.renderEditedTitleText() : null } +
+ ) + } + renderEditedTitleText(){ + return( +
{i18n('TITLE_EDITED')}
+ ) + } + renderEditTitleOption() { + return( + + {this.setState({editTitle: true})}} /> + + ) + } + + renderEditableTitle(){ + return( +
+
+ {this.setState({newTitle: e.target.value })}} /> +
+ +
) } @@ -404,6 +427,20 @@ class TicketViewer extends React.Component { AreYouSure.openModal(null, this.deleteTicket.bind(this)); } + changeTitle(){ + API.call({ + path: '/ticket/edit-title', + data: { + ticketNumber: this.props.ticket.ticketNumber, + title: this.state.newTitle + } + }).then(() => { + this.setState({editTitle: false,editTitleError: false}) ;this.onTicketModification(); + }).catch((result) => { + this.setState({editTitleError: i18n(result.message)} ) + }); + } + reopenTicket() { API.call({ path: '/ticket/re-open', @@ -617,7 +654,6 @@ class TicketViewer extends React.Component { } export default connect((store) => { - return { userId: store.session.userId, userStaff: store.session.staff, diff --git a/client/src/app-components/ticket-viewer.scss b/client/src/app-components/ticket-viewer.scss index 17de6a8c..87b2f6fe 100644 --- a/client/src/app-components/ticket-viewer.scss +++ b/client/src/app-components/ticket-viewer.scss @@ -9,6 +9,42 @@ color: white; font-size: 16px; padding: 6px 0; + display: flex; + align-items:center; + justify-content:center; + position: relative; + &:hover { + .ticket-viewer__edit-title-icon { + color: $grey; + } + } + } + + &__edited-title-text { + font-style: italic; + font-size: 14px; + margin-left: 10px; + } + + &__edit-title-icon { + position: absolute; + color: #414A59; + right: 12px; + &:hover { + cursor:pointer; + } + } + + &___input-edit-title { + color: black; + align-items:center; + justify-content: center; + margin-bottom: 6px; + margin-right: 6px; + + .input__text { + height: 25px; + } } &__number { diff --git a/client/src/data/languages/en.js b/client/src/data/languages/en.js deleted file mode 100644 index 7816e9c3..00000000 --- a/client/src/data/languages/en.js +++ /dev/null @@ -1,436 +0,0 @@ -export default { - 'WELCOME': 'Welcome', - 'TICKETS': 'Tickets', - 'ARTICLES': 'Articles', - 'ACCOUNT': 'Account', - 'SUBMIT': 'Submit', - 'EMAIL': 'Email', - 'PASSWORD': 'Password', - 'REPEAT_PASSWORD': 'Repeat password', - 'LOG_IN': 'Log in', - 'SIGN_UP': 'Sign up', - 'FORGOT_PASSWORD': 'Forgot your password?', - 'RECOVER_PASSWORD': 'Recover Password', - 'SET_UP_PASSWORD': 'Set up your password', - 'RECOVER_SENT': 'An email with recover instructions has been sent.', - 'NEW_EMAIL': 'New email', - 'FULL_NAME': 'Full name', - 'OLD_PASSWORD': 'Old password', - 'NEW_PASSWORD': 'New password', - 'REPEAT_NEW_PASSWORD': 'Repeat new password', - 'BACK_LOGIN_FORM': 'Back to login form', - 'VIEW_ARTICLES': 'View Articles', - 'EDIT_PROFILE': 'Edit Profile', - 'CLOSE_SESSION': 'Close session', - 'CREATE_TICKET': 'Create Ticket', - 'TICKET_LIST': 'Ticket List', - 'SUPPORT_CENTER': 'Support Center', - 'DEPARTMENT': 'Department', - 'AUTHOR': 'Author', - 'DATE': 'Date', - 'RESPOND': 'Respond', - 'RESPOND_TICKET': 'Respond Ticket', - 'CLOSE_TICKET': 'Close ticket', - 'DELETE_TICKET': 'Delete ticket', - 'NO_ATTACHMENT': 'No file attachment', - 'STAFF': 'Staff', - 'CUSTOMER': 'Customer', - 'YES': 'Yes', - 'NO': 'No', - 'CANCEL': 'Cancel', - 'MY_ACCOUNT': 'My Account', - 'DASHBOARD': 'Dashboard', - 'USERS': 'Users', - 'SETTINGS': 'Settings', - 'STATISTICS': 'Statistics', - 'LAST_ACTIVITY': 'Last Activity', - 'MY_TICKETS': 'My Tickets', - 'NEW_TICKETS': 'New Tickets', - 'ALL_TICKETS': 'All Tickets', - 'CUSTOM_RESPONSES': 'Custom Responses', - 'CUSTOM_TAGS': 'Custom Tags', - 'LIST_USERS': 'List Users', - 'BAN_USERS': 'Ban Users', - 'LIST_ARTICLES': 'Article List', - 'STAFF_MEMBERS': 'Staff Members', - 'DEPARTMENTS': 'Departments', - 'SYSTEM_PREFERENCES': 'System Preferences', - 'ADVANCED_SETTINGS': 'Advanced Settings', - 'EMAIL_TEMPLATES': 'Email Templates', - 'FILTERS_CUSTOM_FIELDS': 'Filters and Custom Fields', - 'PRIORITY': 'Priority', - 'NUMBER': 'Number', - 'HIGH': 'High', - 'MEDIUM': 'Medium', - 'LOW': 'Low', - 'TITLE': 'Title', - 'CONTENT': 'Content', - 'SAVE': 'Save', - 'DISCARD_CHANGES': 'Discard changes', - 'DELETE': 'Delete', - 'LANGUAGE': 'Language', - 'OWNER': 'Owner', - 'OWNED': 'Owned', - 'STATUS': 'Status', - 'NONE': 'None', - 'OPENED': 'Opened', - 'CLOSED': 'Closed', - 'CLOSE': 'Close', - 'RE_OPEN': 'Re open', - 'ASSIGN_TO_ME': 'Assign to me', - 'UN_ASSIGN': 'Unassign', - 'VIEW_TICKET': 'View Ticket', - 'VIEW_TICKET_DESCRIPTION': 'Check the status of your ticket using your ticket number and email.', - 'SELECT_CUSTOM_RESPONSE': 'Select a custom response...', - 'WARNING': 'Warning', - 'INFO': 'Information', - 'ALL_DEPARTMENTS': 'All Departments', - 'EMAIL_BANNED': 'Email banned', - 'UN_BAN': 'Disable ban', - 'BAN_NEW_EMAIL': 'Ban new email', - 'BAN_EMAIL': 'Ban email', - 'EDIT_EMAIL': 'Edit email', - 'EDIT_PASSWORD': 'Edit password', - 'CHANGE_EMAIL': 'Change email', - 'CHANGE_PASSWORD': 'Change password', - 'NAME': 'Name', - 'SEARCH': 'Search', - 'SIGNUP_DATE': 'Sign up date', - 'SEARCH_USERS': 'Search users...', - 'SEARCH_EMAIL': 'Search email...', - 'USER_VIEW_TITLE': 'User #{userId}', - 'EDIT_TOPIC': 'Edit Topic', - 'ADD_TOPIC': 'Add Topic', - 'ICON': 'Icon', - 'COLOR': 'Color', - 'ADD_NEW_ARTICLE': 'Add new article', - 'ADD_ARTICLE': 'Add article', - 'LAST_EDITED_IN': 'Last edited in {date}', - 'EDIT': 'Edit', - 'ADD_CUSTOM_TAG': 'Add custom tag', - 'EDIT_CUSTOM_TAG': 'Edit custom tag', - 'NO_RESULTS': 'No results', - 'DELETE_AND_BAN': 'Delete and ban', - 'STAFF_LEVEL': 'Staff Level', - 'ASSIGNED': 'Assigned', - 'ASSIGNED_TICKETS': '{tickets} assigned tickets', - 'CLOSED_TICKETS': '{tickets} closed tickets', - 'LAST_LOGIN': 'Last login', - 'ADD_NEW_STAFF': 'Add new staff', - 'ADD_STAFF': 'Add staff', - 'LEVEL': 'Level', - 'LEVEL_1': 'Level 1 (Tickets)', - 'LEVEL_2': 'Level 2 (Tickets + Articles)', - 'LEVEL_3': 'Level 3 (Tickets + Articles + Staff)', - 'LEVEL_1_DESCRIPTION': 'can only respond tickets and manage users.', - 'LEVEL_2_DESCRIPTION': 'can do every Level 1 does, can create or edit articles and it can create custom responses.', - 'LEVEL_3_DESCRIPTION': 'can do every Level 2 does, can create or edit staff members and can manage the whole system.', - 'UPDATE_EMAIL': 'Update email', - 'UPDATE_PASSWORD': 'Update password', - 'UPDATE_LEVEL': 'Update level', - 'UPDATE_DEPARTMENTS': 'Update departments', - 'EDIT_STAFF': 'Edit staff member', - 'ADD_DEPARTMENT': 'Add department', - 'UPDATE_DEPARTMENT': 'Update department', - 'TRANSFER_TICKETS_TO': 'Transfer tickets to', - 'COMMENTS': 'Comments', - 'DELETE_STAFF_MEMBER': 'Delete staff member', - 'MAINTENANCE_MODE': 'Maintenance mode', - 'MAINTENANCE_MODE_INFO': 'It will temporary disable the system for regular users.', - 'RECOVER_DEFAULT': 'Recover default', - 'SUPPORT_CENTER_URL': 'Support Center URL', - 'SUPPORT_CENTER_TITLE': 'Support Center Title', - 'SUPPORT_CENTER_LAYOUT': 'Support Center Layout', - 'DEFAULT_TIMEZONE': 'Default Timezone (GMT)', - 'NOREPLY_EMAIL': 'Noreply Email', - 'SMTP_USER': 'SMTP User', - 'SMTP_SERVER': 'SMTP Server', - 'SMTP_PASSWORD': 'SMTP Password', - 'IMAP_USER': 'IMAP User', - 'IMAP_SERVER': 'IMAP Server', - 'IMAP_PASSWORD': 'IMAP Password', - 'IMAP_TOKEN': 'IMAP Token', - 'IMAP_TOKEN_DESCRIPTION': 'Use this token to authenticate the polling request.', - 'PORT': 'Port', - 'RECAPTCHA_PUBLIC_KEY': 'Recaptcha Public Key', - 'RECAPTCHA_PRIVATE_KEY': 'Recaptcha Private Key', - 'ALLOW_FILE_ATTACHMENTS': 'Allow file attachments', - 'MAX_SIZE_MB': 'Max Size (MB)', - 'UPDATE_SETTINGS': 'Update settings', - 'DEFAULT_LANGUAGE': 'Default Language', - 'SUPPORTED_LANGUAGES': 'Supported Languages', - 'SUPPORTED_LANGUAGES_INFO': 'Supported languages are the languages that tickets can be written in.', - 'ALLOWED_LANGUAGES': 'Allowed Languages', - 'ALLOWED_LANGUAGES_INFO': 'Allowed languages are the languages that can be used by an user.', - 'SETTINGS_UPDATED': 'Settings have been updated', - 'ON': 'On', - 'OFF': 'Off', - 'BOXED': 'Boxed', - 'FULL_WIDTH': 'Full width', - 'LOAD_MORE': 'Load More', - 'MY_NOTIFICATIONS': 'My notifications', - 'ALL_NOTIFICATIONS': 'All notifications', - 'VERIFY_SUCCESS': 'User verified', - 'VERIFY_FAILED': 'Could not verify', - 'ENABLE_USER_SYSTEM': 'Use user system for customers', - 'ENABLE_USER_REGISTRATION': 'Enable user registration', - 'INCLUDE_USERS_VIA_CSV': 'Include users via CSV file', - 'BACKUP_DATABASE': 'Backup database', - 'DELETE_ALL_USERS': 'Delete all users', - 'PLEASE_CONFIRM_PASSWORD': 'Please confirm your password to make these changes', - 'REGISTRATION_API_KEYS': 'Registration API keys', - 'NAME_OF_KEY': 'Name of key', - 'KEY': 'Key', - 'ADD_API_KEY': 'Add API Key', - 'NO_KEY_SELECTED': 'No Key selected', - 'CHECK_TICKET': 'Check Ticket', - 'ACTIVITY': 'Activity', - 'HOME': 'Home', - 'TICKET_NUMBER': 'Ticket number', - 'NEXT': 'Next', - 'SUBJECT': 'Subject', - 'SEND_EMAIL_ON_NEW_TICKET': 'Send email on new ticket', - 'STAFF_UPDATED': 'Staff member has been updated', - 'UPDATE': 'Update', - 'NEVER': 'Never', - 'HIMSELF': 'himself', - 'ADD_USER': 'Add user', - 'INVITE_USER': 'Invite user', - 'INVITE_STAFF': 'Invite staff', - 'UPLOAD_FILE': 'Upload file', - 'PRIVATE': 'Private', - 'ENABLE_USER': 'Enable User', - 'DISABLE_USER': 'Disable User', - 'SHOW_CLOSED_TICKETS': 'Show Closed Tickets', - 'IMAGE_HEADER_URL': 'Image header URL', - 'IMAGE_HEADER_DESCRIPTION': 'Image that will be used as header of the email', - 'EMAIL_SETTINGS': 'Email Settings', - 'ADDITIONAL_FIELDS': 'Additonal Fields', - 'NEW_CUSTOM_FIELD': 'New Custom field', - 'TYPE': 'Type', - 'SELECT_INPUT': 'Select input', - 'TEXT_INPUT': 'Text input', - 'OPTION': 'Option {index}', - 'OPTIONS': 'Options', - 'FIELD_DESCRIPTION': 'Field description (Optional)', - 'DESCRIPTION_ADD_CUSTOM_TAG': 'here you can add a new custom tag', - 'DESCRIPTION_EDIT_CUSTOM_TAG': 'here you can edit a custom tag', - 'CUSTOM_FIELDS': 'Custom fields', - - 'CHART_CREATE_TICKET': 'Tickets created', - 'CHART_CLOSE': 'Tickets closed', - 'CHART_SIGNUP': 'Signups', - 'CHART_COMMENT': 'Replies', - 'CHART_ASSIGN': 'Assigned', - - //ACTIVITIES - 'ACTIVITY_COMMENT': 'commented ticket', - 'ACTIVITY_ASSIGN': 'assigned ticket', - 'ACTIVITY_UN_ASSIGN': 'unassigned ticket', - 'ACTIVITY_CLOSE': 'closed ticket', - 'ACTIVITY_CREATE_TICKET': 'created ticket', - 'ACTIVITY_RE_OPEN': 'reopened ticket', - 'ACTIVITY_DEPARTMENT_CHANGED': 'changed department of ticket', - 'ACTIVITY_PRIORITY_CHANGED': 'changed priority of ticket', - 'ACTIVITY_EDIT_COMMENT': 'edited a comment of ticket', - - 'ACTIVITY_EDIT_SETTINGS': 'edited settings', - 'ACTIVITY_SIGNUP': 'signed up', - 'ACTIVITY_INVITE': 'invited user', - 'ACTIVITY_ADD_TOPIC': 'added topic', - 'ACTIVITY_ADD_ARTICLE': 'added article', - 'ACTIVITY_DELETE_TOPIC': 'deleted topic', - 'ACTIVITY_DELETE_ARTICLE': 'deleted article', - 'ACTIVITY_EDIT_ARTICLE': 'edited article', - 'ACTIVITY_ADD_STAFF': 'added staff', - 'ACTIVITY_ADD_DEPARTMENT': 'added department', - 'ACTIVITY_DELETE_DEPARTMENT': 'deleted department', - 'ACTIVITY_EDIT_DEPARTMENT': 'edited department', - 'ACTIVITY_ADD_CUSTOM_RESPONSE': 'added custom response', - 'ACTIVITY_DELETE_CUSTOM_RESPONSE': 'deleted custom response', - 'ACTIVITY_EDIT_CUSTOM_RESPONSE': 'edited custom response', - 'ACTIVITY_BAN_USER': 'banned user', - 'ACTIVITY_DELETE_USER': 'deleted user', - 'ACTIVITY_UN_BAN_USER': 'unbanned user', - - 'SERVER_REQUIREMENTS': 'Server requirements', - 'DATABASE_CONFIGURATION': 'Database configuration', - 'ADMIN_SETUP': 'Admin setup', - 'COMPLETED': 'Completed', - 'INSTALL_HEADER_TITLE': 'OpenSupports Installation Wizard', - 'INSTALL_HEADER_DESCRIPTION': 'This wizard will help you to configure and install OpenSupports on your website', - 'SELECT_LANGUAGE': 'Select language', - 'REQUIREMENT': 'Requirement', - 'VALUE': 'Value', - 'REFRESH': 'Refresh', - 'USER_SYSTEM': 'User System', - 'PREVIOUS': 'Previous', - 'DATABASE_HOST': 'MySQL server', - 'DATABASE_PORT': 'MySQL server port', - 'DATABASE_NAME': 'MySQL database name', - 'DATABASE_USER': 'MySQL user', - 'DATABASE_PASSWORD': 'MySQL password', - 'ADMIN_NAME': 'Admin account name', - 'ADMIN_EMAIL': 'Admin account email', - 'ADMIN_PASSWORD': 'Admin account password', - 'ADMIN_PASSWORD_DESCRIPTION': 'Please remember this password. It is needed for accessing the admin panel. You can change it later.', - 'INSTALLATION_COMPLETED': 'Installation completed.', - 'INSTALLATION_COMPLETED_DESCRIPTION': 'The installation of OpenSupports is completed. Redirecting to admin panel...', - - 'STEP_TITLE': 'Step {current} of {total} - {title}', - 'STEP_1_DESCRIPTION': 'Select your preferred language for the installation wizard.', - 'STEP_2_DESCRIPTION': 'Here are listed the requirements for running OpenSupports. Please make sure that all requirements are satisfied.', - 'STEP_3_DESCRIPTION': 'Please fill the MySQL database configuration.', - 'STEP_4_DESCRIPTION': 'Please select your user system preferences.', - 'STEP_5_DESCRIPTION': 'Please fill your general system preferences.', - 'STEP_6_DESCRIPTION': 'Please configure the administrator account.', - 'STEP_7_DESCRIPTION': 'Installation is completed.', - - //VIEW DESCRIPTIONS - 'CREATE_TICKET_DESCRIPTION': 'This is a form for creating tickets. Fill the form and send us your issues/doubts/suggestions. Our support system will answer it as soon as possible.', - 'TICKET_LIST_DESCRIPTION': 'Here you can find a list of all tickets you have sent to our support team.', - 'TICKETS_DESCRIPTION': 'Send ticket through our support center and get response of your doubts, suggestions and issues.', - 'ARTICLES_DESCRIPTION': 'Take a look to our articles about common issues, guides and documentation.', - 'ACCOUNT_DESCRIPTION': 'All your tickets are stored in your account\'s profile. Keep track of all your tickets you send to our staff team.', - 'SUPPORT_CENTER_DESCRIPTION': 'Welcome to our support center. You can contact us through a tickets system. Your tickets will be answered by our staff.', - 'CUSTOM_RESPONSES_DESCRIPTION': 'Custom responses are automated responses for common problems', - 'CUSTOM_TAGS_DESCRIPTION': 'Here you can view manage the custom tags for tickets to identify them better', - 'MY_TICKETS_DESCRIPTION': 'Here you can view the tickets you are responsible for.', - 'NEW_TICKETS_DESCRIPTION': 'Here you can view all the new tickets that are not assigned by anyone.', - 'ALL_TICKETS_DESCRIPTION': 'Here you can view the tickets of the departments you are assigned.', - 'SEARCH_TICKETS_DESCRIPTION': 'Here you can search tickets by specific filters', - 'TICKET_VIEW_DESCRIPTION': 'This ticket has been sent by a customer. Here you can respond or assign the ticket', - 'BAN_USERS_DESCRIPTION': 'Here you can see a list of banned emails, you can un-ban them or add more emails to the list.', - 'LIST_USERS_DESCRIPTION': 'This is the list of users that are registered in this platform. You can search for someone in particular, delete it or ban it.', - 'USER_VIEW_DESCRIPTION': 'Here you can find all the information about an user and all the tickets sent by the user. You can also delete or ban it.', - 'DELETE_USER_DESCRIPTION': 'The user will not be able to log in aging and all its tickets will be erased. Also, the email can not be used any more.', - 'DELETE_TOPIC_DESCRIPTION': 'By deleting the topic, all articles on it will be erased.', - 'EDIT_TOPIC_DESCRIPTION': 'Here you can change the name, the icon and the icon color of the topic.', - 'ADD_ARTICLE_DESCRIPTION': 'Here you can add an article that will be available for every user. It will be added inside the category {category}.', - 'LIST_ARTICLES_DESCRIPTION': 'This is a list of articles that includes information about our services.', - 'ADD_TOPIC_DESCRIPTION': 'Here you can add a topic that works as a category for articles.', - 'DELETE_ARTICLE_DESCRIPTION': 'You\'re going to delete this article forever.', - 'STAFF_MEMBERS_DESCRIPTION': 'Here you can see who are your staff members.', - 'ADD_STAFF_DESCRIPTION': 'Here you can add staff members to your teams.', - 'EDIT_STAFF_DESCRIPTION': 'Here you can edit information about a staff member.', - 'MY_ACCOUNT_DESCRIPTION': 'Here you can edit information about you.', - 'DEPARTMENTS_DESCRIPTION': 'A department is a group where the tickets can go. They are used to categorize the tickets. You can assign them to other staff members.', - 'MAINTENANCE_MODE_DESCRIPTION': 'The support system is in maintenance mode, thus unavailable at the moment. We will come back as soon as possible.', - 'EMAIL_TEMPLATES_DESCRIPTION': 'Here you can edit the templates of the emails that will be sent to users. Remember that the double brackets curly braces indicate a variable value. For example, \'name\' represents the user\'s name.', - 'SYSTEM_PREFERENCES_DESCRIPTION': 'Here you can edit the preferences of the system.', - 'VERIFY_SUCCESS_DESCRIPTION': 'You user has been verified correctly. You can log in now.', - 'VERIFY_FAILED_DESCRIPTION': 'The verification could not be done.', - 'STATISTICS_DESCRIPTION': 'Here you can view statistics related to tickets and signups.', - 'ADVANCED_SETTINGS_DESCRIPTION': 'Here you can change the advanced settings of your system. Please be careful, the changes you make can not be reversed.', - 'USER_SYSTEM_DISABLED': 'User system has been disabled', - 'USER_SYSTEM_ENABLED': 'User system has been enabled', - 'REGISTRATION_DISABLED': 'Registration has been disabled', - 'REGISTRATION_ENABLED': 'Registration has been enabled', - 'ADD_API_KEY_DESCRIPTION': 'Insert the name and a registration api key will be generated.', - 'SIGN_UP_VIEW_DESCRIPTION': 'Here you can create an account for our support center. It is required to send tickets and see documentation.', - 'EDIT_PROFILE_VIEW_DESCRIPTION': 'Here you can edit your user by changing your email or your password.', - 'ENABLE_USER_SYSTEM_DESCRIPTION': 'Enable/disable the use of an user system. If you disable it, all users will be deleted but the tickets will be kept. If you enable it, the users of existent tickets will be created.', - 'CSV_DESCRIPTION': 'The CSV file must have 3 columns: email, password, name. There is no limit in row count. It will be created one user per row in the file.', - 'SMTP_SERVER_DESCRIPTION': 'The configuration of the SMTP server allows the application to send mails. If you do not configure it, no emails will be sent by OpenSupports.', - 'IMAP_SERVER_DESCRIPTION': 'The configuration of the IMAP server allows the application to create tickets from the emails sent to a mailbox.', - 'ENABLE_USER_DESCRIPTION': 'This action allows the user to sign in and create tickets.', - 'DISABLE_USER_DESCRIPTION': 'User will be disabled and will not be able to sign in and create tickets.', - 'PRIVATE_RESPONSE_DESCRIPTION': 'This response will only be seen by staff members', - 'PRIVATE_TOPIC_DESCRIPTION': 'This topic will only be seen by staff members', - 'PRIVATE_DEPARTMENT_DESCRIPTION': 'This department will only be seen by staff members', - 'EMAIL_SETTINGS_DESCRIPTION': 'Here you can edit the settings for receiving and sending email to your customers.', - 'IMAP_POLLING_DESCRIPTION': 'Inbox checking will not be done automatically by OpenSupports. You have to make POST requests periodically to this url to process the emails: {url}', - 'NEW_CUSTOM_FIELD_DESCRIPTION': 'Here you can create a custom field for an user, it can be a blank text box or a fixed set of options.', - 'CUSTOM_FIELDS_DESCRIPTION': 'Custom fields are defined additional fields the users are able to fill to provide more information about them.', - 'INVITE_USER_VIEW_DESCRIPTION': 'Here you can invite an user to join our support system, he will just need to provide his password to create a new user.', - 'INVITE_STAFF_DESCRIPTION': 'Here you can invite staff members to your teams.', - - //ERRORS - 'EMAIL_OR_PASSWORD': 'Email or password invalid', - 'EMAIL_NOT_EXIST': 'Email does not exist', - 'ERROR_EMPTY': 'Invalid value', - 'ERROR_PASSWORD': 'Invalid password', - 'ERROR_NAME': 'Invalid name', - 'ERROR_TITLE': 'Invalid title', - 'ERROR_EMAIL': 'Invalid email', - 'ERROR_CONTENT_SHORT': 'Content too short', - 'PASSWORD_NOT_MATCH': 'Password does not match', - 'INVALID_RECOVER': 'Invalid recover data', - 'TICKET_SENT_ERROR': 'An error occurred while trying to create the ticket.', - 'TICKET_COMMENT_ERROR': 'An error occurred while trying to add the comment.', - 'NO_PERMISSION': 'You\'ve no permission to access to this page.', - 'INVALID_USER': 'User id is invalid', - 'ERROR_RETRIEVING_TICKETS': 'An error occurred while trying to retrieve tickets.', - 'ERROR_RETRIEVING_USERS': 'An error occurred while trying to retrieve users.', - 'ERROR_RETRIEVING_BAN_LIST': 'An error occurred while trying to retrieve the list of banned emails.', - 'ERROR_BANNING_EMAIL': 'An error occurred while trying to ban the email.', - 'ERROR_RETRIEVING_ARTICLES': 'An error occurred while trying to retrieve articles.', - 'ERROR_LIST': 'Select at least one', - 'ERROR_URL': 'Invalid URL', - 'UNVERIFIED_EMAIL': 'Email is not verified yet', - 'ERROR_UPDATING_SETTINGS': 'An error occurred while trying to update settings', - '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', - 'USER_DISABLED': 'This account is disabled.', - 'INVALID_SYNTAX': 'Invalid syntax.', - 'DEPARTMENT_PRIVATE_TICKETS': 'This department has tickets created by non-staff and it can not be private', - 'CURRENTLY_UNAVAILABLE': 'Currently unavailable', - - //MESSAGES - 'SIGNUP_SUCCESS': 'You have registered successfully in our support system.', - 'INVITE_USER_SUCCESS': 'You have invited a new user successfully in our support system', - 'TICKET_SENT': 'Ticket has been created successfully.', - 'VALID_RECOVER': 'Password recovered successfully', - 'EMAIL_EXISTS': 'Email already exists', - 'ARE_YOU_SURE': 'Confirm action', - 'EMAIL_WILL_CHANGE': 'The current email will be changed', - 'PASSWORD_WILL_CHANGE': 'The current password will be changed', - 'EMAIL_CHANGED': 'Email has been changed successfully', - 'PASSWORD_CHANGED': 'Password has been changed successfully', - 'OLD_PASSWORD_INCORRECT': 'Old password is incorrect', - 'WILL_LOSE_CHANGES': 'You haven\'t save. Your changes will be lost.', - 'WILL_DELETE_CUSTOM_RESPONSE': 'The custom response will be deleted.', - 'WILL_DELETE_DEPARTMENT': 'The department will be deleted. All the tickets will be transfer to the department selected.', - 'NO_STAFF_ASSIGNED': 'No staff member is assigned to this department.', - 'NO_DEPARTMENT_ASSIGNED': 'No ticket department is assigned you.', - 'LEVEL_UPDATED': 'Level has been updated successfully.', - 'DEPARTMENTS_UPDATED': 'Departments have been updated successfully.', - 'FAILED_EDIT_STAFF': 'An error occurred while trying to edit staff member.', - 'EMAIL_BANNED_SUCCESSFULLY': 'Email has been banned successfully', - 'WILL_DELETE_STAFF': 'This staff member will be deleted and all its tickets will be unassigned.', - 'WILL_RECOVER_EMAIL_TEMPLATE': 'This email template will be recover to it\'s default value on this language.', - 'SUCCESS_IMPORTING_CSV_DESCRIPTION': 'CSV File has been imported successfully', - 'SUCCESS_DELETING_ALL_USERS': 'Users have beend deleted successfully', - 'SUCCESSFUL_CONNECTION': 'Successful connection', - 'UNSUCCESSFUL_CONNECTION': 'Unsuccessful connection', - 'SERVER_CREDENTIALS_WORKING': 'Server credentials are working correctly', - 'DELETE_CUSTOM_FIELD_SURE': 'Some users may be using this field. Are you sure you want to delete it?', - - 'COMMENT_EDITED': '(comment edited)', - 'LAST_7_DAYS': 'Last 7 days', - 'LAST_30_DAYS': 'Last 30 days', - 'LAST_90_DAYS': 'Last 90 days', - 'LAST_365_DAYS': 'Last 365 days', - - 'TEST': 'Test', - 'ACTIVITY_COMMENT_THIS': 'commented this ticket', - 'ACTIVITY_ASSIGN_THIS': 'assigned this ticket to', - 'ACTIVITY_UN_ASSIGN_THIS': 'unassigned this ticket to', - 'ACTIVITY_CLOSE_THIS': 'closed this ticket', - 'ACTIVITY_CREATE_TICKET_THIS': 'created this ticket', - 'ACTIVITY_RE_OPEN_THIS': 'reopened this ticket', - 'ACTIVITY_DEPARTMENT_CHANGED_THIS': 'changed department of this ticket to ', - 'ACTIVITY_PRIORITY_CHANGED_THIS': 'changed priority of this ticket to', - 'DATE_PREFIX': 'on', - 'LEFT_EMPTY_DATABASE': 'Leave empty for automatic database creation', - 'DEFAULT_PORT': 'Leave empty for 3306 as default', - 'REMEMBER_ME': 'Remember me', - 'EMAIL_LOWERCASE': 'email', - 'PASSWORD_LOWERCASE': 'password', - 'TEST_SMTP_CONNECTION': 'Test SMTP connection', - 'SERVER_ERROR': 'Can not connect to server.', - 'EMAIL_SERVER_ADDRESS': 'Email server address', - 'EMAIL_SERVER_ADDRESS_DESCRIPTION': 'Address where mails will be received and sent' -}; diff --git a/server/controllers/ticket/edit-comment.php b/server/controllers/ticket/edit-comment.php index faa76539..84a980ca 100644 --- a/server/controllers/ticket/edit-comment.php +++ b/server/controllers/ticket/edit-comment.php @@ -38,7 +38,7 @@ class EditCommentController extends Controller { 'error' => ERRORS::INVALID_CONTENT ], 'ticketNumber' => [ - 'validation' => DataValidator::validTicketNumber(), + 'validation' => DataValidator::oneOf(DataValidator::validTicketNumber(),DataValidator::nullType()), 'error' => ERRORS::INVALID_TICKET ] ] diff --git a/server/models/Ticket.php b/server/models/Ticket.php index c820c270..991487fc 100755 --- a/server/models/Ticket.php +++ b/server/models/Ticket.php @@ -133,7 +133,8 @@ class Ticket extends DataStore { 'owner' => $this->ownerToArray(), 'events' => $minimized ? [] : $this->eventsToArray(), 'tags' => $this->sharedTagList->toArray(true), - 'edited' => $this->editedContent + 'edited' => $this->editedContent, + 'editedTitle' => $this->editedTitle ]; } diff --git a/tests/system/disable-user-system.rb b/tests/system/disable-user-system.rb index 9ae8dd15..7f3c63e1 100644 --- a/tests/system/disable-user-system.rb +++ b/tests/system/disable-user-system.rb @@ -19,7 +19,7 @@ describe'system/disable-user-system' do numberOftickets= $database.query("SELECT * FROM ticket WHERE author_id IS NULL AND author_email IS NOT NULL AND author_name IS NOT NULL") - (numberOftickets.num_rows).should.equal(51) + (numberOftickets.num_rows).should.equal(52) request('/user/logout') @@ -122,7 +122,7 @@ describe'system/disable-user-system' do (result['status']).should.equal('success') (result['data'].size).should.equal(10) end - + it 'should be able to get system logs as admin' do result = request('/system/get-logs', { page: 1, @@ -205,7 +205,7 @@ describe'system/disable-user-system' do numberOftickets= $database.query("SELECT * FROM ticket WHERE author_email IS NULL AND author_name IS NULL AND author_id IS NOT NULL" ) - (numberOftickets.num_rows).should.equal(53) + (numberOftickets.num_rows).should.equal(54) end it 'should not enable the user system' do diff --git a/tests/ticket/edit-comment.rb b/tests/ticket/edit-comment.rb index 89b8d662..ae1093b9 100644 --- a/tests/ticket/edit-comment.rb +++ b/tests/ticket/edit-comment.rb @@ -15,7 +15,7 @@ describe '/ticket/edit-comment' do }) ticket = $database.getRow('ticket', 'ticket made by an user', 'title') - + (result['status']).should.equal('success') (ticket['content']).should.equal('content edited by the user') end diff --git a/tests/ticket/edit-title.rb b/tests/ticket/edit-title.rb index 095be590..2f1a3242 100644 --- a/tests/ticket/edit-title.rb +++ b/tests/ticket/edit-title.rb @@ -6,6 +6,20 @@ describe '/ticket/edit-title' do ticket = $database.getRow('ticket', 'Valar Morghulis', 'title') ticketNumber = ticket['ticket_number'] + it 'should fail change title of the ticket if the title is invalid' do + result = request('/ticket/edit-title', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + title: '', + ticketNumber: ticket['ticket_number'] + }) + + ticket = $database.getRow('ticket', ticketNumber, 'ticket_number') + + (result['status']).should.equal('fail') + (result['message']).should.equal('INVALID_TITLE') + end + it 'should change title of the ticket if the author user tries it' do result = request('/ticket/edit-title', { csrf_userid: $csrf_userid, @@ -53,7 +67,7 @@ describe '/ticket/edit-title' do csrf_userid: $csrf_userid, csrf_token: $csrf_token, title: 'Casterly Rock', - ticketEventId: ticket['ticket_number'] + ticketNumber: ticket['ticket_number'] }) (result['status']).should.equal('fail') (result['message']).should.equal('NO_PERMISSION') From 7b608a6d06421f06767ff682e69c74c6fc756f76 Mon Sep 17 00:00:00 2001 From: Ivan Diaz Date: Tue, 7 Jan 2020 17:29:28 -0300 Subject: [PATCH 31/37] Fix disable-user-system test --- tests/system/disable-user-system.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/system/disable-user-system.rb b/tests/system/disable-user-system.rb index 05a2e10d..7107ecc3 100644 --- a/tests/system/disable-user-system.rb +++ b/tests/system/disable-user-system.rb @@ -220,7 +220,7 @@ describe'system/disable-user-system' do numberOftickets= $database.query("SELECT * FROM ticket WHERE author_email IS NULL AND author_name IS NULL AND author_id IS NOT NULL" ) - (numberOftickets.num_rows).should.equal(53) + (numberOftickets.num_rows).should.equal(54) end it 'should not enable the user system' do From 943c910181e05a3bfebef7972149dbff2eba2ec4 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Tue, 7 Jan 2020 18:48:59 -0300 Subject: [PATCH 32/37] add minor changes --- client/src/app-components/ticket-viewer.js | 1 - client/src/data/languages/en.js | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/client/src/app-components/ticket-viewer.js b/client/src/app-components/ticket-viewer.js index 2c4ed3b9..c68771db 100644 --- a/client/src/app-components/ticket-viewer.js +++ b/client/src/app-components/ticket-viewer.js @@ -24,7 +24,6 @@ import InfoTooltip from 'core-components/info-tooltip'; import DepartmentDropdown from 'app-components/department-dropdown'; import TagSelector from 'core-components/tag-selector'; import Tag from 'core-components/tag'; -import Input from 'core-components/input'; class TicketViewer extends React.Component { static propTypes = { diff --git a/client/src/data/languages/en.js b/client/src/data/languages/en.js index dc512fd5..92ca10cf 100644 --- a/client/src/data/languages/en.js +++ b/client/src/data/languages/en.js @@ -91,6 +91,7 @@ export default { 'BAN_EMAIL': 'Ban email', 'EDIT_EMAIL': 'Edit email', 'EDIT_PASSWORD': 'Edit password', + 'EDIT_TITLE': 'Edit title', 'CHANGE_EMAIL': 'Change email', 'CHANGE_PASSWORD': 'Change password', 'NAME': 'Name', @@ -408,6 +409,7 @@ export default { 'SERVER_CREDENTIALS_WORKING': 'Server credentials are working correctly', 'DELETE_CUSTOM_FIELD_SURE': 'Some users may be using this field. Are you sure you want to delete it?', + 'TITLE_EDITED': '(title edited)', 'COMMENT_EDITED': '(comment edited)', 'LAST_7_DAYS': 'Last 7 days', 'LAST_30_DAYS': 'Last 30 days', From 0174233a24a513507814b2cb92d8fd76671cffe4 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Wed, 8 Jan 2020 10:09:35 -0300 Subject: [PATCH 33/37] edit title for system without users --- client/src/data/languages/en.js | 3 +- server/controllers/ticket/edit-comment.php | 47 ++++++++++++++++------ server/controllers/ticket/edit-title.php | 47 ++++++++++++++++------ 3 files changed, 70 insertions(+), 27 deletions(-) diff --git a/client/src/data/languages/en.js b/client/src/data/languages/en.js index 92ca10cf..c1839ae1 100644 --- a/client/src/data/languages/en.js +++ b/client/src/data/languages/en.js @@ -234,7 +234,7 @@ export default { 'ACTIVITY_DEPARTMENT_CHANGED': 'changed department of ticket', 'ACTIVITY_PRIORITY_CHANGED': 'changed priority of ticket', 'ACTIVITY_EDIT_COMMENT': 'edited a comment of ticket', - + 'ACTIVITY_EDIT_TITLE': 'edited title of ticket', 'ACTIVITY_EDIT_SETTINGS': 'edited settings', 'ACTIVITY_SIGNUP': 'signed up', 'ACTIVITY_INVITE': 'invited user', @@ -361,6 +361,7 @@ export default { 'TICKET_COMMENT_ERROR': 'An error occurred while trying to add the comment.', 'NO_PERMISSION': 'You\'ve no permission to access to this page.', 'INVALID_USER': 'User id is invalid', + 'INVALID_TITLE': 'invalid title', 'ERROR_RETRIEVING_TICKETS': 'An error occurred while trying to retrieve tickets.', 'ERROR_RETRIEVING_USERS': 'An error occurred while trying to retrieve users.', 'ERROR_RETRIEVING_BAN_LIST': 'An error occurred while trying to retrieve the list of banned emails.', diff --git a/server/controllers/ticket/edit-comment.php b/server/controllers/ticket/edit-comment.php index 84a980ca..a0ffe1bf 100644 --- a/server/controllers/ticket/edit-comment.php +++ b/server/controllers/ticket/edit-comment.php @@ -20,6 +20,7 @@ DataValidator::with('CustomValidations', true); * * @apiUse NO_PERMISSION * @apiUse INVALID_CONTENT + * @apiUse INVALID_TOKEN * * @apiSuccess {Object} data Empty object * @@ -30,19 +31,39 @@ class EditCommentController extends Controller { const METHOD = 'POST'; public function validations() { - return [ - 'permission' => 'user', - 'requestData' => [ - 'content' => [ - 'validation' => DataValidator::length(10, 5000), - 'error' => ERRORS::INVALID_CONTENT - ], - 'ticketNumber' => [ - 'validation' => DataValidator::oneOf(DataValidator::validTicketNumber(),DataValidator::nullType()), - 'error' => ERRORS::INVALID_TICKET + if(Controller::isUserSystemEnabled()){ + return [ + 'permission' => 'user', + 'requestData' => [ + 'content' => [ + 'validation' => DataValidator::length(10, 5000), + 'error' => ERRORS::INVALID_CONTENT + ], + 'ticketNumber' => [ + 'validation' => DataValidator::oneOf(DataValidator::validTicketNumber(),DataValidator::nullType()), + 'error' => ERRORS::INVALID_TICKET + ] ] - ] - ]; + ]; + } else { + return [ + 'permission' => 'any', + 'requestData' => [ + 'content' => [ + 'validation' => DataValidator::length(10, 5000), + 'error' => ERRORS::INVALID_CONTENT + ], + 'ticketNumber' => [ + 'validation' => DataValidator::oneOf(DataValidator::validTicketNumber(),DataValidator::nullType()), + 'error' => ERRORS::INVALID_TICKET + ], + 'csrf_token' => [ + 'validation' => DataValidator::equals(Session::getInstance()->getToken()), + 'error' => ERRORS::INVALID_TOKEN + ] + ] + ]; + } } public function handler() { @@ -53,7 +74,7 @@ class EditCommentController extends Controller { $ticketevent = Ticketevent::getTicketEvent(Controller::request('ticketEventId')); $ticket = Ticket::getByTicketNumber(Controller::request('ticketNumber')); - if(!Controller::isStaffLogged() && ($user->id !== $ticketevent->authorUserId && $user->id !== $ticket->authorId ) ){ + if(Controller::isUserSystemEnabled() && !Controller::isStaffLogged() && ($user->id !== $ticketevent->authorUserId && $user->id !== $ticket->authorId ) ){ throw new RequestException(ERRORS::NO_PERMISSION); } diff --git a/server/controllers/ticket/edit-title.php b/server/controllers/ticket/edit-title.php index 84450141..f407d28d 100644 --- a/server/controllers/ticket/edit-title.php +++ b/server/controllers/ticket/edit-title.php @@ -19,6 +19,7 @@ DataValidator::with('CustomValidations', true); * * @apiUse NO_PERMISSION * @apiUse INVALID_TITLE + * @apiUse INVALID_TOKEN * * @apiSuccess {Object} data Empty object * @@ -29,19 +30,39 @@ class EditTitleController extends Controller { const METHOD = 'POST'; public function validations() { - return [ - 'permission' => 'user', - 'requestData' => [ - 'title' => [ - 'validation' => DataValidator::length(1, 200), - 'error' => ERRORS::INVALID_TITLE - ], - 'ticketNumber' => [ - 'validation' => DataValidator::validTicketNumber(), - 'error' => ERRORS::INVALID_TICKET + if(Controller::isUserSystemEnabled()){ + return [ + 'permission' => 'user', + 'requestData' => [ + 'title' => [ + 'validation' => DataValidator::length(1, 200), + 'error' => ERRORS::INVALID_TITLE + ], + 'ticketNumber' => [ + 'validation' => DataValidator::validTicketNumber(), + 'error' => ERRORS::INVALID_TICKET + ] ] - ] - ]; + ]; + } else { + return [ + 'permission' => 'any', + 'requestData' => [ + 'title' => [ + 'validation' => DataValidator::length(1, 200), + 'error' => ERRORS::INVALID_TITLE + ], + 'ticketNumber' => [ + 'validation' => DataValidator::validTicketNumber(), + 'error' => ERRORS::INVALID_TICKET + ], + 'csrf_token' => [ + 'validation' => DataValidator::equals(Session::getInstance()->getToken()), + 'error' => ERRORS::INVALID_TOKEN + ] + ] + ]; + } } public function handler() { @@ -49,7 +70,7 @@ class EditTitleController extends Controller { $newtitle = Controller::request('title'); $ticket = Ticket::getByTicketNumber(Controller::request('ticketNumber')); - if(!$user->canManageTicket($ticket)) { + if(Controller::isUserSystemEnabled() && !$user->canManageTicket($ticket)) { throw new RequestException(ERRORS::NO_PERMISSION); } From 7c1315c2449741770785ef369db52067d861bd1c Mon Sep 17 00:00:00 2001 From: Guillermo Date: Wed, 8 Jan 2020 10:27:39 -0300 Subject: [PATCH 34/37] fix disable user system ruby test --- tests/system/disable-user-system.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/system/disable-user-system.rb b/tests/system/disable-user-system.rb index a9279051..9550c0df 100644 --- a/tests/system/disable-user-system.rb +++ b/tests/system/disable-user-system.rb @@ -220,7 +220,7 @@ describe'system/disable-user-system' do numberOftickets= $database.query("SELECT * FROM ticket WHERE author_email IS NULL AND author_name IS NULL AND author_id IS NOT NULL" ) - (numberOftickets.num_rows).should.equal(54) + (numberOftickets.num_rows).should.equal(55) end it 'should not enable the user system' do From f87f809b1508baaa696727680e45d67390a5da87 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Wed, 8 Jan 2020 16:16:36 -0300 Subject: [PATCH 35/37] simplification of code --- client/src/app-components/ticket-viewer.js | 28 +++++++++++++++------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/client/src/app-components/ticket-viewer.js b/client/src/app-components/ticket-viewer.js index c68771db..4d0866bf 100644 --- a/client/src/app-components/ticket-viewer.js +++ b/client/src/app-components/ticket-viewer.js @@ -102,15 +102,19 @@ class TicketViewer extends React.Component { ); } renderTitleHeader() { + + const {ticket, userStaff, userId} = this.props; + const {ticketNumber, title, author, editedTitle, language} = ticket; + return(
- #{this.props.ticket.ticketNumber} - {this.props.ticket.title} + #{ticketNumber} + {title} - + - {((this.props.ticket.author.id == this.props.userId && this.props.ticket.author.staff == this.props.userStaff) || this.props.userStaff) ? this.renderEditTitleOption() : null} - {this.props.ticket.editedTitle ? this.renderEditedTitleText() : null } + {((author.id == userId && author.staff == userStaff) || userStaff) ? this.renderEditTitleOption() : null} + {editedTitlee ? this.renderEditedTitleText() : null }
) } @@ -122,7 +126,7 @@ class TicketViewer extends React.Component { renderEditTitleOption() { return( - {this.setState({editTitle: true})}} /> + this.setState({editTitle: true})} /> ) } @@ -131,7 +135,7 @@ class TicketViewer extends React.Component { return(
- {this.setState({newTitle: e.target.value })}} /> + this.setState({newTitle: e.target.value })} />
) } From 8b32d5e86b256dd1bde757657a878994de5da196 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Thu, 9 Jan 2020 12:07:29 -0300 Subject: [PATCH 37/37] . --- client/src/app-components/ticket-viewer.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/src/app-components/ticket-viewer.js b/client/src/app-components/ticket-viewer.js index 082cc284..70c24645 100644 --- a/client/src/app-components/ticket-viewer.js +++ b/client/src/app-components/ticket-viewer.js @@ -101,8 +101,8 @@ class TicketViewer extends React.Component {
); } - renderTitleHeader() { + renderTitleHeader() { const {ticket, userStaff, userId} = this.props; const {ticketNumber, title, author, editedTitle, language} = ticket; @@ -118,11 +118,13 @@ class TicketViewer extends React.Component {
) } + renderEditedTitleText(){ return(
{i18n('TITLE_EDITED')}
) } + renderEditTitleOption() { return(