First version of Notify on expiration

SVN:trunk[6919]
This commit is contained in:
Vincent Dumas
2018-07-19 14:52:09 +00:00
commit 101b485657
7 changed files with 1056 additions and 0 deletions

View File

@@ -0,0 +1,403 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.3">
<classes>
<class id="ExpirationRule" _delta="define">
<parent>cmdbAbstractObject</parent>
<properties>
<category>bizmodel,searchable</category>
<abstract>false</abstract>
<db_table>expirationrule</db_table>
<naming>
<attributes>
<attribute id="name"/>
</attributes>
</naming>
<reconciliation>
<attributes>
<attribute id="name"/>
<attribute id="class"/>
</attributes>
</reconciliation>
</properties>
<fields>
<field id="name" xsi:type="AttributeString">
<sql>name</sql>
<default_value/>
<is_null_allowed>false</is_null_allowed>
</field>
<field id="description" xsi:type="AttributeText">
<sql>description</sql>
<default_value/>
<is_null_allowed>true</is_null_allowed>
<format>html</format>
</field>
<field id="class" xsi:type="AttributeString">
<sql>class</sql>
<default_value/>
<is_null_allowed>false</is_null_allowed>
</field>
<field id="status" xsi:type="AttributeEnum">
<values>
<value id="active">active</value>
<value id="inactive">inactive</value>
</values>
<sql>status</sql>
<default_value>active</default_value>
<is_null_allowed>false</is_null_allowed>
<display_style>list</display_style>
</field>
<field id="type" xsi:type="AttributeEnum">
<values>
<value id="simple">simple</value>
<value id="advanced">advanced</value>
</values>
<dependencies>
<attribute id="date_to_check_att"/>
<attribute id="term_of_notice"/>
<attribute id="oql_scope"/>
</dependencies>
<sql>type</sql>
<default_value>simple</default_value>
<is_null_allowed>false</is_null_allowed>
</field>
<field id="date_to_check_att" xsi:type="AttributeString">
<sql>date_to_check_att</sql>
<default_value/>
<is_null_allowed>true</is_null_allowed>
</field>
<field id="term_of_notice" xsi:type="AttributeInteger">
<sql>term_of_notice</sql>
<default_value/>
<is_null_allowed>true</is_null_allowed>
</field>
<field id="oql_scope" xsi:type="AttributeOQL">
<sql>oql_scope</sql>
<default_value/>
<is_null_allowed>true</is_null_allowed>
</field>
</fields>
<methods>
<method id="GetInitialStateAttributeFlags">
<static>false</static>
<access>public</access>
<type>Overload-DBObject</type>
<code><![CDATA[ public function GetInitialStateAttributeFlags($sAttCode, &$aReasons = array())
{
if ($sAttCode == 'type')
{
return OPT_ATT_READONLY;
}
return parent::GetInitialStateAttributeFlags($sAttCode, $aReasons);
}
]]></code>
</method>
<method id="GetAttributeFlags">
<static>false</static>
<access>public</access>
<type>Overload-DBObject</type>
<code><![CDATA[ public function GetAttributeFlags($sAttCode, &$aReasons = array(), $sTargetState = '')
{
if ($sAttCode == 'type')
{
return OPT_ATT_READONLY;
}
return parent::GetAttributeFlags($sAttCode, $aReasons, $sTargetState);
}
]]></code>
</method>
<method id="ComputeValues">
<static>false</static>
<access>public</access>
<type>Overload-DBObject</type>
<code><![CDATA[ public function ComputeValues()
{
// Compute the type of the rule
$oAttDef = MetaModel::GetAttributeDef(get_class($this), 'oql_scope');
$sType = ($oAttDef->IsNull($this->Get('oql_scope'))) ? 'simple' : 'advanced';
$this->Set('type', $sType);
return parent::ComputeValues();
}]]></code>
</method>
<method id="DoCheckToWrite">
<static>false</static>
<access>public </access>
<code><![CDATA[ public function DoCheckToWrite()
{
parent::DoCheckToWrite();
// Checking class / attributes consistency with Datamodel
$sClass = $this->Get('class');
$sDateToCheckAttCode = $this->Get('date_to_check_att');
$sOqlScope = $this->Get('oql_scope');
// - Class
if(!MetaModel::IsValidClass($sClass))
{
$this->m_aCheckIssues[] = Dict::Format('Class:ExpirationRule/Error:ClassNotValid', $sClass);
}
// - Date to check attribute
if(!empty($sDateToCheckAttCode))
{
if(!MetaModel::IsValidAttCode($sClass, $sDateToCheckAttCode))
{
$this->m_aCheckIssues[] = Dict::Format('Class:ExpirationRule/Error:AttributeNotValid', $sClass, $sDateToCheckAttCode);
}
else
{
$oAttDef = MetaModel::GetAttributeDef($sClass, $sDateToCheckAttCode);
if(($oAttDef instanceof AttributeDateTime) === false)
{
$this->m_aCheckIssues[] = Dict::Format('Class:ExpirationRule/Error:AttributeMustBeDate', $sClass, $sDateToCheckAttCode);
}
}
}
// Checking option consistency
// - At least one option must be filled (either simple or advanced)
$bNoOptionFilled = true;
$aOptionAttCodes = array('date_to_check_att', 'term_of_notice', 'oql_scope');
foreach($aOptionAttCodes as $sOptionAttCode)
{
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sOptionAttCode);
$value = $this->Get($sOptionAttCode);
if(!$oAttDef->IsNull($value))
{
$bNoOptionFilled = false;
break;
}
}
if($bNoOptionFilled)
{
$this->m_aCheckIssues[] = Dict::Format('Class:ExpirationRule/Error:NoOptionFilled');
}
// - Checking that option 1 is valid if selected
elseif(empty($sOqlScope))
{
$bOptionOneValid = true;
// Removing OQL attribute as we only test option 1 attributes
array_pop($aOptionAttCodes);
foreach($aOptionAttCodes as $sOptionAttCode)
{
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sOptionAttCode);
$value = $this->Get($sOptionAttCode);
if($oAttDef->IsNull($value))
{
$bOptionOneValid = false;
break;
}
}
if(!$bOptionOneValid)
{
$this->m_aCheckIssues[] = Dict::Format('Class:ExpirationRule/Error:OptionOneMissingField');
}
}
// - Checking that option 2 OQL is about the right class
elseif(!empty($sOqlScope))
{
try
{
$oSearch = DBObjectSearch::FromOQL($sOqlScope);
$sOqlClass = $oSearch->GetClass();
if($sOqlClass !== $sClass)
{
$this->m_aCheckIssues[] = Dict::Format('Class:StateRule/Error:OQLClassDontMatch', $sClass, $sOqlClass);
}
}
catch(Exception $e)
{
$this->m_aCheckIssues[] = Dict::Format('Class:StateRule/Error:OQLNotValid', $e->getMessage());
}
}
}]]></code>
</method>
<method id="GetFilter">
<static>false</static>
<access>public</access>
<type>Overload-DBObject</type>
<code><![CDATA[ /**
* @return DBObjectSearch
*/
public function GetFilter()
{
if($this->Get('type') === 'advanced')
{
$oSearch = DBObjectSearch::FromOQL($this->Get('oql_scope'));
}
else
{
$sClass = $this->Get('class');
$sDateAttCode = $this->Get('date_to_check_att');
$oSearch = DBObjectSearch::FromOQL('SELECT '.$sClass.' WHERE '.$sDateAttCode.' = DATE_ADD(CURRENT_DATE(), INTERVAL :term_of_notice DAY)');
$oSearch->SetInternalParams(
array(
'term_of_notice' => $this->Get('term_of_notice'),
)
);
}
return $oSearch;
}]]></code>
</method>
<method id="DisplayBareRelations">
<static>false</static>
<access>public</access>
<type>Overload-DBObject</type>
<code><![CDATA[ /**
* @param \WebPage $oPage
* @param bool $bEditMode
*/
public function DisplayBareRelations(WebPage $oPage, $bEditMode = false)
{
parent::DisplayBareRelations($oPage, $bEditMode);
if(!$bEditMode)
{
$oPage->SetCurrentTab(Dict::S('UI:ExpiredObject:Preview'));
$this->OnShowPreview($oPage);
}
}]]></code>
</method>
<method id="OnShowPreview">
<static>false</static>
<access>protected</access>
<type>Overload-DBObject</type>
<code><![CDATA[/**
* @param \WebPage $oPage
*/
public function OnShowPreview(WebPage $oPage)
{
$sClass = $this->Get('class');
$oPage->p(MetaModel::GetClassIcon($sClass)."&nbsp;".Dict::Format('UI:ExpiredObject:Title', MetaModel::GetName($sClass)));
$aParams = array(
'menu' => true,
'table_id' => 'ExpiredObject_preview_rule_'.$this->GetKey(),
);
$oBlock = new DisplayBlock($this->GetFilter(), 'list', true, $aParams);
$oBlock->Display($oPage, 'rel_preview_rule_'.$this->GetKey(), $aParams);
}]]></code>
</method>
</methods>
<presentation>
<list>
<items>
<item id="class">
<rank>10</rank>
</item>
<item id="name">
<rank>20</rank>
</item>
<item id="description">
<rank>30</rank>
</item>
<item id="status">
<rank>40</rank>
</item>
<item id="type">
<rank>50</rank>
</item>
</items>
</list>
<search>
<items>
<item id="name">
<rank>10</rank>
</item>
<item id="class">
<rank>20</rank>
</item>
<item id="status">
<rank>30</rank>
</item>
<item id="type">
<rank>40</rank>
</item>
<item id="description">
<rank>70</rank>
</item>
<item id="date_to_check_att">
<rank>80</rank>
</item>
<item id="term_of_notice">
<rank>90</rank>
</item>
<item id="oql_scope">
<rank>100</rank>
</item>
</items>
</search>
<details>
<items>
<item id="col:col0">
<rank>10</rank>
<items>
<item id="fieldset:ExpirationRule:general">
<rank>10</rank>
<items>
<item id="name">
<rank>10</rank>
</item>
<item id="class">
<rank>20</rank>
</item>
<item id="description">
<rank>30</rank>
</item>
<item id="status">
<rank>40</rank>
</item>
<item id="type">
<rank>50</rank>
</item>
</items>
</item>
</items>
</item>
<item id="col:col1">
<rank>20</rank>
<items>
<item id="fieldset:ExpirationRule:simple">
<rank>10</rank>
<items>
<item id="date_to_check_att">
<rank>10</rank>
</item>
<item id="term_of_notice">
<rank>20</rank>
</item>
</items>
</item>
</items>
</item>
<item id="col:col2">
<rank>30</rank>
<items>
<item id="fieldset:ExpirationRule:advanced">
<rank>10</rank>
<items>
<item id="oql_scope">
<rank>10</rank>
</item>
</items>
</item>
</items>
</item>
</items>
</details>
</presentation>
</class>
</classes>
<menus>
<menu xsi:type="OQLMenuNode" id="ExpirationRule" _delta="define">
<parent>ServiceManagement</parent>
<rank>17</rank>
<oql>SELECT ExpirationRule</oql>
<do_search>1</do_search>
</menu>
</menus>
</itop_design>

View File

@@ -0,0 +1,78 @@
<?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
/**
* Localized data
*
* @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
*/
// ExpirationRule
Dict::Add('DE DE', 'German', 'Deutsch', array(
// Class
'Class:ExpirationRule/Name' => '%1$s~~',
'Class:ExpirationRule' => 'Expiration rule~~',
'Class:ExpirationRule+' => '~~',
'Class:ExpirationRule/Attribute:name' => 'Name~~',
'Class:ExpirationRule/Attribute:name+' => '~~',
'Class:ExpirationRule/Attribute:class' => 'Class~~',
'Class:ExpirationRule/Attribute:class+' => '~~',
'Class:ExpirationRule/Attribute:description' => 'Description',
'Class:ExpirationRule/Attribute:description+' => '',
'Class:ExpirationRule/Attribute:status' => 'Status~~',
'Class:ExpirationRule/Attribute:status+' => '~~',
'Class:ExpirationRule/Attribute:status/Value:active' => 'Active~~',
'Class:ExpirationRule/Attribute:status/Value:inactive' => 'Inactive~~',
'Class:ExpirationRule/Attribute:type' => 'Applied option~~',
'Class:ExpirationRule/Attribute:type+' => 'Which option will be used regarding the filled fields. If both are filled, advanced option is applied~~',
'Class:ExpirationRule/Attribute:type/Value:simple' => 'Simple~~',
'Class:ExpirationRule/Attribute:type/Value:advanced' => 'Advanced~~',
'Class:ExpirationRule/Attribute:date_to_check_att' => 'Date to check~~',
'Class:ExpirationRule/Attribute:date_to_check_att+' => 'Attribute code of the date to check~~',
'Class:ExpirationRule/Attribute:term_of_notice' => 'Term of notice~~',
'Class:ExpirationRule/Attribute:term_of_notice+' => 'Number of days before the date to check, to trigger the notification~~',
'Class:ExpirationRule/Attribute:oql_scope' => 'OQL scope~~',
'Class:ExpirationRule/Attribute:oql_scope+' => 'OQL query to define which objects are concerned by this rule.~~',
// Integrity errors
'Class:ExpirationRule/Error:ClassNotValid' => 'Class must be a valid class from datamodel, "%1$s" given~~',
'Class:ExpirationRule/Error:AttributeNotValid' => '"%2$s" is not a valid attribute for class "%1$s"~~',
'Class:ExpirationRule/Error:AttributeMustBeDate' => '"%2$s" must be a date attribute of class "%1$s"~~',
'Class:ExpirationRule/Error:NoOptionFilled' => 'Either option 1 or option 2 must be filled~~',
'Class:ExpirationRule/Error:OptionOneMissingField' => 'All fields of option 1 must be filled~~',
// Presentation
'ExpirationRule:general' => 'General informations~~',
'ExpirationRule:simple' => 'Fill either option 1 (simple) ...~~',
'ExpirationRule:advanced' => '... or option 2 (advanced)~~',
// Menus
'Menu:ExpirationRule' => 'Expiration rules~~',
'Menu:ExpirationRule+' => 'Expiration rules~~',
// Tabs
'UI:AutocloseTicket:Preview' => 'Preview~~',
'UI:AutocloseTicket:Title' => '%1$s reach their term of notice today~~',
'Class:TriggerOnExpirationRule' => 'Trigger (on expiration)~~',
'Class:TriggerOnExpirationRule+' => 'Trigger activated when an object is in scope of an expiration rule~~',
));

View File

@@ -0,0 +1,78 @@
<?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
/**
* Localized data
*
* @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
*/
// ExpirationRule
Dict::Add('EN US', 'English', 'English', array(
// Class
'Class:ExpirationRule/Name' => '%1$s',
'Class:ExpirationRule' => 'Expiration rule',
'Class:ExpirationRule+' => '',
'Class:ExpirationRule/Attribute:name' => 'Name',
'Class:ExpirationRule/Attribute:name+' => '',
'Class:ExpirationRule/Attribute:class' => 'Class',
'Class:ExpirationRule/Attribute:class+' => '',
'Class:ExpirationRule/Attribute:description' => 'Description',
'Class:ExpirationRule/Attribute:description+' => '',
'Class:ExpirationRule/Attribute:status' => 'Status',
'Class:ExpirationRule/Attribute:status+' => '',
'Class:ExpirationRule/Attribute:status/Value:active' => 'Active',
'Class:ExpirationRule/Attribute:status/Value:inactive' => 'Inactive',
'Class:ExpirationRule/Attribute:type' => 'Applied option',
'Class:ExpirationRule/Attribute:type+' => 'Which option will be used regarding the filled fields. If both are filled, advanced option is applied',
'Class:ExpirationRule/Attribute:type/Value:simple' => 'Simple',
'Class:ExpirationRule/Attribute:type/Value:advanced' => 'Advanced',
'Class:ExpirationRule/Attribute:date_to_check_att' => 'Date to check',
'Class:ExpirationRule/Attribute:date_to_check_att+' => 'Attribute code of the date to check',
'Class:ExpirationRule/Attribute:term_of_notice' => 'Term of notice',
'Class:ExpirationRule/Attribute:term_of_notice+' => 'Number of days before the date to check, to trigger the notification',
'Class:ExpirationRule/Attribute:oql_scope' => 'OQL scope',
'Class:ExpirationRule/Attribute:oql_scope+' => 'OQL query to define which objects are concerned by this rule (stimulus to apply). Note that the OQL will automatically be restricted to the states in which the stimulus is available.',
// Integrity errors
'Class:ExpirationRule/Error:ClassNotValid' => 'Class must be a valid class from datamodel, "%1$s" given',
'Class:ExpirationRule/Error:AttributeNotValid' => '"%2$s" is not a valid attribute for class "%1$s"',
'Class:ExpirationRule/Error:AttributeMustBeDate' => '"%2$s" must be a date attribute of class "%1$s"',
'Class:ExpirationRule/Error:NoOptionFilled' => 'Either option 1 or option 2 must be filled',
'Class:ExpirationRule/Error:OptionOneMissingField' => 'All fields of option 1 must be filled',
// Presentation
'ExpirationRule:general' => 'General informations',
'ExpirationRule:simple' => 'Fill either option 1 (simple) ...',
'ExpirationRule:advanced' => '... or option 2 (advanced)',
// Menus
'Menu:ExpirationRule' => 'Expiration rules',
'Menu:ExpirationRule+' => 'Expiration rules',
// Tabs
'UI:ExpiredObject:Preview' => 'Preview',
'UI:ExpiredObject:Title' => '%1$s will enter their term of notice today',
'Class:TriggerOnExpirationRule' => 'Trigger (on expiration)',
'Class:TriggerOnExpirationRule+' => 'Trigger activated when an object is in scope of an expiration rule',
));

View File

@@ -0,0 +1,78 @@
<?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
/**
* Localized data
*
* @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
*/
// ClosingRule
Dict::Add('FR FR', 'French', 'Français', array(
// Class
'Class:ExpirationRule/Name' => '%1$s',
'Class:ExpirationRule' => 'Régle de Préavis',
'Class:ExpirationRule+' => '',
'Class:ExpirationRule/Attribute:name' => 'Nom',
'Class:ExpirationRule/Attribute:name+' => '',
'Class:ExpirationRule/Attribute:class' => 'Classe',
'Class:ExpirationRule/Attribute:class+' => '',
'Class:ExpirationRule/Attribute:description' => 'Description',
'Class:ExpirationRule/Attribute:description+' => '',
'Class:ExpirationRule/Attribute:status' => 'Statut',
'Class:ExpirationRule/Attribute:status+' => '',
'Class:ExpirationRule/Attribute:status/Value:active' => 'Active',
'Class:ExpirationRule/Attribute:status/Value:inactive' => 'Inactive',
'Class:ExpirationRule/Attribute:type' => 'Option retenue',
'Class:ExpirationRule/Attribute:type+' => 'Quelle option sera utilisée au regard des champs remplis. Si les 2 options sont remplies, l\'option avancée sera appliquée',
'Class:ExpirationRule/Attribute:type/Value:simple' => 'Simple',
'Class:ExpirationRule/Attribute:type/Value:advanced' => 'Avancée',
'Class:ExpirationRule/Attribute:date_to_check_att' => 'Date à utiliser',
'Class:ExpirationRule/Attribute:date_to_check_att+' => 'Code du champ date à controler',
'Class:ExpirationRule/Attribute:term_of_notice' => 'Préavis en jours',
'Class:ExpirationRule/Attribute:term_of_notice+' => 'Nombre de jours avant la date pour déclencher la notification',
'Class:ExpirationRule/Attribute:oql_scope' => 'Périmêtre en OQL',
'Class:ExpirationRule/Attribute:oql_scope+' => 'Requête OQL définissant les objets concernés par cette règle (trigger à déclencher).',
// Integrity errors
'Class:ExpirationRule/Error:ClassNotValid' => 'La classe doit faire partie du modèle de données, "%1$s" donnée',
'Class:ExpirationRule/Error:AttributeNotValid' => '"%2$s" n\'est pas un attribut valide pour la classe "%1$s"',
'Class:ExpirationRule/Error:AttributeMustBeDate' => '"%2$s" doit être un attribut de type date pour la classe "%1$s"',
'Class:ExpirationRule/Error:NoOptionFilled' => 'Une des 2 options doit être remplie',
'Class:ExpirationRule/Error:OptionOneMissingField' => 'Tous les champs de l\'option 1 doivent être remplis',
// Presentation
'ExpirationRule:general' => 'Informations générales',
'ExpirationRule:simple' => 'Remplir l\'option (simple) ...',
'ExpirationRule:advanced' => '... oo l\'option 2 (avancée)',
// Menus
'Menu:ExpirationRule' => 'Régles de préavis',
'Menu:ExpirationRule+' => 'Régles de préavis',
// Tabs
'UI:ExpiredObject:Preview' => 'Aperçu',
'UI:ExpiredObject:Title' => '%1$s ont leur préavis qui démarre aujourd\'hui',
'Class:TriggerOnExpirationRule' => 'Déclencheur sur préavis atteint',
'Class:TriggerOnExpirationRule+' => 'Déclencheur activé lorsqu\'une régle de préavis est applicable à l\'objet',
));

View File

@@ -0,0 +1,281 @@
<?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";
}
}
}

View File

@@ -0,0 +1,90 @@
<?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
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'combodo-notify-on-expiration/0.1.0',
array(
// Identification
//
'label' => 'Notify on expiration',
'category' => 'business',
// Setup
//
'dependencies' => array(
),
'mandatory' => false,
'visible' => true,
'installer' => 'NotifyOnExpirationInstaller',
// Components
//
'datamodel' => array(
'model.combodo-notify-on-expiration.php',
'main.combodo-notify-on-expiration.php',
'triggerexpirationrule.class.inc.php',
),
'webservice' => array(
),
'data.struct' => array(
// add your 'structure' definition XML files here,
),
'data.sample' => array(
// add your sample data XML files here,
),
// Documentation
//
'doc.manual_setup' => '', // hyperlink to manual setup documentation, if any
'doc.more_information' => '', // hyperlink to more information, if any
// Default settings
//
'settings' => array(
// Module specific settings go here, if any
'time' => '03:00',
'enabled' => true,
'debug' => false
),
)
);
if (!class_exists('NotifyOnExpirationInstaller'))
{
// Module installation handler
//
class NotifyOnExpirationInstaller extends ModuleInstallerAPI
{
public static function BeforeWritingConfig(Config $oConfiguration)
{
return $oConfiguration;
}
/**
* Handler called after the creation/update of the database schema
* @param $oConfiguration Config The new configuration of the application
* @param $sPreviousVersion string PRevious version number of the module (empty string in case of first install)
* @param $sCurrentVersion string Current version number of the module
*/
public static function AfterDatabaseCreation(Config $oConfiguration, $sPreviousVersion, $sCurrentVersion)
{
}
}
}

View File

@@ -0,0 +1,48 @@
<?php
// Copyright (C) 2012-2016 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser 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
/**
* @copyright Copyright (C) 2012-2016 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
/**
* To trigger notifications when an object is in scope of an Expiration Rule
*/
class TriggerOnExpirationRule extends TriggerOnObject
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb,application", // "application" category => admins can perform a CSV import
"key_type" => "autoincrement",
"name_attcode" => "description",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_trigger_expirationrule",
"db_key_field" => "id",
"db_finalclass_field" => "",
"display_template" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
// Display lists
MetaModel::Init_SetZListItems('details', array('description', 'target_class', 'filter', 'action_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class', 'description')); // Attributes to be displayed for a list
// Search criteria
}
}