. * --------------------------------------------------------------------- */ if (!defined('GLPI_ROOT')) { die("Sorry. You can't access this file directly"); } /** QueuedNotification class * * @since 0.85 **/ class QueuedNotification extends CommonDBTM { static $rightname = 'queuednotification'; static function getTypeName($nb = 0) { return __('Notification queue'); } static function canCreate() { // Everybody can create : human and cron return Session::getLoginUserID(false); } static function getForbiddenActionsForMenu() { return ['add']; } function getForbiddenStandardMassiveAction() { $forbidden = parent::getForbiddenStandardMassiveAction(); $forbidden[] = 'update'; return $forbidden; } /** * @see CommonDBTM::getSpecificMassiveActions() **/ function getSpecificMassiveActions($checkitem = null, $is_deleted = false) { $isadmin = static::canUpdate(); $actions = parent::getSpecificMassiveActions($checkitem); if ($isadmin && !$is_deleted) { $actions[__CLASS__.MassiveAction::CLASS_ACTION_SEPARATOR.'sendmail'] = _x('button', 'Send'); } return $actions; } /** * @see CommonDBTM::processMassiveActionsForOneItemtype() **/ static function processMassiveActionsForOneItemtype(MassiveAction $ma, CommonDBTM $item, array $ids) { switch ($ma->getAction()) { case 'sendmail' : foreach ($ids as $id) { if ($item->canEdit($id)) { if ($item->sendById($id)) { $ma->itemDone($item->getType(), $id, MassiveAction::ACTION_OK); } else { $ma->itemDone($item->getType(), $id, MassiveAction::ACTION_KO); } } else { $ma->itemDone($item->getType(), $id, MassiveAction::ACTION_NORIGHT); } } return; } parent::processMassiveActionsForOneItemtype($ma, $item, $ids); } function prepareInputForAdd($input) { global $DB; if (!isset($input['create_time']) || empty($input['create_time'])) { $input['create_time'] = $_SESSION["glpi_currenttime"]; } if (!isset($input['send_time']) || empty($input['send_time'])) { $toadd = 0; if (isset($input['entities_id'])) { $toadd = Entity::getUsedConfig('delay_send_emails', $input['entities_id']); } if ($toadd > 0) { $input['send_time'] = date("Y-m-d H:i:s", strtotime($_SESSION["glpi_currenttime"]) +$toadd*MINUTE_TIMESTAMP); } else { $input['send_time'] = $_SESSION["glpi_currenttime"]; } } $input['sent_try'] = 0; if (isset($input['headers']) && is_array($input['headers']) && count($input['headers'])) { $input["headers"] = exportArrayToDB($input['headers']); } else { $input['headers'] = ''; } if (isset($input['documents']) && is_array($input['documents']) && count($input['documents'])) { $input["documents"] = exportArrayToDB($input['documents']); } else { $input['documents'] = ''; } // Force items_id to integer if (!isset($input['items_id']) || empty($input['items_id'])) { $input['items_id'] = 0; } // Drop existing mails in queue for the same event and item and recipient if (isset($input['itemtype']) && !empty($input['itemtype']) && isset($input['entities_id']) && ($input['entities_id'] >= 0) && isset($input['items_id']) && ($input['items_id'] >= 0) && isset($input['notificationtemplates_id']) && !empty($input['notificationtemplates_id']) && isset($input['recipient'])) { $criteria = [ 'FROM' => $this->getTable(), 'WHERE' => [ 'is_deleted' => 0, 'itemtype' => $input['itemtype'], 'items_id' => $input['items_id'], 'entities_id' => $input['entities_id'], 'notificationtemplates_id' => $input['notificationtemplates_id'], 'recipient' => $input['recipient'] ] ]; $iterator = $DB->request($criteria); while ($data = $iterator->next()) { $this->delete(['id' => $data['id']], 1); } } return $input; } function rawSearchOptions() { $tab = []; $tab[] = [ 'id' => 'common', 'name' => __('Characteristics') ]; $tab[] = [ 'id' => '1', 'table' => $this->getTable(), 'field' => 'name', 'name' => __('Subject'), 'datatype' => 'itemlink', 'massiveaction' => false ]; $tab[] = [ 'id' => '2', 'table' => $this->getTable(), 'field' => 'id', 'name' => __('ID'), 'massiveaction' => false, 'datatype' => 'number' ]; $tab[] = [ 'id' => '16', 'table' => $this->getTable(), 'field' => 'create_time', 'name' => __('Creation date'), 'datatype' => 'datetime', 'massiveaction' => false ]; $tab[] = [ 'id' => '3', 'table' => $this->getTable(), 'field' => 'send_time', 'name' => __('Expected send date'), 'datatype' => 'datetime', 'massiveaction' => false ]; $tab[] = [ 'id' => '4', 'table' => $this->getTable(), 'field' => 'sent_time', 'name' => __('Send date'), 'datatype' => 'datetime', 'massiveaction' => false ]; $tab[] = [ 'id' => '5', 'table' => $this->getTable(), 'field' => 'sender', 'name' => __('Sender email'), 'datatype' => 'text', 'massiveaction' => false ]; $tab[] = [ 'id' => '6', 'table' => $this->getTable(), 'field' => 'sendername', 'name' => __('Sender name'), 'datatype' => 'string', 'massiveaction' => false ]; $tab[] = [ 'id' => '7', 'table' => $this->getTable(), 'field' => 'recipient', 'name' => __('Recipient email'), 'datatype' => 'string', 'massiveaction' => false ]; $tab[] = [ 'id' => '8', 'table' => $this->getTable(), 'field' => 'recipientname', 'name' => __('Recipient name'), 'datatype' => 'string', 'massiveaction' => false ]; $tab[] = [ 'id' => '9', 'table' => $this->getTable(), 'field' => 'replyto', 'name' => __('Reply-to email'), 'datatype' => 'string', 'massiveaction' => false ]; $tab[] = [ 'id' => '10', 'table' => $this->getTable(), 'field' => 'replytoname', 'name' => __('Reply-to name'), 'datatype' => 'string', 'massiveaction' => false ]; $tab[] = [ 'id' => '11', 'table' => $this->getTable(), 'field' => 'headers', 'name' => __('Additional headers'), 'datatype' => 'specific', 'massiveaction' => false ]; $tab[] = [ 'id' => '12', 'table' => $this->getTable(), 'field' => 'body_html', 'name' => __('Email HTML body'), 'datatype' => 'text', 'massiveaction' => false, 'htmltext' => true ]; $tab[] = [ 'id' => '13', 'table' => $this->getTable(), 'field' => 'body_text', 'name' => __('Email text body'), 'datatype' => 'text', 'massiveaction' => false ]; $tab[] = [ 'id' => '14', 'table' => $this->getTable(), 'field' => 'messageid', 'name' => __('Message ID'), 'datatype' => 'string', 'massiveaction' => false ]; $tab[] = [ 'id' => '15', 'table' => $this->getTable(), 'field' => 'sent_try', 'name' => __('Number of tries of sent'), 'datatype' => 'integer', 'massiveaction' => false ]; $tab[] = [ 'id' => '20', 'table' => $this->getTable(), 'field' => 'itemtype', 'name' => _n('Type', 'Types', 1), 'datatype' => 'itemtype', 'massiveaction' => false ]; $tab[] = [ 'id' => '21', 'table' => $this->getTable(), 'field' => 'items_id', 'name' => __('Associated item ID'), 'massiveaction' => false, 'datatype' => 'integer' ]; $tab[] = [ 'id' => '22', 'table' => 'glpi_notificationtemplates', 'field' => 'name', 'name' => _n('Notification template', 'Notification templates', 1), 'massiveaction' => false, 'datatype' => 'dropdown' ]; $tab[] = [ 'id' => '23', 'table' => 'glpi_queuednotifications', 'field' => 'mode', 'name' => __('Mode'), 'massiveaction' => false, 'datatype' => 'specific', 'searchtype' => [ 0 => 'equals', 1 => 'notequals' ] ]; $tab[] = [ 'id' => '80', 'table' => 'glpi_entities', 'field' => 'completename', 'name' => Entity::getTypeName(1), 'massiveaction' => false, 'datatype' => 'dropdown' ]; return $tab; } /** * @param $field * @param $values * @param $options array **/ static function getSpecificValueToDisplay($field, $values, array $options = []) { if (!is_array($values)) { $values = [$field => $values]; } switch ($field) { case 'headers' : $values[$field] = importArrayFromDB($values[$field]); $out=''; if (is_array($values[$field]) && count($values[$field])) { foreach ($values[$field] as $key => $val) { $out .= $key.': '.$val.'
'; } } return $out; break; case 'mode': $out = Notification_NotificationTemplate::getMode($values[$field])['label']; return $out; break; } return parent::getSpecificValueToDisplay($field, $values, $options); } static function getSpecificValueToSelect($field, $name = '', $values = '', array $options = []) { if (!is_array($values)) { $values = [$field => $values]; } $options['display'] = false; switch ($field) { case 'mode' : $options['name'] = $name; $options['value'] = $values[$field]; return Notification_NotificationTemplate::dropdownMode($options); } return parent::getSpecificValueToSelect($field, $name, $values, $options); } /** * Send notification in queue * * @param integer $ID Id * * @return boolean */ public function sendById($ID) { if ($this->getFromDB($ID)) { $mode = $this->getField('mode'); $eventclass = 'NotificationEvent' . ucfirst($mode); $conf = Notification_NotificationTemplate::getMode($mode); if ($conf['from'] != 'core') { $eventclass = 'Plugin' . ucfirst($conf['from']) . $eventclass; } return $eventclass::send([$this->fields]); } else { return false; } } /** * Give cron information * * @param $name : task's name * * @return array of information **/ static function cronInfo($name) { switch ($name) { case 'queuednotification' : return ['description' => __('Send mails in queue'), 'parameter' => __('Maximum emails to send at once')]; case 'queuednotificationclean' : return ['description' => __('Clean notification queue'), 'parameter' => __('Days to keep sent emails')]; } return []; } /** * Get pending notifications in queue * * @param string $send_time Maximum sent_time * @param integer $limit Query limit clause * @param array $limit_modes Modes to limit to * @param array $extra_where Extra params to add to the where clause * * @return array */ static public function getPendings($send_time = null, $limit = 20, $limit_modes = null, $extra_where = []) { global $DB, $CFG_GLPI; if ($send_time === null) { $send_time = date('Y-m-d H:i:s'); } $base_query = [ 'FROM' => self::getTable(), 'WHERE' => [ 'is_deleted' => 0, 'mode' => 'TOFILL', 'send_time' => ['<', $send_time], ] + $extra_where, 'ORDER' => 'send_time ASC', 'START' => 0, 'LIMIT' => $limit ]; $pendings = []; $modes = Notification_NotificationTemplate::getModes(); foreach ($modes as $mode => $conf) { $eventclass = 'NotificationEvent' . ucfirst($mode); if ($conf['from'] != 'core') { $eventclass = 'Plugin' . ucfirst($conf['from']) . $eventclass; } if ($limit_modes !== null && !in_array($mode, $limit_modes) || !$CFG_GLPI['notifications_' . $mode] || !$eventclass::canCron() ) { //mode is not in limits, is disabled, or cannot be called from cron, passing continue; } $query = $base_query; $query['WHERE']['mode'] = $mode; $iterator = $DB->request($query); if ($iterator->numRows() > 0) { $pendings[$mode] = []; while ($row = $iterator->next()) { $pendings[$mode][] = $row; } } } return $pendings; } /** * Cron action on notification queue: send notifications in queue * * @param CommonDBTM $task for log (default NULL) * * @return integer either 0 or 1 **/ static function cronQueuedNotification($task = null) { if (!Notification_NotificationTemplate::hasActiveMode()) { return 0; } $cron_status = 0; // Send notifications at least 1 minute after adding in queue to be sure that process on it is finished $send_time = date("Y-m-d H:i:s", strtotime("+1 minutes")); $pendings = self::getPendings( $send_time, $task->fields['param'] ); foreach ($pendings as $mode => $data) { $eventclass = 'NotificationEvent' . ucfirst($mode); $conf = Notification_NotificationTemplate::getMode($mode); if ($conf['from'] != 'core') { $eventclass = 'Plugin' . ucfirst($conf['from']) . $eventclass; } $result = $eventclass::send($data); if ($result !== false) { $cron_status = 1; if (!is_null($task)) { $task->addVolume($result); } } } return $cron_status; } /** * Cron action on queued notification: clean notification queue * * @param CommonDBTM $task for log (default NULL) * * @return integer either 0 or 1 **/ static function cronQueuedNotificationClean($task = null) { global $DB; $vol = 0; // Expire mails in queue if ($task->fields['param'] > 0) { $secs = $task->fields['param'] * DAY_TIMESTAMP; $send_time = date("U") - $secs; $DB->delete( self::getTable(), [ 'is_deleted' => 1, new \QueryExpression('(UNIX_TIMESTAMP('.$DB->quoteName('send_time').') < '.$DB->quoteValue($send_time).')') ] ); $vol = $DB->affectedRows(); } $task->setVolume($vol); return ($vol > 0 ? 1 : 0); } /** * Force sending all mails in queue for a specific item * * @param string $itemtype item type * @param integer $items_id id of the item * * @return void **/ static function forceSendFor($itemtype, $items_id) { if (!empty($itemtype) && !empty($items_id)) { $pendings = self::getPendings( null, 1, null, [ 'itemtype' => $itemtype, 'items_id' => $items_id ] ); foreach ($pendings as $mode => $data) { $eventclass = Notification_NotificationTemplate::getModeClass($mode, 'event'); $eventclass::send($data); } } } /** * Print the queued mail form * * @param integer $ID ID of the item * @param array $options Options * * @return true if displayed false if item not found or not right to display **/ function showForm($ID, $options = []) { if (!Session::haveRight("queuednotification", READ)) { return false; } $this->check($ID, READ); $options['canedit'] = false; $this->showFormHeader($options); echo ""; echo ""._n('Type', 'Types', 1).""; echo ""; if (!($item = getItemForItemtype($this->fields['itemtype']))) { echo NOT_AVAILABLE; echo ""; echo ""._n('Item', 'Items', 1).""; echo ""; echo NOT_AVAILABLE; } else if ($item instanceof CommonDBTM) { echo $item->getType(); $item->getFromDB($this->fields['items_id']); echo ""; echo ""._n('Item', 'Items', 1).""; echo ""; echo $item->getLink(); } else { echo get_class($item); echo ""; } echo ""; echo ""; echo ""._n('Notification template', 'Notification templates', 1).""; echo ""; echo Dropdown::getDropdownName('glpi_notificationtemplates', $this->fields['notificationtemplates_id']); echo ""; echo " "; echo " "; echo ""; echo ""; echo "".__('Creation date').""; echo ""; echo Html::convDateTime($this->fields['create_time']); echo "".__('Expected send date').""; echo "".Html::convDateTime($this->fields['send_time']).""; echo ""; echo ""; echo "".__('Send date').""; echo "".Html::convDateTime($this->fields['sent_time']).""; echo "".__('Number of tries of sent').""; echo "".$this->fields['sent_try'].""; echo ""; echo ""._n('Email', 'Emails', 1).""; echo ""; echo "".__('Sender email').""; echo "".$this->fields['sender'].""; echo "".__('Sender name').""; echo "".$this->fields['sendername'].""; echo ""; echo ""; echo "".__('Recipient email').""; echo "".$this->fields['recipient'].""; echo "".__('Recipient name').""; echo "".$this->fields['recipientname'].""; echo ""; echo ""; echo "".__('Reply-to email').""; echo "".$this->fields['replyto'].""; echo "".__('Reply-to name').""; echo "".$this->fields['replytoname'].""; echo ""; echo ""; echo "".__('Message ID').""; echo "".$this->fields['messageid'].""; echo "".__('Additional headers').""; echo "".self::getSpecificValueToDisplay('headers', $this->fields).""; echo ""; echo ""; echo "".__('Subject').""; echo "".$this->fields['name'].""; echo ""; echo "".__('Email HTML body').""; echo "".__('Email text body').""; echo ""; echo ""; echo "".self::cleanHtml($this->fields['body_html']).""; echo "".nl2br($this->fields['body_text'], false).""; echo ""; $this->showFormButtons($options); return true; } /** * @since 0.85 * * @param $string **/ static function cleanHtml($string) { $begin_strip = -1; $end_strip = -1; $begin_match = "//"; $end_match = "/<\/body>/"; $content = explode("\n", $string); $newstring = ''; foreach ($content as $ID => $val) { // Get last tag for end if ($begin_strip >= 0) { if (preg_match($end_match, $val)) { $end_strip = $ID; continue; } } if (($begin_strip >= 0) && ($end_strip < 0)) { $newstring .= $val; } // Get first tag for begin if ($begin_strip < 0) { if (preg_match($begin_match, $val)) { $begin_strip = $ID; } } } return nl2br($newstring, false); } static function getIcon() { return "far fa-list-alt"; } }