1<?php 2// Copyright (C) 2010-2016 Combodo SARL 3// 4// This file is part of iTop. 5// 6// iTop is free software; you can redistribute it and/or modify 7// it under the terms of the GNU Affero General Public License as published by 8// the Free Software Foundation, either version 3 of the License, or 9// (at your option) any later version. 10// 11// iTop is distributed in the hope that it will be useful, 12// but WITHOUT ANY WARRANTY; without even the implied warranty of 13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14// GNU Affero General Public License for more details. 15// 16// You should have received a copy of the GNU Affero General Public License 17// along with iTop. If not, see <http://www.gnu.org/licenses/> 18 19 20/** 21 * Class WizardHelper 22 * 23 * @copyright Copyright (C) 2010-2016 Combodo SARL 24 * @license http://opensource.org/licenses/AGPL-3.0 25 */ 26 27require_once(APPROOT.'/application/uiwizard.class.inc.php'); 28 29class WizardHelper 30{ 31 protected $m_aData; 32 33 public function __construct() 34 { 35 } 36 /** 37 * Constructs the PHP target object from the parameters sent to the web page by the wizard 38 * @param boolean $bReadUploadedFiles True to also read any uploaded file (for blob/document fields) 39 * @return object 40 */ 41 public function GetTargetObject($bReadUploadedFiles = false) 42 { 43 if (isset($this->m_aData['m_oCurrentValues']['id'])) 44 { 45 $oObj = MetaModel::GetObject($this->m_aData['m_sClass'], $this->m_aData['m_oCurrentValues']['id']); 46 } 47 else 48 { 49 $oObj = MetaModel::NewObject($this->m_aData['m_sClass']); 50 } 51 foreach($this->m_aData['m_oCurrentValues'] as $sAttCode => $value) 52 { 53 // Because this is stored in a Javascript array, unused indexes 54 // are filled with null values and unused keys (stored as strings) contain $$NULL$$ 55 if ( ($sAttCode !='id') && ($value !== '$$NULL$$')) 56 { 57 $oAttDef = MetaModel::GetAttributeDef($this->m_aData['m_sClass'], $sAttCode); 58 if (($oAttDef->IsLinkSet()) && ($value != '') ) 59 { 60 // special handling for lists 61 // assumes this is handled as an array of objects 62 // thus encoded in json like: [ { name:'link1', 'id': 123}, { name:'link2', 'id': 124}...] 63 $aData = json_decode($value, true); // true means decode as a hash array (not an object) 64 // Check what are the meaningful attributes 65 $aFields = $this->GetLinkedWizardStructure($oAttDef); 66 $sLinkedClass = $oAttDef->GetLinkedClass(); 67 $aLinkedObjectsArray = array(); 68 if (!is_array($aData)) 69 { 70 echo ("aData: '$aData' (value: '$value')\n"); 71 } 72 foreach($aData as $aLinkedObject) 73 { 74 $oLinkedObj = MetaModel::NewObject($sLinkedClass); 75 foreach($aFields as $sLinkedAttCode) 76 { 77 if ( isset($aLinkedObject[$sLinkedAttCode]) && ($aLinkedObject[$sLinkedAttCode] !== null) ) 78 { 79 $sLinkedAttDef = MetaModel::GetAttributeDef($sLinkedClass, $sLinkedAttCode); 80 if (($sLinkedAttDef->IsExternalKey()) && ($aLinkedObject[$sLinkedAttCode] != '') && ($aLinkedObject[$sLinkedAttCode] > 0) ) 81 { 82 // For external keys: load the target object so that external fields 83 // get filled too 84 $oTargetObj = MetaModel::GetObject($sLinkedAttDef->GetTargetClass(), $aLinkedObject[$sLinkedAttCode]); 85 $oLinkedObj->Set($sLinkedAttCode, $oTargetObj); 86 } 87 elseif($sLinkedAttDef instanceof AttributeDateTime) 88 { 89 $sDateClass = get_class($sLinkedAttDef); 90 $sDate = $aLinkedObject[$sLinkedAttCode]; 91 if($sDate !== null && $sDate !== '') 92 { 93 $oDateTimeFormat = $sDateClass::GetFormat(); 94 $oDate = $oDateTimeFormat->Parse($sDate); 95 if ($sDateClass == "AttributeDate") 96 { 97 $sDate = $oDate->format('Y-m-d'); 98 } 99 else 100 { 101 $sDate = $oDate->format('Y-m-d H:i:s'); 102 } 103 } 104 105 $oLinkedObj->Set($sLinkedAttCode, $sDate); 106 } 107 else 108 { 109 $oLinkedObj->Set($sLinkedAttCode, $aLinkedObject[$sLinkedAttCode]); 110 } 111 } 112 } 113 $aLinkedObjectsArray[] = $oLinkedObj; 114 } 115 $oSet = DBObjectSet::FromArray($sLinkedClass, $aLinkedObjectsArray); 116 $oObj->Set($sAttCode, $oSet); 117 } 118 else if ( $oAttDef->GetEditClass() == 'Document' ) 119 { 120 if ($bReadUploadedFiles) 121 { 122 $oDocument = utils::ReadPostedDocument('attr_'.$sAttCode, 'fcontents'); 123 $oObj->Set($sAttCode, $oDocument); 124 } 125 else 126 { 127 // Create a new empty document, just for displaying the file name 128 $oDocument = new ormDocument(null, '', $value); 129 $oObj->Set($sAttCode, $oDocument); 130 } 131 } 132 else if ( $oAttDef->GetEditClass() == 'Image' ) 133 { 134 if ($bReadUploadedFiles) 135 { 136 $oDocument = utils::ReadPostedDocument('attr_'.$sAttCode, 'fcontents'); 137 $oObj->Set($sAttCode, $oDocument); 138 } 139 else 140 { 141 // Create a new empty document, just for displaying the file name 142 $oDocument = new ormDocument(null, '', $value); 143 $oObj->Set($sAttCode, $oDocument); 144 } 145 } 146 else if (($oAttDef->IsExternalKey()) && (!empty($value)) && ($value > 0) ) 147 { 148 // For external keys: load the target object so that external fields 149 // get filled too 150 $oTargetObj = MetaModel::GetObject($oAttDef->GetTargetClass(), $value, false); 151 if ($oTargetObj) 152 { 153 $oObj->Set($sAttCode, $oTargetObj); 154 } 155 else 156 { 157 // May happen for security reasons (portal, see ticket N°1074) 158 $oObj->Set($sAttCode, $value); 159 } 160 } 161 else if ($oAttDef instanceof AttributeDateTime) // AttributeDate is derived from AttributeDateTime 162 { 163 if ($value != null) 164 { 165 $oDate = $oAttDef->GetFormat()->Parse($value); 166 if ($oDate instanceof DateTime) 167 { 168 $value = $oDate->format($oAttDef->GetInternalFormat()); 169 } 170 else 171 { 172 $value = null; 173 } 174 } 175 $oObj->Set($sAttCode, $value); 176 } 177 else if ($oAttDef instanceof AttributeTagSet) // AttributeDate is derived from AttributeDateTime 178 { 179 if (is_null($value)) 180 { 181 // happens if field is hidden (see N°1827) 182 $value = array(); 183 } 184 else 185 { 186 $value = json_decode($value, true); 187 } 188 $oTagSet = new ormTagSet(get_class($oObj), $sAttCode, $oAttDef->GetMaxItems()); 189 $oTagSet->SetValues($value['orig_value']); 190 $oTagSet->ApplyDelta($value); 191 $oObj->Set($sAttCode, $oTagSet); 192 } 193 else if ($oAttDef instanceof AttributeSet) // AttributeDate is derived from AttributeDateTime 194 { 195 $value = json_decode($value, true); 196 $oTagSet = new ormSet(get_class($oObj), $sAttCode, $oAttDef->GetMaxItems()); 197 $oTagSet->SetValues($value['orig_value']); 198 $oTagSet->ApplyDelta($value); 199 $oObj->Set($sAttCode, $oTagSet); 200 } 201 else 202 { 203 $oObj->Set($sAttCode, $value); 204 } 205 } 206 } 207 if (isset($this->m_aData['m_sState']) && !empty($this->m_aData['m_sState'])) 208 { 209 $oObj->Set(MetaModel::GetStateAttributeCode($this->m_aData['m_sClass']), $this->m_aData['m_sState']); 210 } 211 $oObj->DoComputeValues(); 212 return $oObj; 213 } 214 215 public function GetFieldsForDefaultValue() 216 { 217 return $this->m_aData['m_aDefaultValueRequested']; 218 } 219 220 public function SetDefaultValue($sAttCode, $value) 221 { 222 // Protect against a request for a non existing field 223 if (isset($this->m_aData['m_oFieldsMap'][$sAttCode])) 224 { 225 $oAttDef = MetaModel::GetAttributeDef($this->m_aData['m_sClass'], $sAttCode); 226 if ($oAttDef->GetEditClass() == 'List') 227 { 228 // special handling for lists 229 // this as to be handled as an array of objects 230 // thus encoded in json like: [ { name:'link1', 'id': 123}, { name:'link2', 'id': 124}...] 231 // NOT YET IMPLEMENTED !! 232 $oSet = $value; 233 $aData = array(); 234 $aFields = $this->GetLinkedWizardStructure($oAttDef); 235 while($oLinkedObj = $oSet->fetch()) 236 { 237 foreach($aFields as $sLinkedAttCode) 238 { 239 $aRow[$sAttCode] = $oLinkedObj->Get($sLinkedAttCode); 240 } 241 $aData[] = $aRow; 242 } 243 $this->m_aData['m_oDefaultValue'][$sAttCode] = json_encode($aData); 244 245 } 246 else 247 { 248 // Normal handling for all other scalar attributes 249 $this->m_aData['m_oDefaultValue'][$sAttCode] = $value; 250 } 251 } 252 } 253 254 public function GetFieldsForAllowedValues() 255 { 256 return $this->m_aData['m_aAllowedValuesRequested']; 257 } 258 259 public function SetAllowedValuesHtml($sAttCode, $sHtml) 260 { 261 // Protect against a request for a non existing field 262 if (isset($this->m_aData['m_oFieldsMap'][$sAttCode])) 263 { 264 $this->m_aData['m_oAllowedValues'][$sAttCode] = $sHtml; 265 } 266 } 267 268 public function ToJSON() 269 { 270 return json_encode($this->m_aData); 271 } 272 273 static public function FromJSON($sJSON) 274 { 275 $oWizHelper = new WizardHelper(); 276 if (get_magic_quotes_gpc()) 277 { 278 $sJSON = stripslashes($sJSON); 279 } 280 $aData = json_decode($sJSON, true); // true means hash array instead of object 281 $oWizHelper->m_aData = $aData; 282 return $oWizHelper; 283 } 284 285 protected function GetLinkedWizardStructure($oAttDef) 286 { 287 $oWizard = new UIWizard(null, $oAttDef->GetLinkedClass()); 288 $aWizardSteps = $oWizard->GetWizardStructure(); 289 $aFields = array(); 290 $sExtKeyToMeCode = $oAttDef->GetExtKeyToMe(); 291 // Retrieve as a flat list, all the attributes that are needed to create 292 // an object of the linked class and put them into a flat array, except 293 // the attribute 'ext_key_to_me' which is a constant in our case 294 foreach($aWizardSteps as $sDummy => $aMainSteps) 295 { 296 // 2 entries: 'mandatory' and 'optional' 297 foreach($aMainSteps as $aSteps) 298 { 299 // One entry for each step of the wizard 300 foreach($aSteps as $sAttCode) 301 { 302 if ($sAttCode != $sExtKeyToMeCode) 303 { 304 $aFields[] = $sAttCode; 305 } 306 } 307 } 308 } 309 return $aFields; 310 } 311 312 public function GetTargetClass() 313 { 314 return $this->m_aData['m_sClass']; 315 } 316 317 public function GetFormPrefix() 318 { 319 return $this->m_aData['m_sFormPrefix']; 320 } 321 322 public function GetInitialState() 323 { 324 return isset($this->m_aData['m_sInitialState']) ? $this->m_aData['m_sInitialState'] : null; 325 } 326 327 public function GetStimulus() 328 { 329 return isset($this->m_aData['m_sStimulus']) ? $this->m_aData['m_sStimulus'] : null; 330 } 331 332 public function GetIdForField($sFieldName) 333 { 334 $sResult = ''; 335 // It may happen that the field we'd like to update does not 336 // exist in the form. For example, if the field should be hidden/read-only 337 // in the current state of the object 338 if (isset($this->m_aData['m_oFieldsMap'][$sFieldName])) 339 { 340 $sResult = $this->m_aData['m_oFieldsMap'][$sFieldName]; 341 } 342 return $sResult; 343 } 344 345 static function ParseJsonSet($oMe, $sLinkClass, $sExtKeyToMe, $sJsonSet) 346 { 347 $aSet = json_decode($sJsonSet, true); // true means hash array instead of object 348 $oSet = CMDBObjectSet::FromScratch($sLinkClass); 349 foreach($aSet as $aLinkObj) 350 { 351 $oLink = MetaModel::NewObject($sLinkClass); 352 foreach($aLinkObj as $sAttCode => $value) 353 { 354 $oAttDef = MetaModel::GetAttributeDef($sLinkClass, $sAttCode); 355 if (($oAttDef->IsExternalKey()) && ($value != '') && ($value > 0)) 356 { 357 // For external keys: load the target object so that external fields 358 // get filled too 359 $oTargetObj = MetaModel::GetObject($oAttDef->GetTargetClass(), $value); 360 $oLink->Set($sAttCode, $oTargetObj); 361 } 362 $oLink->Set($sAttCode, $value); 363 } 364 $oLink->Set($sExtKeyToMe, $oMe->GetKey()); 365 $oSet->AddObject($oLink); 366 } 367 return $oSet; 368 } 369} 370