Files
iTop/main.combodo-notify-on-expiration.php
Vincent Dumas 101b485657 First version of Notify on expiration
SVN:trunk[6919]
2018-07-19 14:52:09 +00:00

282 lines
8.7 KiB
PHP

<?php
// Copyright (C) 2012-2018 Combodo SARL
//
// 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; version 3 of the License.
//
// 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, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Module combodo-notify-on-expiration
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @author Vincent Dumas <vincent.dumas@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
/**
* Class NotifyOnExpirationExec
*/
class NotifyOnExpiration implements iScheduledProcess
{
const MODULE_CODE = 'combodo-notify-on-expiration';
const MODULE_SETTING_ENABLED = 'enabled';
const MODULE_SETTING_DEBUG = 'debug';
const MODULE_SETTING_WEEKDAYS = 'week_days';
const MODULE_SETTING_TIME = 'time';
const DEFAULT_MODULE_SETTING_ENABLED = true;
const DEFAULT_MODULE_SETTING_DEBUG = false;
const DEFAULT_MODULE_SETTING_WEEKDAYS = 'monday, tuesday, wednesday, thursday, friday, saturday, sunday';
const DEFAULT_MODULE_SETTING_TIME = '03:00';
protected $bDebug;
/**
* NotifyOnExpiration constructor.
*/
function __construct()
{
$this->bDebug = (bool) MetaModel::GetModuleSetting(static::MODULE_CODE, static::MODULE_SETTING_DEBUG, static::DEFAULT_MODULE_SETTING_DEBUG);
}
/**
* Gives the exact time at which the process must be run next time
*
* @return \DateTime
*/
public function GetNextOccurrence()
{
$bEnabled = MetaModel::GetConfig()->GetModuleSetting(static::MODULE_CODE, static::MODULE_SETTING_ENABLED, static::DEFAULT_MODULE_SETTING_ENABLED);
if (!$bEnabled)
{
$oRet = new DateTime('3000-01-01');
}
else
{
// 1st - Interpret the list of days as ordered numbers (monday = 1)
//
$aDays = $this->InterpretWeekDays();
// 2nd - Find the next active week day
//
$sRunTime = MetaModel::GetConfig()->GetModuleSetting(static::MODULE_CODE, static::MODULE_SETTING_TIME, static::DEFAULT_MODULE_SETTING_TIME);
if (!preg_match('/^([01]?\d|2[0-3]):[0-5]?\d(:[0-5]?\d)?$/', $sRunTime, $aMatches))
{
throw new Exception(static::MODULE_CODE.": wrong format for setting 'time' (found '$sRunTime')");
}
$oNow = new DateTime();
$iNextPos = false;
for ($iDay = $oNow->format('N') ; $iDay <= 7 ; $iDay++)
{
$iNextPos = array_search($iDay, $aDays);
if ($iNextPos !== false)
{
if (($iDay > $oNow->format('N')) || ($oNow->format('H:i') < $sRunTime))
{
break;
}
$iNextPos = false; // necessary on sundays
}
}
// 3rd - Compute the result
//
if ($iNextPos === false)
{
// Jump to the first day within the next week
$iFirstDayOfWeek = $aDays[0];
$iDayMove = $oNow->format('N') - $iFirstDayOfWeek;
$oRet = clone $oNow;
$oRet->modify('-'.$iDayMove.' days');
$oRet->modify('+1 weeks');
}
else
{
$iNextDayOfWeek = $aDays[$iNextPos];
$iMove = $iNextDayOfWeek - $oNow->format('N');
$oRet = clone $oNow;
$oRet->modify('+'.$iMove.' days');
}
$oRet->setTime((int)$aMatches[1], (int) $aMatches[2]);
}
return $oRet;
}
/**
* @inheritdoc
*/
public function Process($iTimeLimit)
{
$aReport = array(
'reached_deadline' => 0,
'triggered' => array(),
'not_triggered' => array(),
);
$oRulesSearch = DBObjectSearch::FromOQL('SELECT ExpirationRule WHERE status = "active"');
$oRulesSet = new DBObjectSet($oRulesSearch);
$this->Trace('Processing '.$oRulesSet->Count().' active expiration rules...');
$iTotalProcessedObjectsCount = 0;
while($oRule = $oRulesSet->Fetch())
{
$iRuleProcessedObjectsCount = 0;
$this->Trace('Processing rule "'.$oRule->Get('friendlyname').'" (#'.$oRule->GetKey().')...');
try
{
// Retrieving rule's params
$sClass = $oRule->Get('class');
$oSearch = $oRule->GetFilter();
$this->Trace('|- Parameters:');
$this->Trace('| |- Class: '.$sClass);
$this->Trace('| |- OQL scope: '.$oSearch->ToOQL(true));
// Prepare the Rule information to be passed to the notification
$aRuleContext = $oRule->ToArgs('rule');
// Get applicable Triggers for this object class
$sClassList = implode("', '", MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL));
$oTriggerSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnExpirationRule AS t WHERE t.target_class IN ('$sClassList')"));
$oSet = new DBObjectSet($oSearch);
$this->Trace('|- Objects:');
/** @var $oToTrigger DBObject */
while ((time() < $iTimeLimit) && $oToTrigger = $oSet->Fetch())
{
// Catching exceptions so the process don't get stucked on this object
try
{
$aReport['reached_deadline']++;
//
// $aContext['ruleName'] = $oRule->Get('name');
// Combine the current object :this and :rule to be available in the notification
$aContext = $oToTrigger->ToArgs('this');
$aContext = array_merge($aContext, $aRuleContext);
while ($oTrigger = $oTriggerSet->Fetch())
{
$oTrigger->DoActivate($aContext);
}
// The same set of Triggers is reused for each object returned by the Rule as they all belongs to the same class
$oTriggerSet->Rewind();
$iRuleProcessedObjectsCount++;
$iTotalProcessedObjectsCount++;
$aReport['triggered'][] = $oToTrigger->Get('friendlyname');
$this->Trace('| |- [OK] '.$sClass.' #'.$oToTrigger->GetKey());
} // Trigger was NOT applied because of an exception, which is NOT normal
catch (Exception $e)
{
$aReport['not_triggered'][] = $oToClose->Get('friendlyname');
$this->Trace('| |- [KO] /!\\ '.$sClass.' #'.$oToTrigger->GetKey().' exception raised! Error message: '.$e->getMessage());
}
}
$this->Trace('|- Processed rule "'.$oRule->Get('friendlyname').'" (#'.$oRule->GetKey().') : '.$iRuleProcessedObjectsCount.' out of '.$oSet->Count().'.');
// Info to help understand why not all objects have been processed during this batch.
if (time() >= $iTimeLimit)
{
$this->Trace('Stopped because time limit exceeded!');
}
}
catch(Exception $e)
{
$this->Trace('Skipping rule as there was an exception! ('.$e->getMessage().')');
}
}
// Report
if($aReport['reached_deadline'] === 0)
{
return 'No object to process';
}
else
{
$iClosedCount = count($aReport['triggered']);
$iNotClosedCount = count($aReport['not_triggered']);
$sReport = $aReport['reached_deadline'] . " objects reached triggering date";
$sReport .= " - ".$iClosedCount." were triggered";
if($iClosedCount > 0)
{
$sReport .= " (".implode(", ", $aReport['triggered']).")";
}
$sReport .= " - ".$iNotClosedCount." were not triggered";
if($iNotClosedCount > 0)
{
$sReport .= " (".implode(", ", $aReport['not_triggered']).")";
}
return $sReport;
}
}
/**
* Interpret current setting for the week days
*
* Note: This comes from itop-backup scheduled task.
*
* @returns array of int (monday = 1)
*/
public function InterpretWeekDays()
{
static $aWEEKDAYTON = array('monday' => 1, 'tuesday' => 2, 'wednesday' => 3, 'thursday' => 4, 'friday' => 5, 'saturday' => 6, 'sunday' => 7);
$aDays = array();
$sWeekDays = MetaModel::GetConfig()->GetModuleSetting(static::MODULE_CODE, static::MODULE_SETTING_WEEKDAYS, static::DEFAULT_MODULE_SETTING_WEEKDAYS);
if ($sWeekDays != '')
{
$aWeekDaysRaw = explode(',', $sWeekDays);
foreach ($aWeekDaysRaw as $sWeekDay)
{
$sWeekDay = strtolower(trim($sWeekDay));
if (array_key_exists($sWeekDay, $aWEEKDAYTON))
{
$aDays[] = $aWEEKDAYTON[$sWeekDay];
}
else
{
throw new Exception(static::MODULE_CODE.": wrong format for setting 'week_days' (found '$sWeekDay')");
}
}
}
if (count($aDays) == 0)
{
throw new Exception(static::MODULE_CODE.": missing setting 'week_days'");
}
$aDays = array_unique($aDays);
sort($aDays);
return $aDays;
}
/**
* Prints a $sMessage in the CRON output.
*
* @param string $sMessage
*/
protected function Trace($sMessage)
{
// In the CRON output
if ($this->bDebug)
{
echo $sMessage."\n";
}
}
}