Files
MYSOPHAL/inc/notificationtemplate.class.php
2025-08-07 13:15:31 +01:00

581 lines
19 KiB
PHP

<?php
/**
* ---------------------------------------------------------------------
* GLPI - Gestionnaire Libre de Parc Informatique
* Copyright (C) 2015-2020 Teclib' and contributors.
*
* http://glpi-project.org
*
* based on GLPI - Gestionnaire Libre de Parc Informatique
* Copyright (C) 2003-2014 by the INDEPNET Development Team.
*
* ---------------------------------------------------------------------
*
* LICENSE
*
* This file is part of GLPI.
*
* GLPI 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 2 of the License, or
* (at your option) any later version.
*
* GLPI 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 GLPI. If not, see <http://www.gnu.org/licenses/>.
* ---------------------------------------------------------------------
*/
if (!defined('GLPI_ROOT')) {
die("Sorry. You can't access this file directly");
}
/**
* NotificationTemplate Class
**/
class NotificationTemplate extends CommonDBTM {
// From CommonDBTM
public $dohistory = true;
//Signature to add to the template
public $signature = '';
//Store templates for each language
public $templates_by_languages = [];
static $rightname = 'config';
static function getTypeName($nb = 0) {
return _n('Notification template', 'Notification templates', $nb);
}
static function canCreate() {
return static::canUpdate();
}
/**
* @since 0.85
**/
static function canPurge() {
return static::canUpdate();
}
function defineTabs($options = []) {
$ong = [];
$this->addDefaultFormTab($ong);
$this->addStandardTab('NotificationTemplateTranslation', $ong, $options);
$this->addStandardTab('Notification_NotificationTemplate', $ong, $options);
$this->addStandardTab('Log', $ong, $options);
return $ong;
}
/**
* Reset already computed templates
**/
function resetComputedTemplates() {
$this->templates_by_languages = [];
}
function showForm($ID, $options = []) {
global $CFG_GLPI;
if (!Config::canUpdate()) {
return false;
}
$spotted = false;
if (empty($ID)) {
if ($this->getEmpty()) {
$spotted = true;
}
} else {
if ($this->getFromDB($ID)) {
$spotted = true;
}
}
$this->showFormHeader($options);
echo "<tr class='tab_bg_1'><td>" . __('Name') . "</td>";
echo "<td colspan='3'>";
Html::autocompletionTextField($this, "name");
echo "</td></tr>";
echo "<tr class='tab_bg_1'><td>" . _n('Type', 'Types', 1) . "</td><td colspan='3'>";
Dropdown::showItemTypes('itemtype', $CFG_GLPI["notificationtemplates_types"],
['value' => ($this->fields['itemtype']
?$this->fields['itemtype'] :'Ticket')]);
echo "</td></tr>";
echo "<tr class='tab_bg_1'><td>".__('Comments')."</td>";
echo "<td colspan='3'>";
echo "<textarea cols='60' rows='5' name='comment' >".$this->fields["comment"]."</textarea>";
echo "</td></tr>";
echo "<tr class='tab_bg_1'><td>".__('CSS')."</td>";
echo "<td colspan='3'>";
echo "<textarea cols='60' rows='5' name='css' >".$this->fields["css"]."</textarea></td></tr>";
$this->showFormButtons($options);
return true;
}
function rawSearchOptions() {
$tab = [];
$tab[] = [
'id' => 'common',
'name' => __('Characteristics')
];
$tab[] = [
'id' => '1',
'table' => $this->getTable(),
'field' => 'name',
'name' => __('Name'),
'datatype' => 'itemlink',
'massiveaction' => false,
'autocomplete' => true,
];
$tab[] = [
'id' => '4',
'table' => $this->getTable(),
'field' => 'itemtype',
'name' => _n('Type', 'Types', 1),
'datatype' => 'itemtypename',
'itemtype_list' => 'notificationtemplates_types',
'massiveaction' => false
];
$tab[] = [
'id' => '16',
'table' => $this->getTable(),
'field' => 'comment',
'name' => __('Comments'),
'datatype' => 'text'
];
return $tab;
}
/**
* Display templates available for an itemtype
*
* @param $name the dropdown name
* @param $itemtype display templates for this itemtype only
* @param $value the dropdown's default value (0 by default)
**/
static function dropdownTemplates($name, $itemtype, $value = 0) {
self::dropdown([
'name' => $name,
'value' => $value,
'comment' => 1,
'condition' => ['itemtype' => addslashes($itemtype)]
]);
}
/**
* @param $options
**/
function getAdditionnalProcessOption($options) {
//Additionnal option can be given for template processing
//For the moment, only option to see private tasks & followups is available
if (!empty($options)
&& isset($options['sendprivate'])) {
return 1;
}
return 0;
}
/**
* @param $target NotificationTarget object
* @param $user_infos array
* @param $event
* @param $options array
*
* @return id of the template in templates_by_languages / false if computation failed
**/
function getTemplateByLanguage(NotificationTarget $target, $user_infos = [],
$event = '', $options = []) {
$lang = [];
$language = $user_infos['language'];
if (isset($user_infos['additionnaloption'])) {
$additionnaloption = $user_infos['additionnaloption'];
} else {
$additionnaloption = [];
}
$tid = $language;
$tid .= serialize($additionnaloption);
$tid = sha1($tid);
if (!isset($this->templates_by_languages[$tid])) {
$generated_by = __('Automatically generated by GLPI');
//Switch to the desired language
$bak_dropdowntranslations = (isset($_SESSION['glpi_dropdowntranslations']) ? $_SESSION['glpi_dropdowntranslations'] : null);
$_SESSION['glpi_dropdowntranslations'] = DropdownTranslation::getAvailableTranslations($language);
Session::loadLanguage($language);
$bak_language = $_SESSION["glpilanguage"];
$_SESSION["glpilanguage"] = $language;
//If event is raised by a plugin, load it in order to get the language file available
if ($plug = isPluginItemType(get_class($target->obj))) {
Plugin::loadLang(strtolower($plug['plugin']), $language);
}
//Get template's language data for in this language
$options['additionnaloption'] = $additionnaloption;
$data = &$target->getForTemplate($event, $options);
$footer_string = Html::entity_decode_deep(__('Automatically generated by GLPI'));
$add_header = Html::entity_decode_deep($target->getContentHeader());
$add_footer = Html::entity_decode_deep($target->getContentFooter());
if ($template_datas = $this->getByLanguage($language)) {
//Template processing
// Decode html chars to have clean text
$template_datas['content_text']
= Html::entity_decode_deep($template_datas['content_text']);
$data = Html::entity_decode_deep($data);
$template_datas['subject'] = Html::entity_decode_deep($template_datas['subject']);
$this->signature = Html::entity_decode_deep($this->signature);
$lang['subject'] = $target->getSubjectPrefix($event) .
self::process($template_datas['subject'], $data);
$lang['content_html'] = '';
//If no html content, then send only in text
if (!empty($template_datas['content_html'])) {
$signature_html = Html::entities_deep($this->signature);
$signature_html = Html::nl2br_deep($signature_html);
$template_datas['content_html'] = self::process(
$template_datas['content_html'],
self::getDataForHtml($target, $data)
);
$lang['content_html'] =
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"
'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>".
"<html>
<head>
<META http-equiv='Content-Type' content='text/html; charset=utf-8'>
<title>".Html::entities_deep($lang['subject'])."</title>
<style type='text/css'>
".$this->fields['css']."
</style>
</head>
<body>\n".(!empty($add_header)?$add_header."\n<br><br>":'').
$template_datas['content_html'].
"<br><br>-- \n<br>".$signature_html.
"<br>$footer_string".
"<br><br>\n".(!empty($add_footer)?$add_footer."\n<br><br>":'').
"\n</body></html>";
}
$lang['content_text']
= (!empty($add_header)?$add_header."\n\n":''). Html::clean(self::process($template_datas['content_text'],
$data)."\n\n-- \n".$this->signature.
"\n".Html::entity_decode_deep($generated_by))."\n\n".$add_footer;
$this->templates_by_languages[$tid] = $lang;
// Restore default language
$_SESSION["glpilanguage"] = $bak_language;
Session::loadLanguage();
if ($bak_dropdowntranslations !== null) {
$_SESSION['glpi_dropdowntranslations'] = $bak_dropdowntranslations;
} else {
unset($_SESSION['glpi_dropdowntranslations']);
}
if ($plug = isPluginItemType(get_class($target->obj))) {
Plugin::loadLang(strtolower($plug['plugin']));
}
}
}
if (isset($this->templates_by_languages[$tid])) {
return $tid;
}
return false;
}
/**
* @param $string
* @param $data
**/
static function process($string, $data) {
$offset = $new_offset = 0;
//Template processed
$output = "";
$cleandata = [];
// clean data for strtr
foreach ($data as $field => $value) {
if (!is_array($value)) {
$cleandata[$field] = $value;
}
}
//Remove all
$string = Toolbox::unclean_cross_side_scripting_deep($string);
//First of all process the FOREACH tag
if (preg_match_all("/##FOREACH[ ]?(FIRST|LAST)?[ ]?([0-9]*)?[ ]?([a-z-0-9\._]*)##/i",
$string, $out)) {
foreach ($out[3] as $id => $tag_infos) {
$regex = "/".$out[0][$id]."(.*)##ENDFOREACH".$tag_infos."##/Uis";
if (preg_match($regex, $string, $tag_out)
&& isset($data[$tag_infos])
&& is_array($data[$tag_infos])) {
$data_lang_foreach = $cleandata;
unset($data_lang_foreach[$tag_infos]);
//Manage FIRST & LAST statement
$foreachvalues = $data[$tag_infos];
if (!empty($foreachvalues)) {
if (isset($out[1][$id]) && ($out[1][$id] != '')) {
if ($out[1][$id] == 'FIRST') {
$foreachvalues = array_reverse($foreachvalues);
}
if (isset($out[2][$id]) && $out[2][$id]) {
$foreachvalues = array_slice($foreachvalues, 0, $out[2][$id]);
} else {
$foreachvalues = array_slice($foreachvalues, 0, 1);
}
}
}
$output_foreach_string = "";
foreach ($foreachvalues as $line) {
foreach ($line as $field => $value) {
if (!is_array($value)) {
$data_lang_foreach[$field] = $value;
}
}
$tmp = self::processIf($tag_out[1], $data_lang_foreach);
$output_foreach_string .= strtr($tmp, $data_lang_foreach);
}
$string = str_replace($tag_out[0], $output_foreach_string, $string);
} else {
$string = str_replace($tag_out, '', $string);
}
}
}
//Now process IF statements
$string = self::processIf($string, $cleandata);
$string = strtr($string, $cleandata);
return $string;
}
/**
* @param $string
* @param $data
**/
static function processIf($string, $data) {
if (preg_match_all("/##IF([a-z-0-9\._]*)[=]?(.*?)##/i", $string, $out)) {
foreach ($out[1] as $key => $tag_infos) {
$if_field = $tag_infos;
//Get the field tag value (if one)
$regex_if = "/##IF".$if_field."[=]?.*##(.*)##ENDIF".$if_field."##/Uis";
//Get the else tag value (if one)
$regex_else = "/##ELSE".$if_field."[=]?.*##(.*)##ENDELSE".$if_field."##/Uis";
$condition_ok = false;
if (empty($out[2][$key]) && !strlen($out[2][$key])) { // No = : check if ot empty or not null
if (isset($data['##'.$if_field.'##'])
&& $data['##'.$if_field.'##'] != '0'
&& $data['##'.$if_field.'##'] != ''
&& $data['##'.$if_field.'##'] != '&nbsp;'
&& !is_null($data['##'.$if_field.'##'])) {
$condition_ok = true;
}
} else { // check exact match
if (isset($data['##'.$if_field.'##'])) {
// Data value: the value for the field in the database
$data_value = Html::entity_decode_deep($data['##'.$if_field.'##']);
// Condition value: the expected value needed to validate the condition
$condition_value = Html::entity_decode_deep($out[2][$key]);
// Special case for data returned by Dropdown::getYesNo, we
// need to use the localized value in the comparison
$to_translate = ["Yes", "No"];
if (in_array($condition_value, $to_translate)) {
$condition_value = __($condition_value);
}
// Compare data value and condition value
$condition_ok = $condition_value == $data_value;
}
}
// Force only one replacement to permit multiple use of the same condition
if ($condition_ok) { // Do IF
$string = preg_replace($regex_if, "\\1", $string, 1);
$string = preg_replace($regex_else, "", $string, 1);
} else { // Do ELSE
$string = preg_replace($regex_if, "", $string, 1);
$string = preg_replace($regex_else, "\\1", $string, 1);
}
}
}
return $string;
}
/**
* Convert notification data to HTML format.
*
* @param NotificationTarget $target
* @param array $data
* @return array
*/
private static function getDataForHtml(NotificationTarget $target, array $data) {
$dataForHtml = [];
foreach ($data as $tag => $value) {
if (is_array($value)) {
$dataForHtml[$tag] = self::getDataForHtml($target, $value);
} else if (!in_array($tag, $target->html_tags)) {
$dataForHtml[$tag] = Html::nl2br_deep(Html::entities_deep($value));
} else {
$dataForHtml[$tag] = $value;
}
}
return $dataForHtml;
}
/**
* @param $signature
**/
function setSignature($signature) {
$this->signature = $signature;
}
/**
* @param $language
**/
function getByLanguage($language) {
global $DB;
$iterator = $DB->request([
'FROM' => 'glpi_notificationtemplatetranslations',
'WHERE' => [
'notificationtemplates_id' => $this->getField('id'),
'language' => [$language, '']
],
'ORDER' => 'language DESC',
'LIMIT' => 1
]);
if (count($iterator)) {
return $iterator->next();
}
//No template found at all!
return false;
}
/**
* @param NotificationTarget $target Target instance
* @param string $tid template computed id
* @param mixed $to Recipient
* @param array $user_infos Extra user infos
* @param array $options Options
*
* @return array
**/
function getDataToSend(NotificationTarget $target, $tid, $to, array $user_infos, array $options) {
$language = $user_infos['language'];
$user_name = $user_infos['username'];
$sender = $target->getSender();
$replyto = $target->getReplyTo($options);
$mailing_options['to'] = $to;
$mailing_options['toname'] = $user_name;
$mailing_options['from'] = $sender['email'];
$mailing_options['fromname'] = $sender['name'];
$mailing_options['replyto'] = $replyto['email'];
$mailing_options['replytoname'] = $replyto['name'];
$mailing_options['messageid'] = $target->getMessageID();
$template_data = $this->templates_by_languages[$tid];
$mailing_options['subject'] = $template_data['subject'];
$mailing_options['content_html'] = $template_data['content_html'];
$mailing_options['content_text'] = $template_data['content_text'];
$mailing_options['items_id'] = method_exists($target->obj, "getField")
? $target->obj->getField('id')
: 0;
if (isset($target->obj->documents)) {
$mailing_options['documents'] = $target->obj->documents;
}
return $mailing_options;
}
function cleanDBonPurge() {
$this->deleteChildrenAndRelationsFromDb(
[
Notification_NotificationTemplate::class,
NotificationTemplateTranslation::class,
]
);
// QueuedNotification does not extends CommonDBConnexity
$queued = new QueuedNotification();
$queued->deleteByCriteria(['notificationtemplates_id' => $this->fields['id']]);
}
}