<?php

/**
 * ---------------------------------------------------------------------
 *
 * GLPI - Gestionnaire Libre de Parc Informatique
 *
 * http://glpi-project.org
 *
 * @copyright 2015-2022 Teclib' and contributors.
 * @copyright 2003-2014 by the INDEPNET Development Team.
 * @licence   https://www.gnu.org/licenses/gpl-3.0.html
 *
 * ---------------------------------------------------------------------
 *
 * LICENSE
 *
 * This file is part of GLPI.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * ---------------------------------------------------------------------
 */

abstract class NotificationTargetCommonITILObject extends NotificationTarget
{
    public $private_profiles = [];

    /**
     * Keep track of profiles who have acces to the "central" interface
     * Will only be loaded if the source item's entity is using anonymisation
     */
    public $central_profiles = [];

    /**
     * @param $entity          (default '')
     * @param $event           (default '')
     * @param $object          (default null)
     * @param $options   array
     **/
    public function __construct($entity = '', $event = '', $object = null, $options = [])
    {

        parent::__construct($entity, $event, $object, $options);

        if (isset($options['followup_id'])) {
            $this->options['sendprivate'] = $options['is_private'];
        }

        if (isset($options['task_id'])) {
            $this->options['sendprivate'] = $options['is_private'];
        }

        if (isset($options['users_id'])) {
            $this->options['users_id'] = $options['users_id'];
        }
    }


    public function validateSendTo($event, array $infos, $notify_me = false, $emitter = null)
    {

       // Check global ones for notification to myself
        if (!parent::validateSendTo($event, $infos, $notify_me, $emitter)) {
            return false;
        }

       // Private object and no right to see private items : do not send
        if (
            $this->isPrivate()
            && (!isset($infos['additionnaloption']['show_private'])
              || !$infos['additionnaloption']['show_private'])
        ) {
            return false;
        }

        return true;
    }

    /**
     * Get notification subject prefix
     *
     * @param $event Event name (default '')
     *
     * @return string
     **/
    public function getSubjectPrefix($event = '')
    {

        $perso_tag = trim(Entity::getUsedConfig(
            'notification_subject_tag',
            $this->getEntity(),
            '',
            ''
        ));

        if (empty($perso_tag)) {
            $perso_tag = 'GLPI';
        }
        return sprintf("[$perso_tag #%07d] ", $this->obj->getField('id'));
    }

    /**
     * Get events related to Itil Object
     *
     * @since 9.2
     *
     * @return array of events (event key => event label)
     **/
    public function getEvents()
    {

        $events = [
            'requester_user'    => __('New user in requesters'),
            'requester_group'   => __('New group in requesters'),
            'observer_user'     => __('New user in observers'),
            'observer_group'    => __('New group in observers'),
            'assign_user'       => __('New user in assignees'),
            'assign_group'      => __('New group in assignees'),
            'assign_supplier'   => __('New supplier in assignees'),
            'add_task'          => __('New task'),
            'update_task'       => __('Update of a task'),
            'delete_task'       => __('Deletion of a task'),
            'add_followup'      => __("New followup"),
            'update_followup'   => __('Update of a followup'),
            'delete_followup'   => __('Deletion of a followup'),
            'user_mention'      => __('User mentionned'),
        ];

        asort($events);
        return $events;
    }


    /**
     * Add linked users to the notified users list
     *
     * @param integer $type type of linked users
     *
     * @return void
     */
    public function addLinkedUserByType($type)
    {
        global $DB, $CFG_GLPI;

        $userlinktable = getTableForItemType($this->obj->userlinkclass);
        $fkfield       = $this->obj->getForeignKeyField();

       //Look for the user by his id
        $criteria = ['LEFT JOIN' => [
            User::getTable() => [
                'ON' => [
                    $userlinktable    => 'users_id',
                    User::getTable()  => 'id'
                ]
            ]
        ]
        ] + $this->getDistinctUserCriteria() + $this->getProfileJoinCriteria();
        $criteria['FROM'] = $userlinktable;
        $criteria['FIELDS'] = array_merge(
            $criteria['FIELDS'],
            [
                "$userlinktable.use_notification AS notif",
                "$userlinktable.alternative_email AS altemail"
            ]
        );
        $criteria['WHERE']["$userlinktable.$fkfield"] = $this->obj->fields['id'];
        $criteria['WHERE']["$userlinktable.type"] = $type;

        $iterator = $DB->request($criteria);
        foreach ($iterator as $data) {
            //Add the user email and language in the notified users list
            if ($data['notif']) {
                $author_email = UserEmail::getDefaultForUser($data['users_id']);
                $author_lang  = $data["language"];
                $author_id    = $data['users_id'];

                if (
                    !empty($data['altemail'])
                    && ($data['altemail'] != $author_email)
                    && NotificationMailing::isUserAddressValid($data['altemail'])
                ) {
                    $author_email = $data['altemail'];
                }
                if (empty($author_lang)) {
                    $author_lang = $CFG_GLPI["language"];
                }
                if (empty($author_id)) {
                    $author_id = -1;
                }

                $user = [
                    'language' => $author_lang,
                    'users_id' => $author_id
                ];
                if ($this->isMailMode()) {
                    $user['email'] = $author_email;
                }
                $this->addToRecipientsList($user);
            }
        }

       // Anonymous user
        $iterator = $DB->request([
            'SELECT' => 'alternative_email',
            'FROM'   => $userlinktable,
            'WHERE'  => [
                $fkfield             => $this->obj->fields['id'],
                'users_id'           => 0,
                'use_notification'   => 1,
                'type'               => $type
            ]
        ]);
        foreach ($iterator as $data) {
            if ($this->isMailMode()) {
                if (NotificationMailing::isUserAddressValid($data['alternative_email'])) {
                    $this->addToRecipientsList([
                        'email'    => $data['alternative_email'],
                        'language' => $CFG_GLPI["language"],
                        'users_id' => -1
                    ]);
                }
            }
        }
    }


    /**
     * Add linked group to the notified user list
     *
     * @param integer $type type of linked groups
     *
     * @return void
     */
    public function addLinkedGroupByType($type)
    {
        global $DB;

        $grouplinktable = getTableForItemType($this->obj->grouplinkclass);
        $fkfield        = $this->obj->getForeignKeyField();

       //Look for the user by his id
        $iterator = $DB->request([
            'SELECT' => 'groups_id',
            'FROM'   => $grouplinktable,
            'WHERE'  => [
                $fkfield => $this->obj->fields['id'],
                'type'   => $type
            ]
        ]);

        foreach ($iterator as $data) {
            //Add the group in the notified users list
            $this->addForGroup(0, $data['groups_id']);
        }
    }



    /**
     * Add linked group without supervisor to the notified user list
     *
     * @since 0.84.1
     *
     * @param integer $type type of linked groups
     *
     * @return void
     */
    public function addLinkedGroupWithoutSupervisorByType($type)
    {
        global $DB;

        $grouplinktable = getTableForItemType($this->obj->grouplinkclass);
        $fkfield        = $this->obj->getForeignKeyField();

        $iterator = $DB->request([
            'SELECT' => 'groups_id',
            'FROM'   => $grouplinktable,
            'WHERE'  => [
                $fkfield => $this->obj->fields['id'],
                'type'   => $type
            ]
        ]);

        foreach ($iterator as $data) {
            //Add the group in the notified users list
            $this->addForGroup(2, $data['groups_id']);
        }
    }


    /**
     * Add linked group supervisor to the notified user list
     *
     * @param integer $type type of linked groups
     *
     * @return void
     */
    public function addLinkedGroupSupervisorByType($type)
    {
        global $DB;

        $grouplinktable = getTableForItemType($this->obj->grouplinkclass);
        $fkfield        = $this->obj->getForeignKeyField();

        $iterator = $DB->request([
            'SELECT' => 'groups_id',
            'FROM'   => $grouplinktable,
            'WHERE'  => [
                $fkfield => $this->obj->fields['id'],
                'type'   => $type
            ]
        ]);

        foreach ($iterator as $data) {
            //Add the group in the notified users list
            $this->addForGroup(1, $data['groups_id']);
        }
    }


    /**
     * Get the email of the item's user : Overloaded manual address used
     **/
    public function addItemAuthor()
    {
        $this->addLinkedUserByType(CommonITILActor::REQUESTER);
    }


    /**
     * Add previous technician in charge (before reassign)
     *
     * @return void
     */
    public function addOldAssignTechnician()
    {
        global $CFG_GLPI;

        if (
            isset($this->options['_old_user'])
            && ($this->options['_old_user']['type'] == CommonITILActor::ASSIGN)
            && $this->options['_old_user']['use_notification']
        ) {
            $user = new User();
            $user->getFromDB($this->options['_old_user']['users_id']);

            $author_email = UserEmail::getDefaultForUser($user->fields['id']);
            $author_lang  = $user->fields["language"];
            $author_id    = $user->fields['id'];

            if (
                !empty($this->options['_old_user']['alternative_email'])
                && ($this->options['_old_user']['alternative_email'] != $author_email)
                && NotificationMailing::isUserAddressValid($this->options['_old_user']['alternative_email'])
            ) {
                $author_email = $this->options['_old_user']['alternative_email'];
            }
            if (empty($author_lang)) {
                $author_lang = $CFG_GLPI["language"];
            }
            if (empty($author_id)) {
                $author_id = -1;
            }

            $user = [
                'language' => $author_lang,
                'users_id' => $author_id
            ];
            if ($this->isMailMode()) {
                $user['email'] = $author_email;
            }
            $this->addToRecipientsList($user);
        }
    }


    /**
     * Add recipient
     *
     * @return void
     */
    public function addRecipientAddress()
    {
        $this->addUserByField("users_id_recipient");
    }


    /**
     * Get supplier related to the ITIL object
     *
     * @param boolean $sendprivate (false by default)
     *
     * @return void
     */
    public function addSupplier($sendprivate = false)
    {
        global $DB;

        if (
            !$sendprivate
            && $this->obj->countSuppliers(CommonITILActor::ASSIGN)
            && $this->isMailMode()
        ) {
            $supplierlinktable = getTableForItemType($this->obj->supplierlinkclass);
            $fkfield           = $this->obj->getForeignKeyField();

            $iterator = $DB->request([
                'SELECT'          => [
                    'glpi_suppliers.email AS email',
                    'glpi_suppliers.name AS name'
                ],
                'DISTINCT'        => true,
                'FROM'            => $supplierlinktable,
                'LEFT JOIN'       => [
                    'glpi_suppliers'  => [
                        'ON' => [
                            $supplierlinktable   => 'suppliers_id',
                            'glpi_suppliers'     => 'id'
                        ]
                    ]
                ],
                'WHERE'           => [
                    "$supplierlinktable.$fkfield" => $this->obj->getID()
                ]
            ]);

            foreach ($iterator as $data) {
                $this->addToRecipientsList($data);
            }
        }
    }


    /**
     * Add approver related to the ITIL object validation
     *
     * @param $options array
     *
     * @return void
     */
    public function addValidationApprover($options = [])
    {
        global $DB;

        if (isset($options['validation_id'])) {
            $validationtable = getTableForItemType($this->obj->getType() . 'Validation');

            $criteria = ['LEFT JOIN' => [
                User::getTable() => [
                    'ON' => [
                        $validationtable  => 'users_id_validate',
                        User::getTable()  => 'id'
                    ]
                ]
            ]
            ] + $this->getDistinctUserCriteria() + $this->getProfileJoinCriteria();
            $criteria['FROM'] = $validationtable;
            $criteria['WHERE']["$validationtable.id"] = $options['validation_id'];

            $iterator = $DB->request($criteria);
            foreach ($iterator as $data) {
                $this->addToRecipientsList($data);
            }
        }
    }

    /**
     * Add requester related to the ITIL object validation
     *
     * @param array $options Options
     *
     * @return void
     **/
    public function addValidationRequester($options = [])
    {
        global $DB;

        if (isset($options['validation_id'])) {
            $validationtable = getTableForItemType($this->obj->getType() . 'Validation');

            $criteria = ['LEFT JOIN' => [
                User::getTable() => [
                    'ON' => [
                        $validationtable  => 'users_id',
                        User::getTable()  => 'id'
                    ]
                ]
            ]
            ] + $this->getDistinctUserCriteria() + $this->getProfileJoinCriteria();
            $criteria['FROM'] = $validationtable;
            $criteria['WHERE']["$validationtable.id"] = $options['validation_id'];

            $iterator = $DB->request($criteria);
            foreach ($iterator as $data) {
                $this->addToRecipientsList($data);
            }
        }
    }


    /**
     * Add author related to the followup
     *
     * @param array $options Options
     *
     * @return void
     */
    public function addFollowupAuthor($options = [])
    {
        global $DB;

        if (isset($options['followup_id'])) {
            $followuptable = ITILFollowup::getTable();

            $criteria = array_merge_recursive(
                ['INNER JOIN' => [
                    User::getTable() => [
                        'ON' => [
                            $followuptable    => 'users_id',
                            User::getTable()  => 'id'
                        ]
                    ]
                ]
                ],
                $this->getDistinctUserCriteria() + $this->getProfileJoinCriteria()
            );
            $criteria['FROM'] = $followuptable;
            $criteria['WHERE']["$followuptable.id"] = $options['followup_id'];

            $iterator = $DB->request($criteria);
            foreach ($iterator as $data) {
                 $this->addToRecipientsList($data);
            }
        }
    }


    /**
     * Add task author
     *
     * @param array $options Options
     *
     * @return void
     */
    public function addTaskAuthor($options = [])
    {
        global $DB;

       // In case of delete task pass user id
        if (isset($options['task_users_id'])) {
            $criteria = $this->getDistinctUserCriteria() + $this->getProfileJoinCriteria();
            $criteria['FROM'] = User::getTable();
            $criteria['WHERE'][User::getTable() . '.id'] = $options['task_users_id'];

            $iterator = $DB->request($criteria);
            foreach ($iterator as $data) {
                $this->addToRecipientsList($data);
            }
        } else if (isset($options['task_id'])) {
            $tasktable = getTableForItemType($this->obj->getType() . 'Task');

            $criteria = array_merge_recursive(
                ['INNER JOIN' => [
                    User::getTable() => [
                        'ON' => [
                            $tasktable        => 'users_id',
                            User::getTable()  => 'id'
                        ]
                    ]
                ]
                ],
                $this->getDistinctUserCriteria() + $this->getProfileJoinCriteria()
            );
            $criteria['FROM'] = $tasktable;
            $criteria['WHERE']["$tasktable.id"] = $options['task_id'];

            $iterator = $DB->request($criteria);
            foreach ($iterator as $data) {
                $this->addToRecipientsList($data);
            }
        }
    }


    /**
     * Add user assigned to task
     *
     * @param array $options Options
     *
     * @return void
     */
    public function addTaskAssignUser($options = [])
    {
        global $DB;

       // In case of delete task pass user id
        if (isset($options['task_users_id_tech'])) {
            $criteria = $this->getDistinctUserCriteria() + $this->getProfileJoinCriteria();
            $criteria['FROM'] = User::getTable();
            $criteria['WHERE'][User::getTable() . '.id'] = $options['task_users_id_tech'];

            $iterator = $DB->request($criteria);
            foreach ($iterator as $data) {
                $this->addToRecipientsList($data);
            }
        } else if (isset($options['task_id'])) {
            $tasktable = getTableForItemType($this->obj->getType() . 'Task');

            $criteria = array_merge_recursive(
                ['INNER JOIN' => [
                    User::getTable() => [
                        'ON' => [
                            $tasktable        => 'users_id_tech',
                            User::getTable()  => 'id'
                        ]
                    ]
                ]
                ],
                $this->getDistinctUserCriteria() + $this->getProfileJoinCriteria()
            );
            $criteria['FROM'] = $tasktable;
            $criteria['WHERE']["$tasktable.id"] = $options['task_id'];

            $iterator = $DB->request($criteria);
            foreach ($iterator as $data) {
                $this->addToRecipientsList($data);
            }
        }
    }


    /**
     * Add group assigned to the task
     *
     * @since 9.1
     *
     * @param array $options Options
     *
     * @return void
     */
    public function addTaskAssignGroup($options = [])
    {
        global $DB;

       // In case of delete task pass user id
        if (isset($options['task_groups_id_tech'])) {
            $this->addForGroup(0, $options['task_groups_id_tech']);
        } else if (isset($options['task_id'])) {
            $tasktable = getTableForItemType($this->obj->getType() . 'Task');
            $iterator = $DB->request([
                'FROM'   => $tasktable,
                'INNER JOIN'   => [
                    'glpi_groups'  => [
                        'ON'  => [
                            'glpi_groups'  => 'id',
                            $tasktable     => 'groups_id_tech'
                        ]
                    ]
                ],
                'WHERE'        => ["$tasktable.id" => $options['task_id']]
            ]);
            foreach ($iterator as $data) {
                $this->addForGroup(0, $data['groups_id_tech']);
            }
        }
    }


    public function addAdditionnalInfosForTarget()
    {
        global $DB;

        $iterator = $DB->request([
            'SELECT' => ['profiles_id'],
            'FROM'   => 'glpi_profilerights',
            'WHERE'  => [
                'name'   => 'followup',
                'rights' => ['&', ITILFollowup::SEEPRIVATE]
            ]
        ]);

        foreach ($iterator as $data) {
            $this->private_profiles[$data['profiles_id']] = $data['profiles_id'];
        }

        $profiles_iterator = $DB->request([
            'SELECT' => ['id'],
            'FROM'   => Profile::getTable(),
            'WHERE'  => [
                'interface' => 'central',
            ]
        ]);

        foreach ($profiles_iterator as $profiles_data) {
            $this->central_profiles[$profiles_data['id']] = $profiles_data['id'];
        }
    }


    public function addAdditionnalUserInfo(array $data)
    {
        return [
            'show_private'    => $this->getShowPrivateInfo($data),
            'is_self_service' => $this->getIsSelfServiceInfo($data),
        ];
    }

    protected function getShowPrivateInfo(array $data)
    {
        global $DB;

        if (!isset($data['users_id']) || count($this->private_profiles) === 0) {
            return false;
        }

        $result = $DB->request([
            'COUNT'  => 'cpt',
            'FROM'   => 'glpi_profiles_users',
            'WHERE'  => [
                'users_id'     => $data['users_id'],
                'profiles_id'  => $this->private_profiles
            ] + getEntitiesRestrictCriteria('glpi_profiles_users', 'entities_id', $this->getEntity(), true)
        ])->current();

        if ($result['cpt']) {
            return true;
        }
        return false;
    }

    protected function getIsSelfServiceInfo(array $data)
    {
        global $DB;

        if (!isset($data['users_id']) || count($this->central_profiles) === 0) {
            return true;
        }

        $result = $DB->request([
            'COUNT'  => 'cpt',
            'FROM'   => Profile_User::getTable(),
            'WHERE'  => [
                'users_id'     => $data['users_id'],
                'profiles_id'  => $this->central_profiles
            ] + getEntitiesRestrictCriteria(Profile_User::getTable(), 'entities_id', $this->getEntity(), true)
        ])->current();

        if ($result['cpt']) {
            return false;
        }
        return true;
    }

    public function getProfileJoinCriteria()
    {
        $criteria = parent::getProfileJoinCriteria();

        if ($this->isPrivate()) {
            $criteria['INNER JOIN'][Profile::getTable()] = [
                'ON' => [
                    Profile::getTable()        => 'id',
                    Profile_User::getTable()   => 'profiles_id'
                ]
            ];
            $criteria['INNER JOIN'][ProfileRight::getTable()] = [
                'ON' => [
                    ProfileRight::getTable()   => 'profiles_id',
                    Profile::getTable()        => 'id'
                ]
            ];
            $criteria['WHERE'][ProfileRight::getTable() . '.name'] = 'followup';
            $criteria['WHERE'][ProfileRight::getTable() . '.rights'] = ['&', ITILFollowup::SEEPRIVATE];
            $criteria['WHERE'][Profile::getTable() . '.interface'] = 'central';
        }
        return $criteria;
    }



    public function isPrivate()
    {

        if (isset($this->options['sendprivate']) && ($this->options['sendprivate'] == 1)) {
            return true;
        }
        return false;
    }


    /**
     * Add additionnals targets for ITIL objects
     *
     * @param string $event specif event to get additional targets (default '')
     *
     * @return void
     **/
    public function addAdditionalTargets($event = '')
    {

        if ($event === 'user_mention') {
            $this->addTarget(Notification::MENTIONNED_USER, __('Mentionned user'));
            return; // Do not propose more targets
        }

        if ($event == 'update') {
            $this->addTarget(
                Notification::OLD_TECH_IN_CHARGE,
                __('Former technician in charge of the ticket')
            );
        }

        if ($event == 'satisfaction') {
            $this->addTarget(Notification::AUTHOR, _n('Requester', 'Requesters', 1));
            $this->addTarget(Notification::RECIPIENT, __('Writer'));
        } else if ($event != 'alertnotclosed') {
            $this->addTarget(Notification::RECIPIENT, __('Writer'));
            $this->addTarget(Notification::SUPPLIER, Supplier::getTypeName(1));
            $this->addTarget(
                Notification::SUPERVISOR_ASSIGN_GROUP,
                __('Manager of the group in charge of the ticket')
            );
            $this->addTarget(
                Notification::ASSIGN_GROUP_WITHOUT_SUPERVISOR,
                __("Group in charge of the ticket except manager users")
            );
            $this->addTarget(Notification::SUPERVISOR_REQUESTER_GROUP, __('Requester group manager'));
            $this->addTarget(
                Notification::REQUESTER_GROUP_WITHOUT_SUPERVISOR,
                __("Requester group except manager users")
            );
            $this->addTarget(Notification::ASSIGN_TECH, __('Technician in charge of the ticket'));
            $this->addTarget(Notification::REQUESTER_GROUP, _n('Requester group', 'Requester groups', 1));
            $this->addTarget(Notification::AUTHOR, _n('Requester', 'Requesters', 1));
            $this->addTarget(Notification::ASSIGN_GROUP, __('Group in charge of the ticket'));
            $this->addTarget(Notification::OBSERVER_GROUP, _n('Watcher group', 'Watcher groups', 1));
            $this->addTarget(Notification::OBSERVER, _n('Watcher', 'Watchers', 1));
            $this->addTarget(Notification::SUPERVISOR_OBSERVER_GROUP, __('Watcher group manager'));
            $this->addTarget(
                Notification::OBSERVER_GROUP_WITHOUT_SUPERVISOR,
                __("Watcher group except manager users")
            );
        }

        if (($event == 'validation') || ($event == 'validation_answer')) {
            $this->addTarget(Notification::VALIDATION_REQUESTER, __('Approval requester'));
            $this->addTarget(Notification::VALIDATION_APPROVER, __('Approver'));
        }

        if (($event == 'update_task') || ($event == 'add_task') || ($event == 'delete_task')) {
            $this->addTarget(Notification::TASK_ASSIGN_TECH, __('Technician in charge of the task'));
            $this->addTarget(Notification::TASK_ASSIGN_GROUP, __('Group in charge of the task'));
            $this->addTarget(Notification::TASK_AUTHOR, __('Task author'));
        }

        if (
            ($event == 'update_followup')
            || ($event == 'add_followup')
            || ($event == 'delete_followup')
        ) {
            $this->addTarget(Notification::FOLLOWUP_AUTHOR, __('Followup author'));
        }
    }


    /**
     * Get specifics targets for ITIL objects
     *
     * @param array $data    Data
     * @param array $options Options
     *
     * @return void
     **/
    public function addSpecificTargets($data, $options)
    {

       //Look for all targets whose type is Notification::ITEM_USER
        switch ($data['type']) {
            case Notification::USER_TYPE:
                switch ($data['items_id']) {
                    case Notification::ASSIGN_TECH:
                        $this->addLinkedUserByType(CommonITILActor::ASSIGN);
                        break;

                  //Send to the supervisor of group in charge of the ITIL object
                    case Notification::SUPERVISOR_ASSIGN_GROUP:
                        $this->addLinkedGroupSupervisorByType(CommonITILActor::ASSIGN);
                        break;

                  //Notification to the group in charge of the ITIL object without supervisor
                    case Notification::ASSIGN_GROUP_WITHOUT_SUPERVISOR:
                        $this->addLinkedGroupWithoutSupervisorByType(CommonITILActor::ASSIGN);
                        break;

                //Send to the user who's got the issue
                    case Notification::RECIPIENT:
                          $this->addRecipientAddress();
                        break;

                //Send to the supervisor of the requester's group
                    case Notification::SUPERVISOR_REQUESTER_GROUP:
                         $this->addLinkedGroupSupervisorByType(CommonITILActor::REQUESTER);
                        break;

                //Send to the technician previously in charge of the ITIL object (before reassignation)
                    case Notification::OLD_TECH_IN_CHARGE:
                        $this->addOldAssignTechnician();
                        break;

               //Assign to a supplier
                    case Notification::SUPPLIER:
                        $this->addSupplier($this->isPrivate());
                        break;

                    case Notification::REQUESTER_GROUP:
                        $this->addLinkedGroupByType(CommonITILActor::REQUESTER);
                        break;

               //Notification to the requester group without supervisor
                    case Notification::REQUESTER_GROUP_WITHOUT_SUPERVISOR:
                        $this->addLinkedGroupWithoutSupervisorByType(CommonITILActor::REQUESTER);
                        break;

                    case Notification::ASSIGN_GROUP:
                        $this->addLinkedGroupByType(CommonITILActor::ASSIGN);
                        break;

               //Send to the ITIL object validation approver
                    case Notification::VALIDATION_APPROVER:
                        $this->addValidationApprover($options);
                        break;

               //Send to the ITIL object validation requester
                    case Notification::VALIDATION_REQUESTER:
                        $this->addValidationRequester($options);
                        break;

               //Send to the ITIL object followup author
                    case Notification::FOLLOWUP_AUTHOR:
                        $this->addFollowupAuthor($options);
                        break;

               //Send to the ITIL object followup author
                    case Notification::TASK_AUTHOR:
                        $this->addTaskAuthor($options);
                        break;

               //Send to the ITIL object followup author
                    case Notification::TASK_ASSIGN_TECH:
                        $this->addTaskAssignUser($options);
                        break;

               //Send to the ITIL object task group assigned
                    case Notification::TASK_ASSIGN_GROUP:
                        $this->addTaskAssignGroup($options);
                        break;

               //Notification to the ITIL object's observer group
                    case Notification::OBSERVER_GROUP:
                        $this->addLinkedGroupByType(CommonITILActor::OBSERVER);
                        break;

               //Notification to the ITIL object's observer user
                    case Notification::OBSERVER:
                        $this->addLinkedUserByType(CommonITILActor::OBSERVER);
                        break;

               //Notification to the supervisor of the ITIL object's observer group
                    case Notification::SUPERVISOR_OBSERVER_GROUP:
                        $this->addLinkedGroupSupervisorByType(CommonITILActor::OBSERVER);
                        break;

               //Notification to the observer group without supervisor
                    case Notification::OBSERVER_GROUP_WITHOUT_SUPERVISOR:
                        $this->addLinkedGroupWithoutSupervisorByType(CommonITILActor::OBSERVER);
                        break;

                    case Notification::MENTIONNED_USER:
                        $this->addMentionnedUser($options);
                        break;
                }
        }
    }

    /**
     * Add mentionned user to recipients.
     *
     * @param array $options
     *
     * @return void
     */
    protected function addMentionnedUser(array $options): void
    {
        $user = new User();
        if (array_key_exists('users_id', $options) && $user->getFromDB($options['users_id'])) {
            $this->addToRecipientsList(
                [
                    'language' => $user->fields['language'],
                    'users_id' => $user->fields['id'],
                ]
            );
        }
    }


    public function addDataForTemplate($event, $options = [])
    {
        $events    = $this->getAllEvents();
        $objettype = strtolower($this->obj->getType());

       // Get data from ITIL objects
        if ($event != 'alertnotclosed') {
            $this->data = $this->getDataForObject($this->obj, $options);
        } else {
            if (
                isset($options['entities_id'])
                && isset($options['items'])
            ) {
                $entity = new Entity();
                if ($entity->getFromDB($options['entities_id'])) {
                    $this->data["##$objettype.entity##"]      = $entity->getField('completename');
                    $this->data["##$objettype.shortentity##"] = $entity->getField('name');
                }
                if ($item = getItemForItemtype($objettype)) {
                    $objettypes = Toolbox::strtolower(getPlural($objettype));
                    $items      = [];
                    foreach ($options['items'] as $object) {
                        $item->getFromDB($object['id']);
                        $tmp = $this->getDataForObject($item, $options, true);
                        $this->data[$objettypes][] = $tmp;
                    }
                }
            }
        }

        if (
            ($event == 'validation')
            && isset($options['validation_status'])
        ) {
            $this->data["##$objettype.action##"]
                     //TRANS: %s id of the approval's state
                     = sprintf(
                         __('%1$s - %2$s'),
                         CommonITILValidation::getTypeName(1),
                         TicketValidation::getStatus($options['validation_status'])
                     );
        } else {
            $this->data["##$objettype.action##"] = $events[$event];
        }

        $this->getTags();

        foreach ($this->tag_descriptions[parent::TAG_LANGUAGE] as $tag => $values) {
            if (!isset($this->data[$tag])) {
                $this->data[$tag] = $values['label'];
            }
        }
    }


    /**
     * Get data from an item
     *
     * @param CommonDBTM $item    Object instance
     * @param array      $options Options
     * @param boolean    $simple  (false by default)
     *
     * @return array
     **/
    public function getDataForObject(CommonDBTM $item, array $options, $simple = false)
    {
        global $CFG_GLPI, $DB;

        $is_self_service = $options['additionnaloption']['is_self_service'] ?? true;
        $are_names_anonymized = $is_self_service
            && Entity::getAnonymizeConfig($item->fields['entities_id']) !== Entity::ANONYMIZE_DISABLED;
        $objettype = strtolower($item->getType());

        $data["##$objettype.title##"]        = $item->getField('name');
        $data["##$objettype.content##"]      = $item->getField('content');
        $data["##$objettype.description##"]  = $item->getField('content');
        $data["##$objettype.id##"]           = sprintf("%07d", $item->getField("id"));

        $data["##$objettype.url##"]
                        = $this->formatURL(
                            $options['additionnaloption']['usertype'],
                            $objettype . "_" . $item->getField("id")
                        );

        $tab = '$1';
        $data["##$objettype.urlapprove##"]
                           = $this->formatURL(
                               $options['additionnaloption']['usertype'],
                               $objettype . "_" . $item->getField("id") . "_" .
                               $item->getType() . $tab
                           );

        $entity = new Entity();
        if ($entity->getFromDB($this->getEntity())) {
            $data["##$objettype.entity##"]          = $entity->getField('completename');
            $data["##$objettype.shortentity##"]     = $entity->getField('name');
            $data["##$objettype.entity.phone##"]    = $entity->getField('phonenumber');
            $data["##$objettype.entity.fax##"]      = $entity->getField('fax');
            $data["##$objettype.entity.website##"]  = $entity->getField('website');
            $data["##$objettype.entity.email##"]    = $entity->getField('email');
            $data["##$objettype.entity.address##"]  = $entity->getField('address');
            $data["##$objettype.entity.postcode##"] = $entity->getField('postcode');
            $data["##$objettype.entity.town##"]     = $entity->getField('town');
            $data["##$objettype.entity.state##"]    = $entity->getField('state');
            $data["##$objettype.entity.country##"]  = $entity->getField('country');
            $data["##$objettype.entity.registration_number##"] = $entity->getField('registration_number');
        }

        $data["##$objettype.storestatus##"]  = $item->getField('status');
        $data["##$objettype.status##"]       = $item->getStatus($item->getField('status'));

        $data["##$objettype.urgency##"]      = $item->getUrgencyName($item->getField('urgency'));
        $data["##$objettype.impact##"]       = $item->getImpactName($item->getField('impact'));
        $data["##$objettype.priority##"]     = $item->getPriorityName($item->getField('priority'));
        $data["##$objettype.time##"]         = $item->getActionTime($item->getField('actiontime'));

        $data["##$objettype.creationdate##"] = Html::convDateTime($item->getField('date'));
        $data["##$objettype.closedate##"]    = Html::convDateTime($item->getField('closedate'));
        $data["##$objettype.solvedate##"]    = Html::convDateTime($item->getField('solvedate'));
        $data["##$objettype.duedate##"]      = Html::convDateTime($item->getField('time_to_resolve'));

        $data["##$objettype.category##"] = '';
        if ($item->getField('itilcategories_id')) {
            $data["##$objettype.category##"]
                              = Dropdown::getDropdownName(
                                  'glpi_itilcategories',
                                  $item->getField('itilcategories_id')
                              );
        }
        $data['actors']                = [];

        $data["##$objettype.authors##"] = '';
        $data['authors']                = [];
        if ($item->countUsers(CommonITILActor::REQUESTER)) {
            $users = [];
            foreach ($item->getUsers(CommonITILActor::REQUESTER) as $tmpusr) {
                $uid = $tmpusr['users_id'];
                $user_tmp = new User();
                if (
                    $uid
                    && $user_tmp->getFromDB($uid)
                ) {
                    $users[] = $user_tmp->getName();

                    // Legacy authors data
                    $author = self::getActorData($user_tmp, CommonITILActor::REQUESTER, 'author');
                    $author['##author.title##'] = $author['##author.usertitle##'];
                    $author['##author.category##'] = $author['##author.usercategory##'];
                    $data['authors'][] = $author;

                    $data['actors'][]  = self::getActorData($user_tmp, CommonITILActor::REQUESTER, 'actor');
                } else {
                    // Anonymous users only in xxx.authors, not in authors
                    $users[] = $tmpusr['alternative_email'];
                }
            }
            $data["##$objettype.authors##"] = implode(', ', $users);
        }

        $data["##$objettype.suppliers##"] = '';
        $data["##$objettype.assigntosupplier##"] = '';
        $data['suppliers'] = [];
        if ($item->countSuppliers(CommonITILActor::ASSIGN)) {
            $suppliers = [];
            foreach ($item->getSuppliers(CommonITILActor::ASSIGN) as $supplier_data) {
                $sid      = $supplier_data['suppliers_id'];
                $supplier = new Supplier();
                if ($sid > 0 && $supplier->getFromDB($sid)) {
                    $suppliers[] = $supplier->getName();

                    // Legacy suppliers data
                    $supplier = self::getActorData($supplier, CommonITILActor::ASSIGN, 'supplier');
                    $supplier['##supplier.type##'] = $supplier['##supplier.suppliertype##'];
                    $data['suppliers'][] = $supplier;

                    $data['actors'][]    = self::getActorData($supplier, CommonITILActor::ASSIGN, 'actor');
                }
            }
            $data["##$objettype.suppliers##"] = implode(', ', $suppliers);
            $data["##$objettype.assigntosupplier##"] = implode(', ', $suppliers);
        }

        $data["##$objettype.openbyuser##"] = '';
        if ($item->getField('users_id_recipient')) {
            $user_tmp = new User();
            $user_tmp->getFromDB($item->getField('users_id_recipient'));
            $data["##$objettype.openbyuser##"] = $user_tmp->getName();
        }

        $data["##$objettype.lastupdater##"] = '';
        if ($item->getField('users_id_lastupdater')) {
            $user_tmp = new User();
            $user_tmp->getFromDB($item->getField('users_id_lastupdater'));
            $data["##$objettype.lastupdater##"] = $user_tmp->getName();
        }

        $data["##$objettype.assigntousers##"] = '';
        if ($item->countUsers(CommonITILActor::ASSIGN)) {
            $users = [];
            foreach ($item->getUsers(CommonITILActor::ASSIGN) as $tmp) {
                $uid      = $tmp['users_id'];
                $user_tmp = new User();

                if ($user_tmp->getFromDB($uid)) {
                    // Check if the user need to be anonymized
                    if ($are_names_anonymized) {
                        $users[$uid] = User::getAnonymizedNameForUser($uid, $item->fields['entities_id']);
                    } else {
                        $users[$uid] = $user_tmp->getName();
                    }

                    $actor = self::getActorData($user_tmp, CommonITILActor::ASSIGN, 'actor');
                    $actor['##actor.name##'] = $users[$uid]; // Use anonymized name
                    $data['actors'][] = $actor;
                }
            }
            $data["##$objettype.assigntousers##"] = implode(', ', $users);
        }

        $data["##$objettype.groups##"] = '';
        if ($item->countGroups(CommonITILActor::REQUESTER)) {
            $groups = [];
            foreach ($item->getGroups(CommonITILActor::REQUESTER) as $group_data) {
                $gid = $group_data['groups_id'];

                $group = new Group();
                if ($gid > 0 && $group->getFromDB($gid)) {
                    $groups[$gid] = Dropdown::getDropdownName('glpi_groups', $gid);
                    $data['actors'][] = self::getActorData($group, CommonITILActor::REQUESTER, 'actor');
                }
            }
            $data["##$objettype.groups##"] = implode(', ', $groups);
        }

        $data["##$objettype.observergroups##"] = '';
        if ($item->countGroups(CommonITILActor::OBSERVER)) {
            $groups = [];
            foreach ($item->getGroups(CommonITILActor::OBSERVER) as $group_data) {
                $gid = $group_data['groups_id'];

                $group = new Group();
                if ($gid > 0 && $group->getFromDB($gid)) {
                    $groups[$gid] = Dropdown::getDropdownName('glpi_groups', $gid);
                    $data['actors'][] = self::getActorData($group, CommonITILActor::OBSERVER, 'actor');
                }
            }
            $data["##$objettype.observergroups##"] = implode(', ', $groups);
        }

        $data["##$objettype.observerusers##"] = '';
        if ($item->countUsers(CommonITILActor::OBSERVER)) {
            $users = [];
            foreach ($item->getUsers(CommonITILActor::OBSERVER) as $tmp) {
                $uid      = $tmp['users_id'];
                $user_tmp = new User();
                if (
                    $uid
                    && $user_tmp->getFromDB($uid)
                ) {
                    $users[] = $user_tmp->getName();

                    $data['actors'][] = self::getActorData($user_tmp, CommonITILActor::OBSERVER, 'actor');
                } else {
                    $users[] = $tmp['alternative_email'];
                }
            }
            $data["##$objettype.observerusers##"] = implode(', ', $users);
        }

        $data["##$objettype.assigntogroups##"] = '';
        if ($item->countGroups(CommonITILActor::ASSIGN)) {
            $groups = [];
            foreach ($item->getGroups(CommonITILActor::ASSIGN) as $group_data) {
                $gid = $group_data['groups_id'];

                $group = new Group();
                if ($gid > 0 && $group->getFromDB($gid)) {
                    $groups[$gid] = Dropdown::getDropdownName('glpi_groups', $gid);
                    $data['actors'][] = self::getActorData($group, CommonITILActor::ASSIGN, 'actor');
                }
            }
            $data["##$objettype.assigntogroups##"] = implode(', ', $groups);
        }

        $data["##$objettype.solution.type##"] = '';
        $data["##$objettype.solution.description##"] = '';

        $itilsolution = new ITILSolution();
        $solution = $itilsolution->getFromDBByRequest([
            'WHERE'  => [
                'itemtype'  => $objettype,
                'items_id'  => $item->fields['id']
            ],
            'ORDER'  => 'date_creation DESC',
            'LIMIT'  => 1
        ]);

        if ($solution) {
            $data["##$objettype.solution.type##"] = '';
            if ($itilsolution->getField('solutiontypes_id')) {
                $data["##$objettype.solution.type##"] = Dropdown::getDropdownName(
                    'glpi_solutiontypes',
                    $itilsolution->getField('solutiontypes_id')
                );
            }

            $data["##$objettype.solution.description##"] = $itilsolution->getField('content');
        }

       // Complex mode
        if (!$simple) {
            $show_private = $options['additionnaloption']['show_private'] ?? false;
            $followup_restrict = [];
            $followup_restrict['items_id'] = $item->getField('id');
            if (!$show_private) {
                $followup_restrict['is_private'] = 0;
            }
            $followup_restrict['itemtype'] = $objettype;

           //Followup infos
            $followups          = getAllDataFromTable(
                'glpi_itilfollowups',
                [
                    'WHERE'  => $followup_restrict,
                    'ORDER'  => ['date_mod DESC', 'id ASC']
                ]
            );
            $data['followups'] = [];
            foreach ($followups as $followup) {
                 $tmp                             = [];
                 $tmp['##followup.isprivate##']   = Dropdown::getYesNo($followup['is_private']);

                 // Check if the author need to be anonymized
                if ($are_names_anonymized && ITILFollowup::getById($followup['id'])->isFromSupportAgent()) {
                    $tmp['##followup.author##'] = User::getAnonymizedNameForUser(
                        $followup['users_id'],
                        $item->fields['entities_id']
                    );
                } else {
                    $tmp['##followup.author##'] = getUserName($followup['users_id']);
                }

                $tmp['##followup.requesttype##'] = '';
                if ($followup['requesttypes_id']) {
                     $tmp['##followup.requesttype##'] = Dropdown::getDropdownName(
                         'glpi_requesttypes',
                         $followup['requesttypes_id']
                     );
                }
                $tmp['##followup.date##']        = Html::convDateTime($followup['date']);
                $tmp['##followup.description##'] = $followup['content'];

                $data['followups'][] = $tmp;
            }

            $data["##$objettype.numberoffollowups##"] = count($data['followups']);

            $data['log'] = [];
           // Use list_limit_max or load the full history ?
            foreach (Log::getHistoryData($item, 0, $CFG_GLPI['list_limit_max']) as $log) {
                $tmp                               = [];
                $tmp["##$objettype.log.date##"]    = $log['date_mod'];
                $tmp["##$objettype.log.user##"]    = $log['user_name'];
                $tmp["##$objettype.log.field##"]   = $log['field'];
                $tmp["##$objettype.log.content##"] = $log['change'];
                $data['log'][]                    = $tmp;
            }

            $data["##$objettype.numberoflogs##"] = count($data['log']);

           // Get unresolved items
            $restrict = [
                'NOT' => [
                    $item->getTable() . '.status' => array_merge(
                        $item->getSolvedStatusArray(),
                        $item->getClosedStatusArray()
                    )
                ]
            ];

            if ($item->maybeDeleted()) {
                $restrict[$item->getTable() . '.is_deleted'] = 0;
            }

            $data["##$objettype.numberofunresolved##"]
               = countElementsInTableForEntity($item->getTable(), $this->getEntity(), $restrict, false);

           // Document
            $iterator = $DB->request([
                'SELECT'    => 'glpi_documents.*',
                'FROM'      => 'glpi_documents',
                'LEFT JOIN' => [
                    'glpi_documents_items'  => [
                        'ON' => [
                            'glpi_documents_items'  => 'documents_id',
                            'glpi_documents'        => 'id'
                        ]
                    ]
                ],
                'WHERE'     => [
                    $item->getAssociatedDocumentsCriteria(),
                    'timeline_position' => ['>', CommonITILObject::NO_TIMELINE], // skip inlined images
                ]
            ]);

            $data["documents"] = [];
            $addtodownloadurl   = '';
            if ($item->getType() == 'Ticket') {
                 $addtodownloadurl = "&tickets_id=" . $item->fields['id'];
            }
            foreach ($iterator as $row) {
                $tmp                      = [];
                $tmp['##document.id##']   = $row['id'];
                $tmp['##document.name##'] = $row['name'];
                $tmp['##document.weblink##']
                                       = $row['link'];

                $tmp['##document.url##']  = $this->formatURL(
                    $options['additionnaloption']['usertype'],
                    "document_" . $row['id']
                );
                $downloadurl              = "/front/document.send.php?docid=" . $row['id'];

                $tmp['##document.downloadurl##']
                                       = $this->formatURL(
                                           $options['additionnaloption']['usertype'],
                                           $downloadurl . $addtodownloadurl
                                       );

                $tmp['##document.heading##'] = '';
                if ($row['documentcategories_id']) {
                     $tmp['##document.heading##']
                                          = Dropdown::getDropdownName(
                                              'glpi_documentcategories',
                                              $row['documentcategories_id']
                                          );
                }

                $tmp['##document.filename##']
                                       = $row['filename'];

                $data['documents'][]     = $tmp;
            }

            $data["##$objettype.urldocument##"]
                        = $this->formatURL(
                            $options['additionnaloption']['usertype'],
                            $objettype . "_" . $item->getField("id") . '_Document_Item$1'
                        );

            $data["##$objettype.numberofdocuments##"]
                        = count($data['documents']);

           //costs infos
            $costtype = $item->getType() . 'Cost';
            $costs    = $costtype::getCostsSummary($costtype, $item->getField("id"));

            $data["##$objettype.costfixed##"]    = $costs['costfixed'];
            $data["##$objettype.costmaterial##"] = $costs['costmaterial'];
            $data["##$objettype.costtime##"]     = $costs['costtime'];
            $data["##$objettype.totalcost##"]    = $costs['totalcost'];

            $costs          = getAllDataFromTable(
                getTableForItemType($costtype),
                [
                    'WHERE'  => [$item->getForeignKeyField() => $item->getField('id')],
                    'ORDER'  => ['begin_date DESC', 'id ASC']
                ]
            );
            $data['costs'] = [];
            foreach ($costs as $cost) {
                 $tmp = [];
                 $tmp['##cost.name##']         = $cost['name'];
                 $tmp['##cost.comment##']      = $cost['comment'];
                 $tmp['##cost.datebegin##']    = Html::convDate($cost['begin_date']);
                 $tmp['##cost.dateend##']      = Html::convDate($cost['end_date']);
                 $tmp['##cost.time##']         = $item->getActionTime($cost['actiontime']);
                 $tmp['##cost.costtime##']     = Html::formatNumber($cost['cost_time']);
                 $tmp['##cost.costfixed##']    = Html::formatNumber($cost['cost_fixed']);
                 $tmp['##cost.costmaterial##'] = Html::formatNumber($cost['cost_material']);
                 $tmp['##cost.totalcost##']    = CommonITILCost::computeTotalCost(
                     $cost['actiontime'],
                     $cost['cost_time'],
                     $cost['cost_fixed'],
                     $cost['cost_material']
                 );
                 $tmp['##cost.budget##']       = '';
                if ($cost['budgets_id']) {
                     $tmp['##cost.budget##'] = Dropdown::getDropdownName('glpi_budgets', $cost['budgets_id']);
                }
                 $data['costs'][]             = $tmp;
            }
            $data["##$objettype.numberofcosts##"] = count($data['costs']);

           //Task infos
            $tasktype = $item->getType() . 'Task';
            $taskobj  = new $tasktype();
            $restrict = [$item->getForeignKeyField() => $item->getField('id')];
            if (
                $taskobj->maybePrivate()
                && (!isset($options['additionnaloption']['show_private'])
                || !$options['additionnaloption']['show_private'])
            ) {
                $restrict['is_private'] = 0;
            }

            $tasks          = getAllDataFromTable(
                $taskobj->getTable(),
                [
                    'WHERE'  => $restrict,
                    'ORDER'  => ['date_mod DESC', 'id ASC']
                ]
            );
            $data['tasks'] = [];
            foreach ($tasks as $task) {
                 $tmp                          = [];
                 $tmp['##task.id##']           = $task['id'];
                if ($taskobj->maybePrivate()) {
                    $tmp['##task.isprivate##'] = Dropdown::getYesNo($task['is_private']);
                }
                 $tmp['##task.author##']       = getUserName($task['users_id']);

                 $tmp_taskcatinfo = Dropdown::getDropdownName(
                     'glpi_taskcategories',
                     $task['taskcategories_id'],
                     true,
                     true,
                     false,
                     ''
                 );
                 $tmp['##task.categoryid##']      = $task['taskcategories_id'];
                 $tmp['##task.category##']        = $tmp_taskcatinfo['name'];
                 $tmp['##task.categorycomment##'] = $tmp_taskcatinfo['comment'];

                 $tmp['##task.date##']         = Html::convDateTime($task['date']);
                 $tmp['##task.description##']  = $task['content'];
                 $tmp['##task.time##']         = Ticket::getActionTime($task['actiontime']);
                 $tmp['##task.status##']       = Planning::getState($task['state']);

                 $tmp['##task.user##']         = getUserName($task['users_id_tech']);
                 $tmp['##task.group##'] = '';
                if ($task['groups_id_tech']) {
                     $tmp['##task.group##'] = Dropdown::getDropdownName("glpi_groups", $task['groups_id_tech']);
                }
                 $tmp['##task.begin##']        = "";
                 $tmp['##task.end##']          = "";
                if (!is_null($task['begin'])) {
                    $tmp['##task.begin##']     = Html::convDateTime($task['begin']);
                    $tmp['##task.end##']       = Html::convDateTime($task['end']);
                }

                 $data['tasks'][]             = $tmp;
            }

            $data["##$objettype.numberoftasks##"] = count($data['tasks']);

            $data['timelineitems'] = [];

            $options = [
                'with_documents'    => false,
                'with_logs'         => false,
                'with_validations'  => false,
                'expose_private'    => $show_private,
                'bypass_rights'     => true,
                'sort_by_date_desc' => true,
                'is_self_service'   => $is_self_service,
            ];

            $timeline = $item->getTimelineItems($options);

            foreach ($timeline as $timeline_data) {
                $tmptimelineitem = [];

                if ($timeline_data['type'] == "Solution") {
                    $tmptimelineitem['##timelineitems.type##'] = ITILSolution::getType();
                } else {
                    $tmptimelineitem['##timelineitems.type##'] = $timeline_data['type']::getType();
                }

                $tmptimelineitem['##timelineitems.typename##']    = $tmptimelineitem['##timelineitems.type##']::getTypeName(0);
                $tmptimelineitem['##timelineitems.date##']        = $timeline_data['item']['date'];
                $tmptimelineitem['##timelineitems.description##'] = $timeline_data['item']['content'];
                $tmptimelineitem['##timelineitems.position##']    = $this->getUserPositionFromTimelineItemPosition($timeline_data['item']['timeline_position']);

                if ($timeline_data['type'] == ITILFollowup::getType()) {
                   // Check if the author need to be anonymized
                    if (
                        $are_names_anonymized
                        && ITILFollowup::getById($timeline_data['item']['id'])->isFromSupportAgent()
                    ) {
                        $tmptimelineitem['##timelineitems.author##'] = User::getAnonymizedNameForUser(
                            $timeline_data['item']['users_id'],
                            $item->fields['entities_id']
                        );
                    } else {
                        $tmptimelineitem['##timelineitems.author##'] = getUserName($timeline_data['item']['users_id']);
                    }
                } else {
                    $tmptimelineitem['##timelineitems.author##'] = getUserName($timeline_data['item']['users_id']);
                }
                $data['timelineitems'][] = $tmptimelineitem;
            }
        }
        return $data;
    }

    protected function getActorData(CommonDBTM $actor, int $actortype, string $key_prefix): array
    {
        $data = [
            sprintf('##%s.itemtype##', $key_prefix)     => get_class($actor),
            sprintf('##%s.actortype##', $key_prefix)    => $actortype,
            sprintf('##%s.id##', $key_prefix)           => $actor->getID(),
            sprintf('##%s.name##', $key_prefix)         => '',
            sprintf('##%s.comments##', $key_prefix)     => $actor->getField('comment'),
            sprintf('##%s.location##', $key_prefix)     => '',
            sprintf('##%s.usertitle##', $key_prefix)    => '',
            sprintf('##%s.usercategory##', $key_prefix) => '',
            sprintf('##%s.email##', $key_prefix)        => '',
            sprintf('##%s.mobile##', $key_prefix)       => '',
            sprintf('##%s.phone##', $key_prefix)        => '',
            sprintf('##%s.phone2##', $key_prefix)       => '',
            sprintf('##%s.fax##', $key_prefix)          => '',
            sprintf('##%s.website##', $key_prefix)      => '',
            sprintf('##%s.address##', $key_prefix)      => '',
            sprintf('##%s.postcode##', $key_prefix)     => '',
            sprintf('##%s.town##', $key_prefix)         => '',
            sprintf('##%s.state##', $key_prefix)        => '',
            sprintf('##%s.country##', $key_prefix)      => '',
            sprintf('##%s.suppliertype##', $key_prefix) => '',
        ];
        switch (get_class($actor)) {
            case User::class:
                $data[sprintf('##%s.name##', $key_prefix)] = $actor->getName();

                $location = new Location();
                if ($actor->fields['locations_id'] > 0 && $location->getFromDB($actor->fields['locations_id'])) {
                    $data[sprintf('##%s.location##', $key_prefix)] = Dropdown::getDropdownName(
                        'glpi_locations',
                        $actor->fields['locations_id']
                    );
                    $data[sprintf('##%s.address##', $key_prefix)]  = $location->getField('address');
                    $data[sprintf('##%s.postcode##', $key_prefix)] = $location->getField('postcode');
                    $data[sprintf('##%s.town##', $key_prefix)]     = $location->getField('town');
                }

                if ($actor->fields['usertitles_id'] > 0) {
                    $data[sprintf('##%s.usertitle##', $key_prefix)] = Dropdown::getDropdownName(
                        'glpi_usertitles',
                        $actor->fields['usertitles_id']
                    );
                }

                if ($actor->fields['usercategories_id'] > 0) {
                    $data[sprintf('##%s.usercategory##', $key_prefix)] = Dropdown::getDropdownName(
                        'glpi_usercategories',
                        $actor->fields['usercategories_id']
                    );
                }

                $data[sprintf('##%s.email##', $key_prefix)]      = $actor->getDefaultEmail();
                $data[sprintf('##%s.mobile##', $key_prefix)]     = $actor->getField('mobile');
                $data[sprintf('##%s.phone##', $key_prefix)]      = $actor->getField('phone');
                $data[sprintf('##%s.phone2##', $key_prefix)]     = $actor->getField('phone2');
                break;
            case Group::class:
                $data[sprintf('##%s.name##', $key_prefix)]       = Dropdown::getDropdownName('glpi_groups', $actor->getID());
                break;
            case Supplier::class:
                $data[sprintf('##%s.name##', $key_prefix)]       = $actor->getName();
                $data[sprintf('##%s.email##', $key_prefix)]      = $actor->getField('email');
                $data[sprintf('##%s.phone##', $key_prefix)]      = $actor->getField('phonenumber');
                $data[sprintf('##%s.fax##', $key_prefix)]        = $actor->getField('fax');
                $data[sprintf('##%s.website##', $key_prefix)]    = $actor->getField('website');
                $data[sprintf('##%s.address##', $key_prefix)]    = $actor->getField('address');
                $data[sprintf('##%s.postcode##', $key_prefix)]   = $actor->getField('postcode');
                $data[sprintf('##%s.town##', $key_prefix)]       = $actor->getField('town');
                $data[sprintf('##%s.state##', $key_prefix)]      = $actor->getField('state');
                $data[sprintf('##%s.country##', $key_prefix)]    = $actor->getField('country');
                if ($actor->getField('suppliertypes_id')) {
                    $data[sprintf('##%s.type##', $key_prefix)]
                               = Dropdown::getDropdownName(
                                   'glpi_suppliertypes',
                                   $actor->getField('suppliertypes_id')
                               );
                    $data[sprintf('##%s.suppliertype##', $key_prefix)]
                               = Dropdown::getDropdownName(
                                   'glpi_suppliertypes',
                                   $actor->getField('suppliertypes_id')
                               );
                }
                break;
        }
        return $data;
    }

    public function getTags()
    {

        $itemtype  = $this->obj->getType();
        $objettype = strtolower($itemtype);

       //Locales
        $tags = [$objettype . '.id'                    => __('ID'),
            $objettype . '.title'                 => __('Title'),
            $objettype . '.url'                   => __('URL'),
            $objettype . '.category'              => _n('Category', 'Categories', 1),
            $objettype . '.content'               => __('Description'),
            $objettype . '.description'           => sprintf(
                __('%1$s: %2$s'),
                $this->obj->getTypeName(1),
                __('Description')
            ),
            $objettype . '.status'                => __('Status'),
            $objettype . '.urgency'               => __('Urgency'),
            $objettype . '.impact'                => __('Impact'),
            $objettype . '.priority'              => __('Priority'),
            $objettype . '.time'                  => __('Total duration'),
            $objettype . '.creationdate'          => __('Opening date'),
            $objettype . '.closedate'             => __('Closing date'),
            $objettype . '.solvedate'             => __('Date of solving'),
            $objettype . '.duedate'               => __('Time to resolve'),
            $objettype . '.authors'               => _n('Requester', 'Requesters', Session::getPluralNumber()),
            'author.id'                         => __('Requester ID'),
            'author.name'                       => _n('Requester', 'Requesters', 1),
            'author.location'                   => __('Requester location'),
            'author.mobile'                     => __('Mobile phone'),
            'author.phone'                      => Phone::getTypeName(1),
            'author.phone2'                     => __('Phone 2'),
            'author.email'                      => _n('Email', 'Emails', 1),
            'author.title'                      => _x('person', 'Title'),
            'author.category'                   => _n('Category', 'Categories', 1),
            $objettype . '.suppliers'             => _n('Supplier', 'Suppliers', Session::getPluralNumber()),
            'supplier.id'                       => __('Supplier ID'),
            'supplier.name'                     => Supplier::getTypeName(1),
            'supplier.phone'                    => Phone::getTypeName(1),
            'supplier.fax'                      => __('Fax'),
            'supplier.website'                  => __('Website'),
            'supplier.email'                    => _n('Email', 'Emails', 1),
            'supplier.address'                  => __('Address'),
            'supplier.postcode'                 => __('Postal code'),
            'supplier.town'                     => __('City'),
            'supplier.state'                    => _x('location', 'State'),
            'supplier.country'                  => __('Country'),
            'supplier.comments'                 => _n('Comment', 'Comments', Session::getPluralNumber()),
            'supplier.type'                     => SupplierType::getTypeName(1),
            $objettype . '.openbyuser'            => __('Writer'),
            $objettype . '.lastupdater'           => __('Last updater'),
            $objettype . '.assigntousers'         => __('Assigned to technicians'),
            'actors.itemtype'     => __('Internal type'),
            'actors.actortype'    => __('Actor type'),
            'actors.id'           => __('ID'),
            'actors.name'         => __('Name'),
            'actors.location'     => __('User location'),
            'actors.usertitle'    => _x('person', 'Title'),
            'actors.usercategory' => _n('Category', 'Categories', 1),
            'actors.email'        => _n('Email', 'Emails', 1),
            'actors.mobile'       => __('Mobile phone'),
            'actors.phone'        => Phone::getTypeName(1),
            'actors.phone2'       => __('Phone 2'),
            'actors.fax'          => __('Fax'),
            'actors.website'      => __('Website'),
            'actors.address'      => __('Address'),
            'actors.postcode'     => __('Postal code'),
            'actors.town'         => __('City'),
            'actors.state'        => _x('location', 'State'),
            'actors.country'      => __('Country'),
            'actors.comments'     => _n('Comment', 'Comments', Session::getPluralNumber()),
            'actors.suppliertype' => SupplierType::getTypeName(1),
            $objettype . '.assigntosupplier'      => __('Assigned to a supplier'),
            $objettype . '.groups'                => _n(
                'Requester group',
                'Requester groups',
                Session::getPluralNumber()
            ),
            $objettype . '.observergroups'        => _n('Watcher group', 'Watcher groups', Session::getPluralNumber()),
            $objettype . '.assigntogroups'        => __('Assigned to groups'),
            $objettype . '.solution.type'         => SolutionType::getTypeName(1),
            $objettype . '.solution.description'  => ITILSolution::getTypeName(1),
            $objettype . '.observerusers'         => _n('Watcher', 'Watchers', Session::getPluralNumber()),
            $objettype . '.action'                => _n('Event', 'Events', 1),
            'followup.date'                     => __('Opening date'),
            'followup.isprivate'                => __('Private'),
            'followup.author'                   => __('Writer'),
            'followup.description'              => __('Description'),
            'followup.requesttype'              => RequestType::getTypeName(1),
            $objettype . '.numberoffollowups'     => _x('quantity', 'Number of followups'),
            $objettype . '.numberofunresolved'    => __('Number of unresolved items'),
            $objettype . '.numberofdocuments'     => _x('quantity', 'Number of documents'),
            $objettype . '.costtime'              => __('Time cost'),
            $objettype . '.costfixed'             => __('Fixed cost'),
            $objettype . '.costmaterial'          => __('Material cost'),
            $objettype . '.totalcost'             => __('Total cost'),
            $objettype . '.numberofcosts'         => __('Number of costs'),
            'cost.name'                         => sprintf(
                __('%1$s: %2$s'),
                _n('Cost', 'Costs', 1),
                __('Name')
            ),
            'cost.comment'                      => sprintf(
                __('%1$s: %2$s'),
                _n('Cost', 'Costs', 1),
                __('Comments')
            ),
            'cost.datebegin'                    => sprintf(
                __('%1$s: %2$s'),
                _n('Cost', 'Costs', 1),
                __('Begin date')
            ),
            'cost.dateend'                      => sprintf(
                __('%1$s: %2$s'),
                _n('Cost', 'Costs', 1),
                __('End date')
            ),
            'cost.time'                         => sprintf(
                __('%1$s: %2$s'),
                _n('Cost', 'Costs', 1),
                __('Duration')
            ),
            'cost.costtime'                     => sprintf(
                __('%1$s: %2$s'),
                _n('Cost', 'Costs', 1),
                __('Time cost')
            ),
            'cost.costfixed'                    => sprintf(
                __('%1$s: %2$s'),
                _n('Cost', 'Costs', 1),
                __('Fixed cost')
            ),
            'cost.costmaterial'                 => sprintf(
                __('%1$s: %2$s'),
                _n('Cost', 'Costs', 1),
                __('Material cost')
            ),
            'cost.totalcost'                    => sprintf(
                __('%1$s: %2$s'),
                _n('Cost', 'Costs', 1),
                __('Total cost')
            ),
            'cost.budget'                       => sprintf(
                __('%1$s: %2$s'),
                _n('Cost', 'Costs', 1),
                Budget::getTypeName(1)
            ),
            'task.author'                       => __('Writer'),
            'task.isprivate'                    => __('Private'),
            'task.date'                         => __('Opening date'),
            'task.description'                  => __('Description'),
            'task.categoryid'                   => __('Category id'),
            'task.category'                     => _n('Category', 'Categories', 1),
            'task.categorycomment'              => __('Category comment'),
            'task.time'                         => __('Total duration'),
            'task.user'                         => __('User assigned to task'),
            'task.group'                        => __('Group assigned to task'),
            'task.begin'                        => __('Start date'),
            'task.end'                          => __('End date'),
            'task.status'                       => __('Status'),
            $objettype . '.numberoftasks'         => _x('quantity', 'Number of tasks'),
            $objettype . '.entity.phone'          => sprintf(
                __('%1$s (%2$s)'),
                Entity::getTypeName(1),
                Phone::getTypeName(1)
            ),
            $objettype . '.entity.fax'            => sprintf(
                __('%1$s (%2$s)'),
                Entity::getTypeName(1),
                __('Fax')
            ),
            $objettype . '.entity.website'        => sprintf(
                __('%1$s (%2$s)'),
                Entity::getTypeName(1),
                __('Website')
            ),
            $objettype . '.entity.email'          => sprintf(
                __('%1$s (%2$s)'),
                Entity::getTypeName(1),
                _n('Email', 'Emails', 1)
            ),
            $objettype . '.entity.address'        => sprintf(
                __('%1$s (%2$s)'),
                Entity::getTypeName(1),
                __('Address')
            ),
            $objettype . '.entity.postcode'       => sprintf(
                __('%1$s (%2$s)'),
                Entity::getTypeName(1),
                __('Postal code')
            ),
            $objettype . '.entity.town'           => sprintf(
                __('%1$s (%2$s)'),
                Entity::getTypeName(1),
                __('City')
            ),
            $objettype . '.entity.state'          => sprintf(
                __('%1$s (%2$s)'),
                Entity::getTypeName(1),
                _x('location', 'State')
            ),
            $objettype . '.entity.country'        => sprintf(
                __('%1$s (%2$s)'),
                Entity::getTypeName(1),
                __('Country')
            ),
            $objettype . '.entity.registration_number'        => sprintf(
                __('%1$s (%2$s)'),
                Entity::getTypeName(1),
                __('Administrative Number')
            ),
            'timelineitems.author'              => __('Writer'),
            'timelineitems.date'                => __('Opening date'),
            'timelineitems.type'                => __('Internal type'),
            'timelineitems.typename'            => _n('Type', 'Types', 1),
            'timelineitems.description'         => __('Description'),
            'timelineitems.position'            => __('Position'),

        ];

        foreach ($tags as $tag => $label) {
            $this->addTagToList(['tag'    => $tag,
                'label'  => $label,
                'value'  => true,
                'events' => parent::TAG_FOR_ALL_EVENTS
            ]);
        }

       //Foreach global tags
        $tags = ['log'       => __('Historical'),
            'followups' => _n('Followup', 'Followups', Session::getPluralNumber()),
            'tasks'     => _n('Task', 'Tasks', Session::getPluralNumber()),
            'costs'     => _n('Cost', 'Costs', Session::getPluralNumber()),
            'authors'   => _n('Requester', 'Requesters', Session::getPluralNumber()),
            'suppliers' => _n('Supplier', 'Suppliers', Session::getPluralNumber()),
            'actors' => __('Actors'),
            'timelineitems' => sprintf(__('Processing %1$s'), strtolower($objettype))
        ];

        foreach ($tags as $tag => $label) {
            $this->addTagToList(['tag'     => $tag,
                'label'   => $label,
                'value'   => false,
                'foreach' => true
            ]);
        }

       //Tags with just lang
        $tags = [$objettype . '.days'               => _n('Day', 'Days', Session::getPluralNumber()),
            $objettype . '.attribution'        => __('Assigned to'),
            $objettype . '.entity'             => Entity::getTypeName(1),
            $objettype . '.nocategoryassigned' => __('No defined category'),
            $objettype . '.log'                => __('Historical'),
            $objettype . '.tasks'              => _n('Task', 'Tasks', Session::getPluralNumber()),
            $objettype . '.costs'              => _n('Cost', 'Costs', Session::getPluralNumber()),
            $objettype . '.timelineitems'       => sprintf(__('Processing %1$s'), strtolower($objettype))
        ];

        foreach ($tags as $tag => $label) {
            $this->addTagToList(['tag'   => $tag,
                'label' => $label,
                'value' => false,
                'lang'  => true
            ]);
        }

       //Tags without lang
        $tags = [$objettype . '.urlapprove'     => __('Web link to approval the solution'),
            $objettype . '.entity'         => sprintf(
                __('%1$s (%2$s)'),
                Entity::getTypeName(1),
                __('Complete name')
            ),
            $objettype . '.shortentity'    => sprintf(
                __('%1$s (%2$s)'),
                Entity::getTypeName(1),
                __('Name')
            ),
            $objettype . '.numberoflogs'   => sprintf(
                __('%1$s: %2$s'),
                __('Historical'),
                _x('quantity', 'Number of items')
            ),
            $objettype . '.log.date'       => sprintf(
                __('%1$s: %2$s'),
                __('Historical'),
                _n('Date', 'Dates', 1)
            ),
            $objettype . '.log.user'       => sprintf(
                __('%1$s: %2$s'),
                __('Historical'),
                User::getTypeName(1)
            ),
            $objettype . '.log.field'      => sprintf(
                __('%1$s: %2$s'),
                __('Historical'),
                _n('Field', 'Fields', 1)
            ),
            $objettype . '.log.content'    => sprintf(
                __('%1$s: %2$s'),
                __('Historical'),
                _x('name', 'Update')
            ),
            'document.url'               => sprintf(
                __('%1$s: %2$s'),
                Document::getTypeName(1),
                __('URL')
            ),
            'document.downloadurl'       => sprintf(
                __('%1$s: %2$s'),
                Document::getTypeName(1),
                __('Download URL')
            ),
            'document.heading'           => sprintf(
                __('%1$s: %2$s'),
                Document::getTypeName(1),
                __('Heading')
            ),
            'document.id'                => sprintf(
                __('%1$s: %2$s'),
                Document::getTypeName(1),
                __('ID')
            ),
            'document.filename'          => sprintf(
                __('%1$s: %2$s'),
                Document::getTypeName(1),
                __('File')
            ),
            'document.weblink'           => sprintf(
                __('%1$s: %2$s'),
                Document::getTypeName(1),
                __('Web link')
            ),
            'document.name'              => sprintf(
                __('%1$s: %2$s'),
                Document::getTypeName(1),
                __('Name')
            ),
            $objettype . '.urldocument'   => sprintf(
                __('%1$s: %2$s'),
                Document::getTypeName(Session::getPluralNumber()),
                __('URL')
            )
        ];

        foreach ($tags as $tag => $label) {
            $this->addTagToList(['tag'   => $tag,
                'label' => $label,
                'value' => true,
                'lang'  => false
            ]);
        }

       //Tickets with a fixed set of values
        $status         = $this->obj->getAllStatusArray(false);
        $allowed_ticket = [];
        foreach ($status as $key => $value) {
            $allowed_ticket[] = $key;
        }

        $tags = [$objettype . '.storestatus' => ['text'     => __('Status value in database'),
            'allowed_values'
                                                                  => $allowed_ticket
        ]
        ];
        foreach ($tags as $tag => $label) {
            $this->addTagToList(['tag'            => $tag,
                'label'          => $label['text'],
                'value'          => true,
                'lang'           => false,
                'allowed_values' => $label['allowed_values']
            ]);
        }
    }

    private function getUserPositionFromTimelineItemPosition($position)
    {

        switch ($position) {
            case CommonITILObject::TIMELINE_LEFT:
                $user_position = 'left';
                break;
            case CommonITILObject::TIMELINE_MIDLEFT:
                $user_position = 'left middle';
                break;
            case CommonITILObject::TIMELINE_MIDRIGHT:
                $user_position = 'right middle';
                break;
            case CommonITILObject::TIMELINE_RIGHT:
                $user_position = 'right';
                break;
            default:
                $user_position = 'left';
                break;
        }

        return $user_position;
    }
}
