%PDF- %PDF-
Direktori : /var/www/projetos/suporte.iigd.com.br/src/RichText/ |
Current File : /var/www/projetos/suporte.iigd.com.br/src/RichText/UserMention.php |
<?php /** * --------------------------------------------------------------------- * * GLPI - Gestionnaire Libre de Parc Informatique * * http://glpi-project.org * * @copyright 2015-2024 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/>. * * --------------------------------------------------------------------- */ namespace Glpi\RichText; use CommonDBTM; use CommonITILActor; use CommonITILObject; use CommonITILTask; use CommonITILValidation; use Glpi\Toolbox\Sanitizer; use DOMDocument; use ITILFollowup; use ITILSolution; use NotificationEvent; use User; final class UserMention { /** * Handle user mentions. * Add newly mention users to observers and send them a notification. * * @return void */ public static function handleUserMentions(CommonDBTM $item): void { $content_fields = $item instanceof CommonITILValidation ? ['comment_submission', 'comment_validation'] : ['content']; $previously_mentionned_actors_ids = []; $mentionned_actors_ids = []; foreach ($content_fields as $content_field) { if (!array_key_exists($content_field, $item->fields) && !array_key_exists($content_field, $item->input)) { // Field is not define in both `$item->fields` and `$item->input`, it means that // it is certainly not a valid field for current item. continue; } if (array_key_exists($content_field, $item->oldvalues)) { // Update case: content field was updated $previous_value = $item->oldvalues[$content_field]; } else if (count($item->updates) > 0) { // Update case: content field was not updated $previous_value = $item->fields[$content_field]; } else { // Creation case $previous_value = null; } $new_value = $item->input[$content_field] ?? null; if ($new_value !== null) { $mentionned_actors_ids = array_merge( $mentionned_actors_ids, self::getUserIdsFromUserMentions($new_value) ); } if ($previous_value !== null) { $previously_mentionned_actors_ids = array_merge( $previously_mentionned_actors_ids, self::getUserIdsFromUserMentions($previous_value) ); } } // Keep only newly mentioned actors $mentionned_actors_ids = array_diff($mentionned_actors_ids, $previously_mentionned_actors_ids); if (empty($mentionned_actors_ids)) { return; } // Retrieve main item $main_item = $item; $options = []; if ($item instanceof CommonITILTask) { $options = [ 'task_id' => $item->fields['id'], 'is_private' => $item->isPrivate(), ]; $main_item = $item->getItem(); } else if ($item instanceof CommonITILValidation) { $options = [ 'validation_id' => $item->fields['id'], 'validation_status' => $item->fields['status'] ]; $main_item = getItemForItemtype($item->getItilObjectItemType()); $main_item->getFromDB($item->fields[$item::$items_id]); } else if ($item instanceof ITILFollowup) { $options = [ 'followup_id' => $item->fields['id'], 'is_private' => $item->isPrivate(), ]; $main_item = getItemForItemtype($item->fields['itemtype']); $main_item->getFromDB($item->fields['items_id']); } else if ($item instanceof ITILSolution) { $main_item = getItemForItemtype($item->fields['itemtype']); $main_item->getFromDB($item->fields['items_id']); } // Send a "you have been mentioned" notification foreach ($mentionned_actors_ids as $user_id) { $options['users_id'] = $user_id; NotificationEvent::raiseEvent('user_mention', $main_item, $options); } if ($main_item instanceof CommonITILObject) { if (empty($main_item->userlinkclass) || !class_exists($main_item->userlinkclass)) { return; // Cannot add observers } // Retrieve current actors list $userlink = new $main_item->userlinkclass(); $current_actors_ids = []; $current_actors = $userlink->getActors($main_item->fields['id']); foreach ($current_actors as $actors) { foreach ($actors as $actor) { $current_actors_ids[] = $actor['users_id']; } } // Add newly mentioned actors as observers foreach ($mentionned_actors_ids as $user_id) { if (in_array($user_id, $current_actors_ids)) { continue; } $input = [ 'type' => CommonITILActor::OBSERVER, 'users_id' => $user_id, $main_item->getForeignKeyField() => $main_item->fields['id'], '_do_not_compute_takeintoaccount' => true, '_from_object' => true, ]; $userlink->add($input); } } } /** * Extract ids of mentioned users. * * @param string $content * * @return int[] */ public static function getUserIdsFromUserMentions(string $content) { $ids = []; if (empty($content)) { return $ids; } try { $content = Sanitizer::getVerbatimValue($content); $dom = new DOMDocument(); libxml_use_internal_errors(true); $dom->loadHTML($content); // TODO In GLPI 10.1, find a way to remove usage of this `@` operator // that was added to prevent Error E_WARNING simplexml_import_dom(): Invalid Nodetype to import // with bad HTML content. $content_as_xml = @simplexml_import_dom($dom); } catch (\Throwable $e) { // Sanitize process does not handle correctly `<` and `>` chars that are not surrounding html tags. // This generates invalid HTML that cannot be loaded by `SimpleXMLElement`. return []; } if ($content_as_xml === null) { return []; } $mention_elements = $content_as_xml->xpath('//*[@data-user-mention="true"]'); foreach ($mention_elements as $mention_element) { $ids[] = (int)$mention_element->attributes()->{'data-user-id'}; } return $ids; } /** * Refresh user mentions HTML in order to display them. * User name is updated, and a link to user page could be added on mention. * * @param string $content * * @return string */ public static function refreshUserMentionsHtmlToDisplay(string $content): string { $mentionned_users_ids = self::getUserIdsFromUserMentions($content); foreach ($mentionned_users_ids as $user_id) { $user = new User(); if (!$user->getFromDB($user_id)) { // User does not exist anymore, keep the mention but do not add link. continue; } $pattern = '/' // <span data-user-mention="true" ...> . '<span[^>]*' . '(' . 'data-user-mention="true"[^>]+data-user-id="' . $user_id . '"' . '|' . 'data-user-id="' . $user_id . '"[^>]+data-user-mention="true"' . ')' . '[^>]*>' // @Name . '@[^>]+' // span closing . '<\/span>' . '/'; $replacement = sprintf( '<a class="user-mention" href="%s">@%s</a>', $user->getLinkURL(), $user->getFriendlyName() ); $content = preg_replace($pattern, $replacement, $content); } return $content; } }