diff --git a/install/empty_data.php b/install/empty_data.php index c8218e7bbc0..9dc7a49c21f 100644 --- a/install/empty_data.php +++ b/install/empty_data.php @@ -9235,6 +9235,7 @@ public function getEmptyData(): array ], ]; + // allowed_statuses is set using default value, @see install/mysql/glpi-empty.sql ( table `glpi_tickettemplates` ) $tables['glpi_tickettemplates'] = [ [ 'id' => 1, diff --git a/install/migrations/update_10.0.x_to_11.0.0/itilobject_templates.php b/install/migrations/update_10.0.x_to_11.0.0/itilobject_templates.php index a911bf25cff..7a0b272a14f 100644 --- a/install/migrations/update_10.0.x_to_11.0.0/itilobject_templates.php +++ b/install/migrations/update_10.0.x_to_11.0.0/itilobject_templates.php @@ -58,7 +58,7 @@ // Add status_allowed field to all ITIL Object template tables $itiltemplate_tables = [ - 'glpi_tickettemplates' => [1, 2, 3, 4, 5, 6], + 'glpi_tickettemplates' => [1, 10, 2, 3, 4, 5, 6], 'glpi_changetemplates' => [1, 9, 10, 7, 4, 11, 12, 5, 8, 6, 14, 13], 'glpi_problemtemplates' => [1, 7, 2, 3, 4, 5, 8, 6], ]; diff --git a/install/mysql/glpi-empty.sql b/install/mysql/glpi-empty.sql index 4cc7dc0a97d..11c20cccb54 100644 --- a/install/mysql/glpi-empty.sql +++ b/install/mysql/glpi-empty.sql @@ -7588,7 +7588,7 @@ CREATE TABLE `glpi_tickettemplates` ( `entities_id` int unsigned NOT NULL DEFAULT '0', `is_recursive` tinyint NOT NULL DEFAULT '0', `comment` text, - `allowed_statuses` varchar(255) NOT NULL DEFAULT '[1,2,3,4,5,6]', + `allowed_statuses` varchar(255) NOT NULL DEFAULT '[1,10,2,3,4,5,6]', PRIMARY KEY (`id`), KEY `name` (`name`), KEY `entities_id` (`entities_id`), diff --git a/phpunit/functional/PendingReasonTest.php b/phpunit/functional/PendingReasonTest.php index 980e3ccab89..d51e5814eb7 100644 --- a/phpunit/functional/PendingReasonTest.php +++ b/phpunit/functional/PendingReasonTest.php @@ -578,7 +578,7 @@ public function testRemovePendingAndRevertStatus(): void foreach ( [ Ticket::class => CommonITILObject::ASSIGNED, - Change::class => CommonITILObject::EVALUATION, + Change::class => Change::EVALUATION, Problem::class => CommonITILObject::OBSERVED ] as $itemtype => $status ) { diff --git a/phpunit/functional/TicketTest.php b/phpunit/functional/TicketTest.php index 27099ec9115..6e62770f2b0 100644 --- a/phpunit/functional/TicketTest.php +++ b/phpunit/functional/TicketTest.php @@ -1982,7 +1982,16 @@ public function testFormTech() ); } - public function changeTechRights(array $rights) + /** + * Update tech user rights (and relogin to apply these rights) + * + * $rights parameter is an array with the following format: + * key : object type (e.g. ticket) + * value : right (e.g. \Ticket::READNEWTICKET) + * @param array $rights + * @throws \Exception + */ + public function changeTechRights(array $rights): void { global $DB; @@ -6848,6 +6857,23 @@ public function testViewIncomingTicketWithNewTicketRight() $this->assertTrue($ticket->canViewItem()); } + /** + * The right "View new tickets" should not include those with the "approval" status. + */ + public function testUserCannotViewApprovalTicketsWithReadNewTicketRight() + { + $this->login(); + + $ticket = $this->createItem('Ticket', [ + 'name' => __FUNCTION__, + 'content' => __FUNCTION__, + 'status' => CommonITILObject::APPROVAL, + ]); + + $this->changeTechRights(['ticket' => \Ticket::READNEWTICKET]); + $this->assertFalse($ticket->canViewItem()); + } + public function testAssignToMe() { $this->login(); diff --git a/src/Change.php b/src/Change.php index 32dc6488936..f34c072447c 100644 --- a/src/Change.php +++ b/src/Change.php @@ -51,7 +51,7 @@ class Change extends CommonITILObject public $grouplinkclass = 'Change_Group'; public $supplierlinkclass = 'Change_Supplier'; - public static $rightname = 'change'; + public static $rightname = 'change'; protected $usenotepad = true; const MATRIX_FIELD = 'priority_matrix'; @@ -64,8 +64,11 @@ class Change extends CommonITILObject const READALL = 1024; // Specific status for changes - const REFUSED = 13; - const CANCELED = 14; + public const EVALUATION = 9; + public const TEST = 11; + public const QUALIFICATION = 12; + public const REFUSED = 13; + public const CANCELED = 14; public static function getTypeName($nb = 0) { @@ -703,19 +706,11 @@ public static function rawSearchOptionsToAdd(string $itemtype) return $tab; } - - /** - * get the change status list - * To be overridden by class - * - * @param $withmetaforsearch boolean (default false) - * - * @return array - **/ public static function getAllStatusArray($withmetaforsearch = false) { - $tab = [self::INCOMING => _x('status', 'New'), + $tab = [ + self::INCOMING => _x('status', 'New'), self::EVALUATION => __('Evaluation'), self::APPROVAL => _n('Approval', 'Approvals', 1), self::ACCEPTED => _x('status', 'Accepted'), diff --git a/src/CommonITILObject.php b/src/CommonITILObject.php index f33c3fcd894..15915f14a3f 100644 --- a/src/CommonITILObject.php +++ b/src/CommonITILObject.php @@ -83,20 +83,18 @@ abstract class CommonITILObject extends CommonDBTM const STATUS_MATRIX_FIELD = ''; - // STATUS + // ITIL Object shared statuses const INCOMING = 1; // new - const ASSIGNED = 2; // assign - const PLANNED = 3; // plan - const WAITING = 4; // waiting - const SOLVED = 5; // solved - const CLOSED = 6; // closed - const ACCEPTED = 7; // accepted - const OBSERVED = 8; // observe - const EVALUATION = 9; // evaluation - const APPROVAL = 10; // approbation - const TEST = 11; // test - const QUALIFICATION = 12; // qualification - + const ASSIGNED = 2; // processing (assigned) + const PLANNED = 3; // processing (planned) + const WAITING = 4; // pending + const SOLVED = 5; + const CLOSED = 6; + const ACCEPTED = 7; + const OBSERVED = 8; + const APPROVAL = 10; // approval / validation + + // --- timeline position const NO_TIMELINE = -1; const TIMELINE_NOTSET = 0; const TIMELINE_LEFT = 1; @@ -5132,7 +5130,7 @@ public static function getStatusIcon($status) } /** - * Get status class + * Get CSS status class * * @since 9.3 * @@ -5142,12 +5140,12 @@ public static function getStatusClass($status) { $class = match ($status) { self::INCOMING, self::WAITING, self::CLOSED => 'circle-filled', - self::ASSIGNED, self::SOLVED, self::EVALUATION => 'circle', + self::ASSIGNED, self::SOLVED, Change::EVALUATION => 'circle', self::PLANNED => 'calendar', self::ACCEPTED => 'check-circle-filled', self::OBSERVED => 'eye', - self::APPROVAL, self::TEST => 'help', - self::QUALIFICATION => 'circle', + self::APPROVAL, Change::TEST => 'help', + Change::QUALIFICATION => 'circle', Change::REFUSED => 'circle-x', Change::CANCELED => 'ban', default => null @@ -5191,16 +5189,16 @@ public static function getStatusKey($status) case self::OBSERVED: $key = 'observe'; break; - case self::EVALUATION: + case Change::EVALUATION: $key = 'eval'; break; case self::APPROVAL: $key = 'approval'; break; - case self::TEST: + case Change::TEST: $key = 'test'; break; - case self::QUALIFICATION: + case Change::QUALIFICATION: $key = 'qualif'; break; } diff --git a/src/Problem.php b/src/Problem.php index 1ea2cf3ad04..bcd05bfd922 100644 --- a/src/Problem.php +++ b/src/Problem.php @@ -686,18 +686,10 @@ public static function rawSearchOptionsToAdd(string $itemtype) return $tab; } - /** - * get the problem status list - * - * @param $withmetaforsearch boolean (false by default) - * - * @return array - **/ public static function getAllStatusArray($withmetaforsearch = false) { - - // To be overridden by class - $tab = [self::INCOMING => _x('status', 'New'), + $tab = [ + self::INCOMING => _x('status', 'New'), self::ACCEPTED => _x('status', 'Accepted'), self::ASSIGNED => _x('status', 'Processing (assigned)'), self::PLANNED => _x('status', 'Processing (planned)'), diff --git a/src/Ticket.php b/src/Ticket.php index 160e32d5b93..4c93ddf2e08 100644 --- a/src/Ticket.php +++ b/src/Ticket.php @@ -254,10 +254,10 @@ public function canViewItem(): bool return true; } - // Can see incoming tickets + // Can see tickets considered as new (incoming status) if ( Session::haveRight(self::$rightname, self::READNEWTICKET) - && ($this->fields["status"] == self::INCOMING) + && $this->fields["status"] == self::INCOMING ) { return true; } @@ -3270,19 +3270,11 @@ public static function getTicketTypeName($value) } } - - /** - * get the Ticket status list - * - * @param boolean $withmetaforsearch (false by default) - * - * @return array - **/ public static function getAllStatusArray($withmetaforsearch = false) { - - // To be overridden by class - $tab = [self::INCOMING => _x('status', 'New'), + $tab = [ + self::INCOMING => _x('status', 'New'), + self::APPROVAL => _n('Approval', 'Approvals', 1), self::ASSIGNED => _x('status', 'Processing (assigned)'), self::PLANNED => _x('status', 'Processing (planned)'), self::WAITING => __('Pending'), @@ -3297,6 +3289,7 @@ public static function getAllStatusArray($withmetaforsearch = false) $tab['old'] = _x('status', 'Solved + Closed'); $tab['all'] = __('All'); } + return $tab; } diff --git a/templates/components/itilobject/fields_panel.html.twig b/templates/components/itilobject/fields_panel.html.twig index 28b5d4c4f1a..e35b777ba16 100644 --- a/templates/components/itilobject/fields_panel.html.twig +++ b/templates/components/itilobject/fields_panel.html.twig @@ -192,8 +192,14 @@ ) }} + {# status #} {{ include('components/itilobject/fields/status.html.twig') }} + {# validation #} + {% if item.fields['global_validation'] != 1 %} + {{ include('components/itilobject/fields/global_validation.html.twig') }} + {% endif %} + {% if item.isField('requesttypes_id') %} {{ fields.dropdownField( 'RequestType', @@ -249,8 +255,6 @@ ) }} {% endif %} - {{ include('components/itilobject/fields/global_validation.html.twig') }} - {% if item.isField('externalid') %} {{ fields.textField('externalid', item.fields['externalid'], __('External ID'), field_options) }} {% endif %}