Browse Source

Trac #57 - Implemented beta version of email notifications (triggers and actions)

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@225 a333f486-631f-4898-b8df-5754b55c2be0
romainq 15 years ago
parent
commit
de63a10a8c

+ 1 - 16
application/template.class.inc.php

@@ -16,7 +16,7 @@ class DisplayTemplate
 	
 	public function Render(web_page $oPage, $aParams = array())
 	{
-		$this->ApplyParams($aParams);
+		$this->m_sTemplate = MetaModel::ApplyParams($this->m_sTemplate, $aParams);
 		$iStart = 0;
 		$iEnd = strlen($this->m_sTemplate);
 		$iCount = 0;
@@ -40,21 +40,6 @@ class DisplayTemplate
 		$oPage->add(substr($this->m_sTemplate, $iAfterTagPos));
 	}
 	
-	/**
-	 * Replaces all the parameters by the values passed in the hash array
-	 */
-	public function ApplyParams($aParams)
-	{
-		$aSearches = array();
-		$aReplacements = array();
-		foreach($aParams as $sSearch => $sReplace)
-		{
-			$aSearches[] = '$'.$sSearch.'$';
-			$aReplacements[] = $sReplace;
-		}
-		$this->m_sTemplate = str_replace($aSearches, $aReplacements, $this->m_sTemplate);
-	}
-	
 	public function GetNextTag(&$iStartPos, &$iEndPos)
 	{
 		$iChunkStartPos = $iStartPos;

+ 1 - 1
business/itop.business.class.inc.php

@@ -200,7 +200,7 @@ class bizContact extends logRealObject
 		MetaModel::Init_Params($aParams);
 		MetaModel::Init_InheritAttributes();
 		MetaModel::Init_AddAttribute(new AttributeExternalField("org_name", array("label"=>"Organization", "description"=>"Company / Department of the contact", "allowed_values"=>null, "extkey_attcode"=> 'org_id', "target_attcode"=>"name")));
-		MetaModel::Init_AddAttribute(new AttributeString("email", array("label"=>"eMail", "description"=>"Email address", "allowed_values"=>null, "sql"=>"email", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
+		MetaModel::Init_AddAttribute(new AttributeEmailAddress("email", array("label"=>"eMail", "description"=>"Email address", "allowed_values"=>null, "sql"=>"email", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
 		MetaModel::Init_AddAttribute(new AttributeString("phone", array("label"=>"Phone", "description"=>"Telephone", "allowed_values"=>null, "sql"=>"telephone", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
 		MetaModel::Init_AddAttribute(new AttributeExternalKey("location_id", array("targetclass"=>"bizLocation", "label"=>"Location", "description"=>"Id of the location where the contact is located", "allowed_values"=>null, "sql"=>"location_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
 		MetaModel::Init_AddAttribute(new AttributeExternalField("location_name", array("label"=>"Location Name", "description"=>"Name of the location where the contact is located", "allowed_values"=>null, "extkey_attcode"=> 'location_id', "target_attcode"=>"name")));

+ 61 - 1
core/attributedef.class.inc.php

@@ -547,7 +547,7 @@ class AttributeString extends AttributeDBField
 	}
 	public function ScalarToSQL($value)
 	{
-		if (!is_string($value))
+		if (!is_string($value) && !is_null($value))
 		{
 			throw new CoreWarning('Expected the attribute value to be a string', array('found_type' => gettype($value), 'value' => $value, 'class' => $this->GetCode(), 'attribute' => $this->GetHostClass()));
 		}
@@ -616,6 +616,66 @@ class AttributeText extends AttributeString
 }
 
 /**
+ * Specialization of a string: email 
+ *
+ * @package     iTopORM
+ * @author      Romain Quetiez <romainquetiez@yahoo.fr>
+ * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
+ * @link        www.itop.com
+ * @since       1.0
+ * @version     $itopversion$
+ */
+class AttributeEmailAddress extends AttributeString
+{
+	public function GetTypeDesc() {return "Email address(es)";}
+}
+
+/**
+ * Specialization of a string: OQL expression 
+ *
+ * @package     iTopORM
+ * @author      Romain Quetiez <romainquetiez@yahoo.fr>
+ * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
+ * @link        www.itop.com
+ * @since       1.0
+ * @version     $itopversion$
+ */
+class AttributeOQL extends AttributeString
+{
+	public function GetTypeDesc() {return "OQL expression";}
+}
+
+/**
+ * Specialization of a string: template 
+ *
+ * @package     iTopORM
+ * @author      Romain Quetiez <romainquetiez@yahoo.fr>
+ * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
+ * @link        www.itop.com
+ * @since       1.0
+ * @version     $itopversion$
+ */
+class AttributeTemplateString extends AttributeString
+{
+	public function GetTypeDesc() {return "Template string";}
+}
+
+/**
+ * Specialization of a text: template 
+ *
+ * @package     iTopORM
+ * @author      Romain Quetiez <romainquetiez@yahoo.fr>
+ * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
+ * @link        www.itop.com
+ * @since       1.0
+ * @version     $itopversion$
+ */
+class AttributeTemplateText extends AttributeText
+{
+	public function GetTypeDesc() {return "Multiline template string";}
+}
+
+/**
  * Map a enum column to an attribute 
  *
  * @package     iTopORM

+ 10 - 0
core/cmdbobject.class.inc.php

@@ -37,9 +37,19 @@ require_once('dbobject.class.php');
 require_once('dbobjectsearch.class.php');
 require_once('dbobjectset.class.php');
 
+// db change tracking data model
 require_once('cmdbchange.class.inc.php');
 require_once('cmdbchangeop.class.inc.php');
 
+// customization data model
+// Romain: temporary moved into application.inc.php (see explanations there)
+//require_once('trigger.class.inc.php');
+//require_once('action.class.inc.php');
+
+// application log
+// Romain: temporary moved into application.inc.php (see explanations there)
+//require_once('event.class.inc.php');
+
 require_once('csvparser.class.inc.php');
 require_once('bulkchange.class.inc.php');
 

+ 8 - 0
core/config.class.inc.php

@@ -289,6 +289,14 @@ class Config
 			fwrite($hFile, "\t'application' => array (\n");
 			fwrite($hFile, "\t\t'../application/menunode.class.inc.php',\n");
 			fwrite($hFile, "\t\t'../application/audit.rule.class.inc.php',\n");
+// Romain - That's dirty, because those 3 classes are in fact part of the core
+//          but I needed those classes to be derived from cmdbAbstractObject
+//          (to be managed via the GUI) and this class in not really known from
+//          the core, PLUS I needed the includes to be there also for the setup
+//          to create the tables.
+			fwrite($hFile, "\t\t'../core/event.class.inc.php',\n");
+			fwrite($hFile, "\t\t'../core/action.class.inc.php',\n");
+			fwrite($hFile, "\t\t'../core/trigger.class.inc.php',\n");
 			fwrite($hFile, "\t\t// to be continued...\n");
 			fwrite($hFile, "\t),\n");
 			fwrite($hFile, "\t'business' => array (\n");

+ 35 - 3
core/dbobject.class.php

@@ -611,7 +611,7 @@ abstract class DBObject
 		if (!empty($this->m_iKey) && ($this->m_iKey >= 0))
 		{
 			// Add it to the list of fields to write
-			$aFieldsToWrite[] = MetaModel::DBGetKey($sTableClass);
+			$aFieldsToWrite[] = '`'.MetaModel::DBGetKey($sTableClass).'`';
 			$aValuesToWrite[] = CMDBSource::Quote($this->m_iKey);
 		}
 
@@ -622,7 +622,7 @@ abstract class DBObject
 			$aAttColumns = $oAttDef->GetSQLValues($this->m_aCurrValues[$sAttCode]);
 			foreach($aAttColumns as $sColumn => $sValue)
 			{
-				$aFieldsToWrite[] = $sColumn; 
+				$aFieldsToWrite[] = "`$sColumn`"; 
 				$aValuesToWrite[] = CMDBSource::Quote($sValue);
 			}
 		}
@@ -803,7 +803,9 @@ abstract class DBObject
 
 		// Change the state before proceeding to the actions, this is necessary because an action might
 		// trigger another stimuli (alternative: push the stimuli into a queue)
-		$this->Set($sStateAttCode, $aTransitionDef['target_state']);
+		$sPreviousState = $this->Get($sStateAttCode);
+		$sNewState = $aTransitionDef['target_state'];
+		$this->Set($sStateAttCode, $sNewState);
 
 		// $aTransitionDef is an
 		//    array('target_state'=>..., 'actions'=>array of handlers procs, 'user_restriction'=>TBD
@@ -824,9 +826,39 @@ abstract class DBObject
 			if (!$bRet) $bSuccess = false;
 		}
 
+		// Change state triggers...
+		$sClass = get_class($this);
+		$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnStateLeave AS t WHERE t.target_class='$sClass' AND t.state='$sPreviousState'"));
+		while ($oTrigger = $oSet->Fetch())
+		{
+			$oTrigger->DoActivate($this->ToArgs('this'));
+		}
+
+		$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnStateEnter AS t WHERE t.target_class='$sClass' AND t.state='$sNewState'"));
+		while ($oTrigger = $oSet->Fetch())
+		{
+			$oTrigger->DoActivate($this->ToArgs('this'));
+		}
+
 		return $bSuccess;
 	}
 
+	// Make standard context arguments
+	public function ToArgs($sArgName)
+	{
+		$aScalarArgs = array();
+		$aScalarArgs[$sArgName] = $this->GetKey();
+		$aScalarArgs[$sArgName.'->id'] = $this->GetKey();
+	
+		$sClass = get_class($this);
+		foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
+		{
+			$aScalarArgs[$sArgName.'->'.$sAttCode] = $this->Get($sAttCode);
+		}
+		return $aScalarArgs;
+	}
+
+
 	// Return an empty set for the parent of all
 	public static function GetRelationQueries($sRelCode)
 	{

+ 16 - 8
core/metamodel.class.php

@@ -1222,14 +1222,7 @@ abstract class MetaModel
 		{
 			if (self::IsValidObject($value))
 			{
-				$aScalarArgs[$sArgName] = $value->GetKey();
-				$aScalarArgs[$sArgName.'->id'] = $value->GetKey();
-			
-				$sClass = get_class($value);
-				foreach(self::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
-				{
-					$aScalarArgs[$sArgName.'->'.$sAttCode] = $value->Get($sAttCode);
-				}
+				$aScalarArgs = array_merge($aScalarArgs, $value->ToArgs($sArgName));
 			}
 			else
 			{
@@ -2849,6 +2842,21 @@ abstract class MetaModel
 		return self::GetLabel($sLinkClass, $sAttCode);
 	}
 
+	/**
+	 * Replaces all the parameters by the values passed in the hash array
+	 */
+	static public function ApplyParams($aInput, $aParams)
+	{
+		$aSearches = array();
+		$aReplacements = array();
+		foreach($aParams as $sSearch => $sReplace)
+		{
+			$aSearches[] = '$'.$sSearch.'$';
+			$aReplacements[] = $sReplace;
+		}
+		return str_replace($aSearches, $aReplacements, $aInput);
+	}
+
 } // class MetaModel
 
 

+ 13 - 0
pages/schema.php

@@ -232,6 +232,16 @@ function DisplayLifecycle($oPage, $sClass)
 
 
 /**
+ * Helper for the trigger
+ */
+function DisplayTriggers($oPage, $sClass)
+{
+	$oSet = new CMDBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnStateChange WHERE target_class = '$sClass'"));
+	cmdbAbstractObject::DisplaySet($oPage, $oSet);
+}
+
+
+/**
  * Display the list of classes from the business model
  */
 function DisplayClassesList($oPage)
@@ -402,6 +412,9 @@ function DisplayClassDetails($oPage, $sClass)
 	$oPage->SetCurrentTab('Lifecycle');
 	DisplayLifecycle($oPage, $sClass);
 
+	$oPage->SetCurrentTab('Triggers');
+	DisplayTriggers($oPage, $sClass);
+
 	$oPage->SetCurrentTab();
 	$oPage->SetCurrentTabContainer();
 }

+ 79 - 0
setup/data/structure/1.menus.xml

@@ -918,4 +918,83 @@ text-align:left;
 <parent_id>0</parent_id>
 <user_id>0</user_id>
 </menuNode>
+<menuNode id="500">
+<name>Customization</name>
+<label>Customization</label>
+<hyperlink>UI.php</hyperlink>
+<icon_path></icon_path>
+<template></template>
+<type>administrator</type>
+<rank>2</rank>
+<parent_id>1</parent_id>
+<user_id>0</user_id>
+</menuNode>
+<menuNode id="501">
+<name>Triggers - entering</name>
+<label>Triggers - On entering state</label>
+<hyperlink>UI.php</hyperlink>
+<icon_path>../images/std_view.gif</icon_path>
+<template>
+&lt;itopblock BlockClass=&quot;DisplayBlock&quot; type=&quot;search&quot; asynchronous=&quot;false&quot; encoding=&quot;text/oql&quot;&gt;SELECT TriggerOnStateEnter&lt;/itopblock&gt;
+&lt;div id=&quot;BottomPane&quot;&gt;
+&lt;p&gt;&lt;/p&gt;
+&lt;itopblock BlockClass=&quot;DisplayBlock&quot; type=&quot;list&quot; asynchronous=&quot;false&quot; encoding=&quot;text/oql&quot;&gt;SELECT TriggerOnStateEnter&lt;/itopblock&gt;
+&lt;/div&gt;
+</template>
+<type>administrator</type>
+<rank>2</rank>
+<parent_id>500</parent_id>
+<user_id>0</user_id>
+</menuNode>
+<menuNode id="502">
+<name>Triggers - leaving</name>
+<label>Triggers - On leaving state</label>
+<hyperlink>UI.php</hyperlink>
+<icon_path>../images/std_view.gif</icon_path>
+<template>
+&lt;itopblock BlockClass=&quot;DisplayBlock&quot; type=&quot;search&quot; asynchronous=&quot;false&quot; encoding=&quot;text/oql&quot;&gt;SELECT TriggerOnStateLeave&lt;/itopblock&gt;
+&lt;div id=&quot;BottomPane&quot;&gt;
+&lt;p&gt;&lt;/p&gt;
+&lt;itopblock BlockClass=&quot;DisplayBlock&quot; type=&quot;list&quot; asynchronous=&quot;false&quot; encoding=&quot;text/oql&quot;&gt;SELECT TriggerOnStateLeave&lt;/itopblock&gt;
+&lt;/div&gt;
+</template>
+<type>administrator</type>
+<rank>2</rank>
+<parent_id>500</parent_id>
+<user_id>0</user_id>
+</menuNode>
+<menuNode id="505">
+<name>Actions</name>
+<label>Actions - Send an email</label>
+<hyperlink>UI.php</hyperlink>
+<icon_path>../images/std_view.gif</icon_path>
+<template>
+&lt;itopblock BlockClass=&quot;DisplayBlock&quot; type=&quot;search&quot; asynchronous=&quot;false&quot; encoding=&quot;text/oql&quot;&gt;SELECT ActionEmail&lt;/itopblock&gt;
+&lt;div id=&quot;BottomPane&quot;&gt;
+&lt;p&gt;&lt;/p&gt;
+&lt;itopblock BlockClass=&quot;DisplayBlock&quot; type=&quot;list&quot; asynchronous=&quot;false&quot; encoding=&quot;text/oql&quot;&gt;SELECT ActionEmail&lt;/itopblock&gt;
+&lt;/div&gt;
+</template>
+<type>administrator</type>
+<rank>2</rank>
+<parent_id>500</parent_id>
+<user_id>0</user_id>
+</menuNode>
+<menuNode id="510">
+<name>Application log</name>
+<label>Application log</label>
+<hyperlink>UI.php</hyperlink>
+<icon_path>../images/std_view.gif</icon_path>
+<template>
+&lt;itopblock BlockClass=&quot;DisplayBlock&quot; type=&quot;search&quot; asynchronous=&quot;false&quot; encoding=&quot;text/oql&quot;&gt;SELECT Event&lt;/itopblock&gt;
+&lt;div id=&quot;BottomPane&quot;&gt;
+&lt;p&gt;&lt;/p&gt;
+&lt;itopblock BlockClass=&quot;DisplayBlock&quot; type=&quot;list&quot; asynchronous=&quot;false&quot; encoding=&quot;text/oql&quot;&gt;SELECT Event&lt;/itopblock&gt;
+&lt;/div&gt;
+</template>
+<type>administrator</type>
+<rank>2</rank>
+<parent_id>1</parent_id>
+<user_id>0</user_id>
+</menuNode>
 </Set>