Parcourir la source

Trac#56 Improvements to the SOAP web services and to the first operation CreateIncidentTicket

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@230 a333f486-631f-4898-b8df-5754b55c2be0
romainq il y a 15 ans
Parent
commit
b7d4bdeceb

+ 8 - 7
core/action.class.inc.php

@@ -72,7 +72,7 @@ abstract class ActionNotification extends Action
 			"description" => "Notification (abstract)",
 			"key_type" => "autoincrement",
 			"key_label" => "",
-			"name_attcode" => "",
+			"name_attcode" => "name",
 			"state_attcode" => "",
 			"reconc_keys" => array(),
 			"db_table" => "priv_action_notification",
@@ -116,7 +116,7 @@ class ActionEmail extends ActionNotification
 			"description" => "Action: Email notification",
 			"key_type" => "autoincrement",
 			"key_label" => "",
-			"name_attcode" => "",
+			"name_attcode" => "name",
 			"state_attcode" => "",
 			"reconc_keys" => array(),
 			"db_table" => "priv_action_email",
@@ -215,18 +215,19 @@ class ActionEmail extends ActionNotification
 			$sHeaders .= "Bcc: $sBCC\r\n";
 		}
 
-		$sOverview = "TO:$sTo, FROM:$sFrom, REPLY-TO:$sReplyTo, CC:$sCC, BCC:$sBCC, SUBJECT:$sSubject, BODY:$sBody";
-
 		// Mail it
 		if (mail($sTo, $sSubject, $sBody, $sHeaders))
 		{
-			$oLog = new EventNotification();
-			$oLog->Set('message', 'Email sent successfully');
+			$oLog = new EventNotificationEmail();
 			$oLog->Set('userinfo', UserRights::GetUser());
 			$oLog->Set('trigger_id', $oTrigger->GetKey());
 			$oLog->Set('action_id', $this->GetKey());
 			$oLog->Set('object_id', $aContextArgs['this->id']);
-			$oLog->Set('overview', $sOverview);
+			$oLog->Set('to', $sTo);
+			$oLog->Set('cc', $sCC);
+			$oLog->Set('bcc', $sBCC);
+			$oLog->Set('subject', $sSubject);
+			$oLog->Set('body', $sBody);
 			$oLog->DBInsertNoReload();
 		}
 		else

+ 41 - 0
core/attributedef.class.inc.php

@@ -476,6 +476,47 @@ class AttributeInteger extends AttributeDBField
 }
 
 /**
+ * Map a boolean column to an attribute 
+ *
+ * @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 AttributeBoolean extends AttributeInteger
+{
+	static protected function ListExpectedParams()
+	{
+		return parent::ListExpectedParams();
+		//return array_merge(parent::ListExpectedParams(), array());
+	}
+
+	public function GetType() {return "Boolean";}
+	public function GetTypeDesc() {return "Boolean";}
+	public function GetEditClass() {return "Integer";}
+	protected function GetSQLCol() {return "TINYINT(1)";}
+	
+	public function MakeRealValue($proposedValue)
+	{
+		if ((int)$proposedValue) return true;
+		return false;
+	}
+	public function ScalarToSQL($value)
+	{
+		assert(is_bool($value));
+		if ($value) return 1;
+		return 0;
+	}
+	public function SQLToScalar($value)
+	{
+		// Use cast (int) or intval() ?
+		return (int)$value;
+	}
+}
+
+/**
  * Map a varchar column (size < ?) to an attribute 
  *
  * @package     iTopORM

+ 126 - 9
core/event.class.inc.php

@@ -32,18 +32,15 @@ class Event extends cmdbAbstractObject
 		);
 		MetaModel::Init_Params($aParams);
 		//MetaModel::Init_InheritAttributes();
-		MetaModel::Init_AddAttribute(new AttributeString("message", array("label"=>"Message", "description"=>"one line description", "allowed_values"=>null, "sql"=>"message", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
-
 		MetaModel::Init_AddAttribute(new AttributeDate("date", array("label"=>"date", "description"=>"date and time at which the changes have been recorded", "allowed_values"=>null, "sql"=>"date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
-		MetaModel::Init_AddAttribute(new AttributeString("userinfo", array("label"=>"misc. info", "description"=>"caller's defined information", "allowed_values"=>null, "sql"=>"userinfo", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
+		MetaModel::Init_AddAttribute(new AttributeString("userinfo", array("label"=>"user info", "description"=>"identification of the user that was doing the action that triggered this event", "allowed_values"=>null, "sql"=>"userinfo", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
 
 		//MetaModel::Init_InheritFilters();
-		MetaModel::Init_AddFilterFromAttribute("message");
 		MetaModel::Init_AddFilterFromAttribute("date");
 
 		// Display lists
-		MetaModel::Init_SetZListItems('details', array('finalclass', 'date', 'message')); // Attributes to be displayed for the complete details
-		MetaModel::Init_SetZListItems('list', array('finalclass', 'date', 'message')); // Attributes to be displayed for a list
+		MetaModel::Init_SetZListItems('details', array('finalclass', 'date', 'userinfo')); // Attributes to be displayed for the complete details
+		MetaModel::Init_SetZListItems('list', array('finalclass', 'date')); // Attributes to be displayed for a list
 		// Search criteria
 //		MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
 //		MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
@@ -74,15 +71,53 @@ class EventNotification extends Event
 		MetaModel::Init_AddAttribute(new AttributeExternalKey("trigger_id", array("targetclass"=>"Trigger", "jointype"=> "", "label"=>"Trigger", "description"=>"user account", "allowed_values"=>null, "sql"=>"trigger_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
 		MetaModel::Init_AddAttribute(new AttributeExternalKey("action_id", array("targetclass"=>"Action", "jointype"=> "", "label"=>"user", "description"=>"user account", "allowed_values"=>null, "sql"=>"action_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
 		MetaModel::Init_AddAttribute(new AttributeInteger("object_id", array("label"=>"Object id", "description"=>"object id (class defined by the trigger ?)", "allowed_values"=>null, "sql"=>"object_id", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
-		MetaModel::Init_AddAttribute(new AttributeText("overview", array("label"=>"Overview", "description"=>"Complete view of the resulting email", "allowed_values"=>null, "sql"=>"overview", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
 
 		MetaModel::Init_InheritFilters();
 		MetaModel::Init_AddFilterFromAttribute("trigger_id");
 		MetaModel::Init_AddFilterFromAttribute("action_id");
 
 		// Display lists
-		MetaModel::Init_SetZListItems('details', array('date', 'message', 'userinfo', 'trigger_id', 'action_id', 'object_id', 'overview')); // Attributes to be displayed for the complete details
-		MetaModel::Init_SetZListItems('list', array('date', 'message')); // Attributes to be displayed for a list
+		MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'trigger_id', 'action_id', 'object_id')); // Attributes to be displayed for the complete details
+		MetaModel::Init_SetZListItems('list', array('date', 'userinfo')); // Attributes to be displayed for a list
+		// Search criteria
+//		MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
+//		MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
+	}
+
+}
+
+class EventNotificationEmail extends EventNotification
+{
+	public static function Init()
+	{
+		$aParams = array
+		(
+			"category" => "core/cmdb",
+			"name" => "Email emission event",
+			"description" => "Trace of an email that has been sent",
+			"key_type" => "autoincrement",
+			"key_label" => "",
+			"name_attcode" => "",
+			"state_attcode" => "",
+			"reconc_keys" => array(),
+			"db_table" => "priv_event_email",
+			"db_key_field" => "id",
+			"db_finalclass_field" => "",
+			"display_template" => "",
+		);
+		MetaModel::Init_Params($aParams);
+		MetaModel::Init_InheritAttributes();
+		MetaModel::Init_AddAttribute(new AttributeText("to", array("label"=>"TO", "description"=>"TO", "allowed_values"=>null, "sql"=>"to", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
+		MetaModel::Init_AddAttribute(new AttributeText("cc", array("label"=>"CC", "description"=>"CC", "allowed_values"=>null, "sql"=>"cc", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
+		MetaModel::Init_AddAttribute(new AttributeText("bcc", array("label"=>"BCC", "description"=>"BCC", "allowed_values"=>null, "sql"=>"bcc", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
+		MetaModel::Init_AddAttribute(new AttributeText("subject", array("label"=>"Subject", "description"=>"Subject", "allowed_values"=>null, "sql"=>"subject", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
+		MetaModel::Init_AddAttribute(new AttributeText("body", array("label"=>"Body", "description"=>"Body", "allowed_values"=>null, "sql"=>"body", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
+
+		MetaModel::Init_InheritFilters();
+
+		// Display lists
+		MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'trigger_id', 'action_id', 'object_id', 'to', 'cc', 'bcc', 'subject', 'body')); // Attributes to be displayed for the complete details
+		MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'subject')); // Attributes to be displayed for a list
 		// Search criteria
 //		MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
 //		MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
@@ -90,5 +125,87 @@ class EventNotification extends Event
 
 }
 
+class EventIssue extends Event
+{
+	public static function Init()
+	{
+		$aParams = array
+		(
+			"category" => "core/cmdb",
+			"name" => "Issue event",
+			"description" => "Trace of an issue (warning, error, etc.)",
+			"key_type" => "autoincrement",
+			"key_label" => "",
+			"name_attcode" => "",
+			"state_attcode" => "",
+			"reconc_keys" => array(),
+			"db_table" => "priv_event_issue",
+			"db_key_field" => "id",
+			"db_finalclass_field" => "",
+			"display_template" => "",
+		);
+		MetaModel::Init_Params($aParams);
+		MetaModel::Init_InheritAttributes();
+		MetaModel::Init_AddAttribute(new AttributeString("issue", array("label"=>"Issue", "description"=>"What happened", "allowed_values"=>null, "sql"=>"issue", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
+		MetaModel::Init_AddAttribute(new AttributeString("impact", array("label"=>"Impact", "description"=>"What are the consequences", "allowed_values"=>null, "sql"=>"impact", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
+		MetaModel::Init_AddAttribute(new AttributeString("page", array("label"=>"Page", "description"=>"HTTP entry point", "allowed_values"=>null, "sql"=>"page", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
+		MetaModel::Init_AddAttribute(new AttributeText("arguments_post", array("label"=>"Posted arguments", "description"=>"HTTP POST arguments", "allowed_values"=>null, "sql"=>"arguments_post", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
+		MetaModel::Init_AddAttribute(new AttributeText("arguments_get", array("label"=>"URL arguments", "description"=>"HTTP GET arguments", "allowed_values"=>null, "sql"=>"arguments_get", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
+		MetaModel::Init_AddAttribute(new AttributeText("callstack", array("label"=>"Callstack", "description"=>"Call stack", "allowed_values"=>null, "sql"=>"callstack", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
+		MetaModel::Init_AddAttribute(new AttributeBlob("data", array("label"=>"Data", "description"=>"More information", "allowed_values"=>null, "sql"=>"data", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
+
+		MetaModel::Init_InheritFilters();
+		MetaModel::Init_AddFilterFromAttribute("issue");
+		MetaModel::Init_AddFilterFromAttribute("impact");
+
+		// Display lists
+		MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'issue', 'impact', 'page', 'arguments_post', 'arguments_get', 'callstack', 'data')); // Attributes to be displayed for the complete details
+		MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'issue', 'impact')); // Attributes to be displayed for a list
+		// Search criteria
+//		MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
+//		MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
+	}
+}
+
+
+class EventWebService extends Event
+{
+	public static function Init()
+	{
+		$aParams = array
+		(
+			"category" => "core/cmdb",
+			"name" => "Web service event",
+			"description" => "Trace of an web service call",
+			"key_type" => "autoincrement",
+			"key_label" => "",
+			"name_attcode" => "",
+			"state_attcode" => "",
+			"reconc_keys" => array(),
+			"db_table" => "priv_event_webservice",
+			"db_key_field" => "id",
+			"db_finalclass_field" => "",
+			"display_template" => "",
+		);
+		MetaModel::Init_Params($aParams);
+		MetaModel::Init_InheritAttributes();
+		MetaModel::Init_AddAttribute(new AttributeString("verb", array("label"=>"Verb", "description"=>"Name of the operation", "allowed_values"=>null, "sql"=>"verb", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
+		//MetaModel::Init_AddAttribute(new AttributeStructure("arguments", array("label"=>"Arguments", "description"=>"Operation arguments", "allowed_values"=>null, "sql"=>"data", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
+		MetaModel::Init_AddAttribute(new AttributeBoolean("result", array("label"=>"Result", "description"=>"Overall success/failure", "allowed_values"=>null, "sql"=>"result", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
+		MetaModel::Init_AddAttribute(new AttributeText("log_info", array("label"=>"Info log", "description"=>"Result info log", "allowed_values"=>null, "sql"=>"log_info", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
+		MetaModel::Init_AddAttribute(new AttributeText("log_warning", array("label"=>"Warning log", "description"=>"Result warning log", "allowed_values"=>null, "sql"=>"log_warning", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
+		MetaModel::Init_AddAttribute(new AttributeText("log_error", array("label"=>"Error log", "description"=>"Result error log", "allowed_values"=>null, "sql"=>"log_error", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
+		MetaModel::Init_AddAttribute(new AttributeText("data", array("label"=>"Data", "description"=>"Result data", "allowed_values"=>null, "sql"=>"data", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
+
+		MetaModel::Init_InheritFilters();
+
+		// Display lists
+		MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'verb', 'result', 'log_info', 'log_warning', 'log_error', 'data')); // Attributes to be displayed for the complete details
+		MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'verb', 'result')); // Attributes to be displayed for a list
+		// Search criteria
+//		MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
+//		MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
+	}
+}
 
 ?>

+ 177 - 39
pages/testlist.inc.php

@@ -1174,32 +1174,146 @@ class TestItopWebServices extends TestWebServices
 	}
 }
 
+
 $aWebServices = array(
 	array(
+		'verb' => 'GetVersion',
+		'expected result' => true,
+		'explain result' => 'n/a',
+		'args' => array(),
+	),
+	array(
 		'verb' => 'CreateIncidentTicket',
+		'expected result' => true,
+		'explain result' => 'ok, but link attribute unknown',
 		'args' => array(
+			'admin', /* sLogin */
+			'admin', /* sPassword */
+			'Server', /* sType */
 			'desc of ticket', /* sDescription */
 			'initial situation blah blah blah', /* sInitialSituation */
-			array('id' => 1), /* aCallerDesc */
-			array('id' => 2), /* aCustomerDesc */
-			array('id' => 1), /* aWorkgroupDesc */ 
+			'very grave', /* sImpact */
+			new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */
+			new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */
+			new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aWorkgroupDesc */
 			array(
-				array(
-					'class' => 'logInfra',
-					'search' => array('id' => 108),
-					'link_values' => array('impactoche' => 'plus que critique'),
+				new SOAPLinkCreationSpec(
+					'logInfra',
+					array(new SOAPSearchCondition('id', 108)),
+					array(new SOAPAttributeValue('impacting', 'very critical'))
 				),
-				array(
-					'class' => 'bizDevice',
-					'search' => array('name' => 'Router03'),
-					'link_values' => array('impact' => 'ouais bof'),
+				new SOAPLinkCreationSpec(
+					'bizDevice',
+					array(new SOAPSearchCondition('name', 'Router03')),
+					array(new SOAPAttributeValue('impact', 'who cares'))
 				),
 			), /* aImpact */
 			'low' /* sSeverity */
 		),
 	),
+	array(
+		'verb' => 'CreateIncidentTicket',
+		'expected result' => true,
+		'explain result' => 'ok, but CI unknown',
+		'args' => array(
+			'admin', /* sLogin */
+			'admin', /* sPassword */
+			'Desktop', /* sType */
+			'PC burning', /* sDescription */
+			'The power supply suddenly started to warm up', /* sInitialSituation */
+			'The agent could not do his job', /* sImpact */
+			new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */
+			new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */
+			new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aWorkgroupDesc */
+			array(
+				new SOAPLinkCreationSpec(
+					'logInfra',
+					array(new SOAPSearchCondition('id', 99999)),
+					array(new SOAPAttributeValue('impact', 'very critical'))
+				),
+			), /* aImpact */
+			'low' /* sSeverity */
+		),
+	),
+	array(
+		'verb' => 'CreateIncidentTicket',
+		'expected result' => false,
+		'explain result' => 'ok, no CI to attach',
+		'args' => array(
+			'admin', /* sLogin */
+			'admin', /* sPassword */
+			'Network', /* sType */
+			'Houston not reachable', /* sDescription */
+			'Tried to join the shuttle', /* sInitialSituation */
+			'Could not talk to my wife', /* sImpact */
+			new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */
+			new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */
+			new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aWorkgroupDesc */
+			array(
+			), /* aImpact */
+			'low' /* sSeverity */
+		),
+	),
+	array(
+		'verb' => 'CreateIncidentTicket',
+		'expected result' => false,
+		'explain result' => 'caller unknown',
+		'args' => array(
+			'admin', /* sLogin */
+			'admin', /* sPassword */
+			'Network', /* sType */
+			'Houston not reachable', /* sDescription */
+			'Tried to join the shuttle', /* sInitialSituation */
+			'Could not talk to my wife', /* sImpact */
+			new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1000))), /* aCallerDesc */
+			new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */
+			new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aWorkgroupDesc */
+			array(
+			), /* aImpact */
+			'low' /* sSeverity */
+		),
+	),
+	array(
+		'verb' => 'CreateIncidentTicket',
+		'expected result' => false,
+		'explain result' => 'wrong password',
+		'args' => array(
+			'admin', /* sLogin */
+			'xxxxx', /* sPassword */
+			'Network', /* sType */
+			'Houston not reachable', /* sDescription */
+			'Tried to join the shuttle', /* sInitialSituation */
+			'Could not talk to my wife', /* sImpact */
+			new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */
+			new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */
+			new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aWorkgroupDesc */
+			array(
+			), /* aImpact */
+			'low' /* sSeverity */
+		),
+	),
+	array(
+		'verb' => 'CreateIncidentTicket',
+		'expected result' => false,
+		'explain result' => 'wrong login',
+		'args' => array(
+			'xxxxx', /* sLogin */
+			'yyyyy', /* sPassword */
+			'Network', /* sType */
+			'Houston not reachable', /* sDescription */
+			'Tried to join the shuttle', /* sInitialSituation */
+			'Could not talk to my wife', /* sImpact */
+			new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */
+			new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */
+			new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aWorkgroupDesc */
+			array(
+			), /* aImpact */
+			'low' /* sSeverity */
+		),
+	),
 );
 
+
 class TestSoap extends TestSoapWebService
 {
 	static public function GetName() {return 'Test SOAP';}
@@ -1207,34 +1321,56 @@ class TestSoap extends TestSoapWebService
 
 	protected function DoExecute()
 	{
-		$this->m_SoapClient = new SoapClient(
-//			null,
-			"http://localhost:81/trunk/webservices/Itop.wsdl",
+		global $aSOAPMapping;
+
+		// this file is generated dynamically with location = here
+		$sWsdlUri = 'http'.(empty($_SERVER['HTTPS']) ? '' : 's').'://'.$_SERVER['SERVER_NAME'].':'.$_SERVER['SERVER_PORT'].dirname($_SERVER['SCRIPT_NAME']).'/../webservices/itop.wsdl.php';
+
+		ini_set("soap.wsdl_cache_enabled","0");
+		$this->m_SoapClient = new SoapClient
+		(
+			$sWsdlUri,
 			array(
-				//'location' => 'http://localhost:81/trunk/webservices/soapserver.php',
-				//'uri' => 'http://test-itop/',
-				'login' => 'admin',
-				'password' => 'admin',
-				// note: using the classmap functionality lead to APACHE fault on the server side
-				//'classmap' => array('stdClass' => 'ItopError'),
+				//'uri' => 'http://soap-itop/',
+				'classmap' => $aSOAPMapping,
 				'trace' => 1,
 			)
 		);
 
-		global $aWebServices;
-		$aWebService = $aWebServices[0];
-
-//		$oRes = $this->m_SoapClient->CreateIncidentTicket();
-		$oRes = call_user_func_array(array($this->m_SoapClient, $aWebService['verb']), $aWebService['args']);
+		if (false)
+		{
+			print "<pre>\n"; 
+			print_r($this->m_SoapClient->__getTypes());
+			print "</pre>\n";
+		} 
 
-		echo "<pre>\n";
-		print_r($oRes);
-		echo "</pre>\n";
+		global $aWebServices;
+		foreach ($aWebServices as $iPos => $aWebService)
+		{
+			echo "<h4>SOAP call #$iPos ".$aWebService['explain result']."</h4>\n";
+			try
+			{
+				$oRes = call_user_func_array(array($this->m_SoapClient, $aWebService['verb']), $aWebService['args']);
+			}
+			catch(SoapFault $e)
+			{
+				print "<pre>\n"; 
+				print "Request: \n".htmlspecialchars($this->m_SoapClient->__getLastRequest()) ."\n"; 
+				print "Response: \n".htmlspecialchars($this->m_SoapClient->__getLastResponse())."\n"; 
+				print "</pre>";
+				print "Response in HTML: <p>".$this->m_SoapClient->__getLastResponse()."</p>"; 
+				return false;
+			}
 
-print "<pre>\n"; 
-print "Request: \n".htmlspecialchars($this->m_SoapClient->__getLastRequest()) ."\n"; 
-print "Response: \n".htmlspecialchars($this->m_SoapClient->__getLastResponse())."\n"; 
-print "</pre>"; 
+			echo "<pre>\n";
+			print_r($oRes);
+			echo "</pre>\n";
+	
+			print "<pre>\n"; 
+			print "Request: \n".htmlspecialchars($this->m_SoapClient->__getLastRequest()) ."\n"; 
+			print "Response: \n".htmlspecialchars($this->m_SoapClient->__getLastResponse())."\n"; 
+			print "</pre>";
+		} 
 
 		return true;
 	}
@@ -1249,14 +1385,16 @@ class TestWebServicesDirect extends TestBizModel
 
 	protected function DoExecute()
 	{
-		global $aWebServices;
-		$aWebService = $aWebServices[0];
-
 		$oWebServices = new WebServices();
-		$oRes = call_user_func_array(array($oWebServices, $aWebService['verb']), $aWebService['args']);
-		echo "<pre>\n";
-		print_r($oRes);
-		echo "</pre>\n";
+
+		global $aWebServices;
+		foreach ($aWebServices as $aWebService)
+		{
+			$oRes = call_user_func_array(array($oWebServices, $aWebService['verb']), $aWebService['args']);
+			echo "<pre>\n";
+			print_r($oRes);
+			echo "</pre>\n";
+		}
 		return true;
 	}
 }

+ 3 - 0
toolkit.php

@@ -15,6 +15,9 @@ echo "<h2>Itop consultant</h2>\n";
 echo "<a href=\"./pages/ITopConsultant.php?config=..%2Fconfig-itop.php\">Check model, Create DB, Update DB (new class, new attribute)</a></br>\n";
 echo "<a href=\"./pages/db_importer.php\">Backup and restore (shortcut)</a></br>\n";
 echo "<a href=\"./pages/schema.php\">Objects schema (shortcut)</a></br>\n";
+echo "<h2>Web services</h2>\n";
+echo "<a href=\"./webservices/soapserver.php\">Available functions</a></br>\n";
+echo "<a href=\"./webservices/itop.wsdl.php\">WSDL (dynamically generated)</a></br>\n";
 echo "<h2>Not working or deprecated</h2>\n";
 echo "<a href=\"./pages/data_generator.php\">Data generator</a></br>\n";
 echo "<a href=\"./pages/advanced_search.php\">ITop finder</a></br>\n";

+ 0 - 4
webservices/itop.wsdl

@@ -1,4 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<!-- WSDL file generated by PHP WSDLCreator (http://www.protung.ro) -->
-<definitions name="WSDLItop" targetNamespace="urn:WSDLItop" xmlns:typens="urn:WSDLItop" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:typens0="http://localhost/php2swdl"><message name="CreateIncidentTicket"><part name="sDescription" type="xsd:anyType"></part><part name="sInitialSituation" type="xsd:anyType"></part><part name="aCallerDesc" type="xsd:anyType"></part><part name="aCustomerDesc" type="xsd:anyType"></part><part name="aWorkgroupDesc" type="xsd:anyType"></part><part name="aImpactedCIs" type="xsd:anyType"></part><part name="sSeverity" type="xsd:anyType"></part></message><message name="CreateIncidentTicketResponse"><part name="CreateIncidentTicketReturn" type="typens0:WebServiceResult"></part></message><portType name="WebServicesPortType"><operation name="CreateIncidentTicket"><documentation>Create an incident ticket from a monitoring system
-Some CIs might be specified (by their name/IP)</documentation><input message="typens:CreateIncidentTicket"></input><output message="typens:CreateIncidentTicketResponse"></output></operation></portType><binding name="WebServicesBinding" type="typens:WebServicesPortType"><soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"></soap:binding><operation name="CreateIncidentTicket"><soap:operation soapAction="urn:WebServicesAction"></soap:operation><input><soap:body namespace="urn:WSDLItop" use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"></soap:body></input><output><soap:body namespace="urn:WSDLItop" use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"></soap:body></output></operation></binding><service name="WSDLItopService"><port name="WebServicesPort" binding="typens:WebServicesBinding"><soap:address location="http://localhost:81/trunk/webservices/soapserver.php"></soap:address></port></service></definitions>

+ 21 - 0
webservices/itop.wsdl.php

@@ -0,0 +1,21 @@
+<?php
+// This is to make sure that the client will accept it....
+//
+header('Content-Type: application/xml; charset=UTF-8');
+//header('Content-Disposition: attachment; filename="itop.wsdl"');
+header('Content-Disposition: online; filename="itop.wsdl"');
+
+$sMyWsdl = './itop.wsdl.tpl';
+
+$sRawFile = file_get_contents($sMyWsdl);
+
+$sServerURI = 'http'.(empty($_SERVER['HTTPS']) ? '' : 's').'://'.$_SERVER['SERVER_NAME'].':'.$_SERVER['SERVER_PORT'].dirname($_SERVER['SCRIPT_NAME']).'/soapserver.php';
+
+$sFinalFile = str_replace(
+	'___SOAP_SERVER_URI___',
+	$sServerURI,
+	$sRawFile
+);
+
+echo $sFinalFile;
+?>

+ 173 - 0
webservices/itop.wsdl.tpl

@@ -0,0 +1,173 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<!-- WSDL file originally generated by Zend Studio, then reworked manually -->
+
+<definitions name="ITop" targetNamespace="urn:ITop" xmlns:typens="urn:ITop" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns="http://schemas.xmlsoap.org/wsdl/">
+	<types>
+		<xsd:schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:ITop">
+			<!-- Added the following import tag to pass the Eclipse validation -->
+			<xsd:import namespace="http://schemas.xmlsoap.org/soap/encoding/" />
+			<xsd:complexType name="SearchCondition">
+				<xsd:all>
+					<xsd:element name="attcode" type="xsd:string"/>
+					<xsd:element name="value" type="xsd:anyType"/>
+				</xsd:all>
+			</xsd:complexType>
+			<xsd:complexType name="ArrayOfSearchCondition">
+				<xsd:complexContent mixed="false">
+					<xsd:restriction base="soapenc:Array">
+						<xsd:attribute ref="soapenc:arrayType" wsdl:arrayType="typens:SearchCondition[]"/>
+					</xsd:restriction>
+				</xsd:complexContent>
+			</xsd:complexType>
+			<xsd:complexType name="ExternalKeySearch">
+				<xsd:all>
+					<xsd:element name="conditions" type="typens:ArrayOfSearchCondition"/>
+				</xsd:all>
+			</xsd:complexType>
+			<xsd:complexType name="AttributeValue">
+				<xsd:all>
+					<xsd:element name="attcode" type="xsd:string"/>
+					<xsd:element name="value" type="xsd:anyType"/>
+				</xsd:all>
+			</xsd:complexType>
+			<xsd:complexType name="ArrayOfAttributeValue">
+				<xsd:complexContent mixed="false">
+					<xsd:restriction base="soapenc:Array">
+						<xsd:attribute ref="soapenc:arrayType" wsdl:arrayType="typens:AttributeValue[]"/>
+					</xsd:restriction>
+				</xsd:complexContent>
+			</xsd:complexType>
+			<xsd:complexType name="LinkCreationSpec">
+				<xsd:all>
+					<xsd:element name="class" type="xsd:string"/>
+					<xsd:element name="conditions" type="typens:ArrayOfSearchCondition"/>
+					<xsd:element name="attributes" type="typens:ArrayOfAttributeValue"/>
+				</xsd:all>
+			</xsd:complexType>
+			<xsd:complexType name="ArrayOfLinkCreationSpec">
+				<xsd:complexContent mixed="false">
+					<xsd:restriction base="soapenc:Array">
+						<xsd:attribute ref="soapenc:arrayType" wsdl:arrayType="typens:LinkCreationSpec[]"/>
+					</xsd:restriction>
+				</xsd:complexContent>
+			</xsd:complexType>
+			<xsd:complexType name="LogMessage">
+				<xsd:all>
+					<xsd:element name="text" type="xsd:string"/>
+				</xsd:all>
+			</xsd:complexType>
+			<xsd:complexType name="ArrayOfLogMessage">
+				<xsd:complexContent mixed="false">
+					<xsd:restriction base="soapenc:Array">
+						<xsd:attribute ref="soapenc:arrayType" wsdl:arrayType="typens:LogMessage[]"/>
+					</xsd:restriction>
+				</xsd:complexContent>
+			</xsd:complexType>
+			<xsd:complexType name="ResultLog">
+				<xsd:all>
+					<xsd:element name="messages" type="typens:ArrayOfLogMessage"/>
+				</xsd:all>
+			</xsd:complexType>
+			<xsd:complexType name="ResultData">
+				<xsd:all>
+					<xsd:element name="key" type="xsd:string"/>
+					<xsd:element name="value" type="xsd:anyType"/>
+				</xsd:all>
+			</xsd:complexType>
+			<xsd:complexType name="ArrayOfResultData">
+				<xsd:complexContent mixed="false">
+					<xsd:restriction base="soapenc:Array">
+						<xsd:attribute ref="soapenc:arrayType" wsdl:arrayType="typens:ResultData[]"/>
+					</xsd:restriction>
+				</xsd:complexContent>
+			</xsd:complexType>
+			<xsd:complexType name="ResultMessage">
+				<xsd:all>
+					<xsd:element name="label" type="xsd:string"/>
+					<xsd:element name="values" type="typens:ArrayOfResultData"/>
+				</xsd:all>
+			</xsd:complexType>
+			<xsd:complexType name="ArrayOfResultMessage">
+				<xsd:complexContent mixed="false">
+					<xsd:restriction base="soapenc:Array">
+						<xsd:attribute ref="soapenc:arrayType" wsdl:arrayType="typens:ResultMessage[]"/>
+					</xsd:restriction>
+				</xsd:complexContent>
+			</xsd:complexType>
+			<xsd:complexType name="Result">
+				<xsd:all>
+					<xsd:element name="status" type="xsd:boolean"/>
+					<xsd:element name="result" type="typens:ArrayOfResultMessage"/>
+					<xsd:element name="errors" type="typens:ResultLog"/>
+					<xsd:element name="warnings" type="typens:ResultLog"/>
+					<xsd:element name="infos" type="typens:ResultLog"/>
+				</xsd:all>
+			</xsd:complexType>
+		</xsd:schema>
+	</types>
+	<message name="GetVersion">
+	</message>
+	<message name="GetVersionResponse">
+		<part name="GetVersionReturn" type="xsd:string"/>
+	</message>
+	<message name="CreateIncidentTicket">
+		<part name="login" type="xsd:string"/>
+		<part name="password" type="xsd:string"/>
+		<part name="type" type="xsd:string"/>
+		<part name="description" type="xsd:string"/>
+		<part name="initialsituation" type="xsd:string"/>
+		<part name="impact" type="xsd:string"/>
+		<part name="caller" type="typens:ExternalKeySearch"/>
+		<part name="customer" type="typens:ExternalKeySearch"/>
+		<part name="workgroup" type="typens:ExternalKeySearch"/>
+		<part name="impacted_cis" type="typens:ArrayOfLinkCreationSpec"/>
+		<part name="severity" type="xsd:string"/>
+	</message>
+	<message name="CreateIncidentTicketResponse">
+		<part name="CreateIncidentTicketReturn" type="typens:Result"/>
+	</message>
+	<portType name="WebServicePortType">
+		<operation name="GetVersion">
+			<documentation>
+				Get the current version of Itop
+				As this service is very simple, it is a test to get trained for more complex operations 
+			</documentation>
+			<input message="typens:GetVersion"/>
+			<output message="typens:GetVersionResponse"/>
+		</operation>
+		<operation name="CreateIncidentTicket">
+			<documentation>
+				Create a ticket, return information about reconciliation on external keys and the created ticket
+			</documentation>
+			<input message="typens:CreateIncidentTicket"/>
+			<output message="typens:CreateIncidentTicketResponse"/>
+		</operation>
+	</portType>
+	<binding name="WebServiceBinding" type="typens:WebServicePortType">
+		<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
+		<operation name="GetVersion">
+			<soap:operation soapAction="urn:WebServiceAction"/>
+			<input>
+				<soap:body namespace="urn:ITop" use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+			</input>
+			<output>
+				<soap:body namespace="urn:ITop" use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+			</output>
+		</operation>
+		<operation name="CreateIncidentTicket">
+			<soap:operation soapAction="urn:WebServiceAction"/>
+			<input>
+				<soap:body namespace="urn:ITop" use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+			</input>
+			<output>
+				<soap:body namespace="urn:ITop" use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+			</output>
+		</operation>
+	</binding>
+	<service name="ITopService">
+		<port name="WebServicePort" binding="typens:WebServiceBinding">
+			<soap:address location="___SOAP_SERVER_URI___"/>
+		</port>
+	</service>
+</definitions>

+ 8 - 9
webservices/soapserver.php

@@ -20,18 +20,17 @@ require_once('../application/startup.inc.php');
 
 require('./webservices.class.inc.php');
 
+// this file is generated dynamically with location = here
+$sWsdlUri = 'http'.(empty($_SERVER['HTTPS']) ? '' : 's').'://'.$_SERVER['SERVER_NAME'].':'.$_SERVER['SERVER_PORT'].dirname($_SERVER['SCRIPT_NAME']).'/../webservices/itop.wsdl.php';
 
-// pb ? - login_web_page::DoLogin(); // Check user rights and prompt if needed
 
-// Main program
+ini_set("soap.wsdl_cache_enabled","0");
 
-$oSoapServer = new SoapServer(
-	null,
-	//"http://localhost:81/trunk/webservices/Itop.wsdl", // to be a file generated dynamically with location = here
+$oSoapServer = new SoapServer
+(
+	$sWsdlUri,
 	array(
-		'uri' => 'http://test-itop/',
-		// note: using the classmap and no WSDL spec causes a fault in APACHE (looks like an infinite loop)
-		//'classmap' => array('ItopErrorSOAP' => 'ItopError')
+		'classmap' => $aSOAPMapping
 	)
 );
 // $oSoapServer->setPersistence(SOAP_PERSISTENCE_SESSION);
@@ -51,6 +50,6 @@ else
 		echo "<li>$sFunc</li>\n";
 	}
 	echo "</ul>\n";
-	echo "";
+	echo "<p>Here the <a href=\"$sWsdlUri\">WSDL file</a><p>";
 }
 ?>

+ 257 - 29
webservices/webservices.class.inc.php

@@ -1,5 +1,7 @@
 <?php
 
+require_once('../webservices/itopsoaptypes.class.inc.php');
+
 /**
  * Create Ticket web service
  * Web Service API wrapper
@@ -52,11 +54,51 @@ class WebServiceResult
 	public function __construct()
 	{
 		$this->m_bStatus = true;
+		$this->m_aResult = array();
 		$this->m_aErrors = array();
 		$this->m_aWarnings = array();
 		$this->m_aInfos = array();
 	}
 
+	public function ToSoapStructure()
+	{
+		$aResults = array();
+		foreach($this->m_aResult as $sLabel => $aData)
+		{
+			$aValues = array();
+			foreach($aData as $sKey => $value)
+			{
+				$aValues[] = new SoapResultData($sKey, $value);
+			}
+			$aResults[] = new SoapResultMessage($sLabel, $aValues);
+		}
+		$aInfos = array();
+		foreach($this->m_aInfos as $sMessage)
+		{
+			$aInfos[] = new SoapLogMessage($sMessage);
+		}
+		$aWarnings = array();
+		foreach($this->m_aWarnings as $sMessage)
+		{
+			$aWarnings[] = new SoapLogMessage($sMessage);
+		}
+		$aErrors = array();
+		foreach($this->m_aErrors as $sMessage)
+		{
+			$aErrors[] = new SoapLogMessage($sMessage);
+		}
+
+		$oRet = new SOAPResult(
+			$this->m_bStatus,
+			$aResults,
+			new SOAPResultLog($aErrors),
+			new SOAPResultLog($aWarnings),
+			new SOAPResultLog($aInfos)
+		);
+
+		return $oRet;
+	}
+
 	/**
 	 * Did the current processing encounter a stopper issue ?
 	 *
@@ -68,20 +110,36 @@ class WebServiceResult
 	}
 
 	/**
+	 * Add result details - object reference
+	 *
+	 * @param string sLabel
+	 * @param object oObject
+	 */
+	public function AddResultObject($sLabel, $oObject)
+	{
+		$this->m_aResult[$sLabel] = array(
+			'id' => $oObject->GetKey(),
+			'name' => $oObject->GetName(),
+			'url' => $oObject->GetHyperlink(),
+		);
+	}
+
+	/**
 	 * Log an error
 	 *
-	 * @param description $sDescription
+	 * @param string sDescription
 	 */
 	public function LogError($sDescription)
 	{
 		$this->m_aErrors[] = $sDescription;
-		$this->m_bStatus = false;
+		// Note: SOAP do transform false into null
+		$this->m_bStatus = 0;
 	}
 
 	/**
 	 * Log a warning
 	 *
-	 * @param description $sDescription
+	 * @param string sDescription
 	 */
 	public function LogWarning($sDescription)
 	{
@@ -91,8 +149,8 @@ class WebServiceResult
 	/**
 	 * Log an error or a warning
 	 *
-	 * @param string $sDescription
-	 * @param boolean $bIsStopper
+	 * @param string sDescription
+	 * @param boolean bIsStopper
 	 */
 	public function LogIssue($sDescription, $bIsStopper = true)
 	{
@@ -109,15 +167,77 @@ class WebServiceResult
 	{
 		$this->m_aInfos[] = $sDescription;
 	}
+
+	protected static function LogToText($aLog)
+	{
+		return implode("\n", $aLog);
+	}
+
+	public function GetInfoAsText()
+	{
+		return self::LogToText($this->m_aInfos);
+	}
+
+	public function GetWarningsAsText()
+	{
+		return self::LogToText($this->m_aWarnings);
+	}
+
+	public function GetErrorsAsText()
+	{
+		return self::LogToText($this->m_aErrors);
+	}
+
+	public function GetReturnedDataAsText()
+	{
+		$sRet = '';
+		foreach ($this->m_aResult as $sKey => $value)
+		{
+			$sRet .= "===== $sKey =====\n";
+			$sRet .= print_r($value, true);
+		}
+		return $sRet;
+	}
+}
+
+
+class WebServiceResultFailedLogin extends WebServiceResult
+{
+	public function __construct($sLogin)
+	{
+		parent::__construct();
+		$this->LogError("Wrong credentials: '$sLogin'");
+	}
 }
 
 class WebServices
 {
 	/**
+	 * Helper to log a service delivery
+	 *
+	 * @param string sVerb
+	 * @param array aArgs
+	 * @param WebServiceResult oRes
+	 *
+	 */
+	protected function LogUsage($sVerb, $oRes)
+	{
+		$oLog = new EventWebService();
+		$oLog->Set('userinfo', UserRights::GetUser());
+		$oLog->Set('verb', $sVerb);
+		$oLog->Set('result', $oRes->IsOk());
+		$oLog->Set('log_info', $oRes->GetInfoAsText());
+		$oLog->Set('log_warning', $oRes->GetWarningsAsText());
+		$oLog->Set('log_error', $oRes->GetErrorsAsText());
+		$oLog->Set('data', $oRes->GetReturnedDataAsText());
+		$oLog->DBInsertNoReload();
+	}
+
+	/**
 	 * Helper to set an external key
 	 *
 	 * @param string sAttCode
-	 * @param array aCallerDesc
+	 * @param array aExtKeyDesc
 	 * @param DBObject oTargetObj
 	 * @param WebServiceResult oRes
 	 *
@@ -190,6 +310,11 @@ class WebServices
 		$aItemsNotFound = array();
 		foreach ($aLinkList as $aItemData)
 		{
+			if (!array_key_exists('class', $aItemData))
+			{
+				$oRes->LogWarning("Linked object descriptor: missing 'class' specification");
+				continue; // skip
+			}
 			$sTargetClass = $aItemData['class'];
 			if (!MetaModel::IsValidClass($sTargetClass))
 			{
@@ -273,6 +398,84 @@ class WebServices
 		return $aItemsNotFound;
 	}
 
+
+	static protected function SoapStructToExternalKeySearch(SoapExternalKeySearch $oExternalKeySearch)
+	{
+		$aRes = array();
+		foreach($oExternalKeySearch->conditions as $oSearchCondition)
+		{
+			$aRes[$oSearchCondition->attcode] = $oSearchCondition->value;
+		}
+		return $aRes;
+	}
+
+	static protected function SoapStructToLinkCreationSpec(SoapLinkCreationSpec $oLinkCreationSpec)
+	{
+		$aRes = array
+		(
+			'class' => $oLinkCreationSpec->class,
+			'search' => array(),
+			'link_values' => array(),
+		);
+
+		foreach($oLinkCreationSpec->conditions as $oSearchCondition)
+		{
+			$aRes['search'][$oSearchCondition->attcode] = $oSearchCondition->value;
+		}
+
+		foreach($oLinkCreationSpec->attributes as $oAttributeValue)
+		{
+			$aRes['link_values'][$oAttributeValue->attcode] = $oAttributeValue->value;
+		}
+
+		return $aRes;
+	}
+
+
+	/**
+	 * Get the server version (TODO: get it dynamically, where ?)
+	 *	 
+	 * @return WebServiceResult
+	 */
+	public function GetVersion()
+	{
+		return "0.8";
+	}
+
+	public function CreateIncidentTicket($sLogin, $sPassword, $sType, $sDescription, $sInitialSituation, $sImpact, $oCallerDesc, $oCustomerDesc, $oWorkgroupDesc, $aSOAPImpactedCIs, $sSeverity)
+	{
+		if (!UserRights::Login($sLogin, $sPassword))
+		{
+			$oRes = new WebServiceResultFailedLogin($sLogin);
+			$this->LogUsage(__FUNCTION__, $oRes);
+
+			return $oRes->ToSoapStructure();
+		}
+
+		$aCallerDesc = self::SoapStructToExternalKeySearch($oCallerDesc);
+		$aCustomerDesc = self::SoapStructToExternalKeySearch($oCustomerDesc);
+		$aWorkgroupDesc = self::SoapStructToExternalKeySearch($oWorkgroupDesc);
+		$aImpactedCIs = array();
+		foreach($aSOAPImpactedCIs as $oImpactedCIs)
+		{
+			$aImpactedCIs[] = self::SoapStructToLinkCreationSpec($oImpactedCIs);
+		}
+
+		$oRes = $this->_CreateIncidentTicket
+		(
+			$sType,
+			$sDescription,
+			$sInitialSituation,
+			$sImpact,
+			$aCallerDesc,
+			$aCustomerDesc,
+			$aWorkgroupDesc,
+			$aImpactedCIs,
+			$sSeverity
+		);
+		return $oRes->ToSoapStructure();
+	}
+
 	/**
 	 * Create an incident ticket from a monitoring system
 	 * Some CIs might be specified (by their name/IP)
@@ -287,36 +490,61 @@ class WebServices
 	 *
 	 * @return WebServiceResult
 	 */
-	function CreateIncidentTicket($sDescription, $sInitialSituation, $aCallerDesc, $aCustomerDesc, $aWorkgroupDesc, $aImpactedCIs, $sSeverity)
+	protected function _CreateIncidentTicket($sType, $sDescription, $sInitialSituation, $sImpact, $aCallerDesc, $aCustomerDesc, $aWorkgroupDesc, $aImpactedCIs, $sSeverity)
 	{
-		$oRes = new WebServiceResult();
 
-		new CMDBChange();
-		$oMyChange = MetaModel::NewObject("CMDBChange");
-		$oMyChange->Set("date", time());
-		$oMyChange->Set("userinfo", "Administrator");
-		$iChangeId = $oMyChange->DBInsertNoReload();
-
-		$oNewTicket = MetaModel::NewObject('bizIncidentTicket');
-		$oNewTicket->Set('title', $sDescription);
-		$oNewTicket->Set('initial_situation', $sInitialSituation);
-		$oNewTicket->Set('severity', $sSeverity);
-
-		$this->SetExternalKey('org_id', $aCustomerDesc, $oNewTicket, $oRes);
-		$this->SetExternalKey('caller_id', $aCallerDesc, $oNewTicket, $oRes);
-		$this->SetExternalKey('workgroup_id', $aWorkgroupDesc, $oNewTicket, $oRes);
+		$oRes = new WebServiceResult();
 
-		$aDevicesNotFound = $this->AddLinkedObjects('impacted_infra_manual', 'logInfra', $aImpactedCIs, $oNewTicket, $oRes);
-		if (count($aDevicesNotFound) > 0)
+		try
+		{
+			new CMDBChange();
+			$oMyChange = MetaModel::NewObject("CMDBChange");
+			$oMyChange->Set("date", time());
+			$oMyChange->Set("userinfo", "Administrator");
+			$iChangeId = $oMyChange->DBInsertNoReload();
+	
+			$oNewTicket = MetaModel::NewObject('bizIncidentTicket');
+			$oNewTicket->Set('type', $sType);
+			$oNewTicket->Set('title', $sDescription);
+			$oNewTicket->Set('initial_situation', $sInitialSituation);
+			$oNewTicket->Set('severity', $sSeverity);
+	
+			$this->SetExternalKey('org_id', $aCustomerDesc, $oNewTicket, $oRes);
+			$this->SetExternalKey('caller_id', $aCallerDesc, $oNewTicket, $oRes);
+			$this->SetExternalKey('workgroup_id', $aWorkgroupDesc, $oNewTicket, $oRes);
+	
+			$aDevicesNotFound = $this->AddLinkedObjects('impacted_infra_manual', 'logInfra', $aImpactedCIs, $oNewTicket, $oRes);
+			if (count($aDevicesNotFound) > 0)
+			{
+				$oNewTicket->Set('impact', $sImpact.' - Related CIs: '.implode(', ', $aDevicesNotFound));
+			}
+			else
+			{
+				$oNewTicket->Set('impact', $sImpact);
+			}
+	
+			if (!$oNewTicket->CheckToInsert())
+			{
+				$oRes->LogError("The ticket could not be created due to forbidden values (or inconsistent values)");
+			}
+	
+			if ($oRes->IsOk())
+			{
+				$iId = $oNewTicket->DBInsertTrackedNoReload($oMyChange);
+				$oRes->LogInfo("Created ticket #$iId");
+				$oRes->AddResultObject('created', $oNewTicket);
+			}
+		}
+		catch (CoreException $e)
 		{
-			$oTargetObj->Set('impact', implode(', ', $aDevicesNotFound));
+			$oRes->LogError($e->getMessage());
 		}
-
-		if ($oRes->IsOk())
+		catch (Exception $e)
 		{
-			$iId = $oNewTicket->DBInsertTrackedNoReload($oMyChange);
-			$oRes->LogInfo("Created ticket #$iId");
+			$oRes->LogError($e->getMessage());
 		}
+
+		$this->LogUsage(__FUNCTION__, $oRes);
 		return $oRes;
 	}
 }