1<?php 2// Copyright (C) 2010-2017 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 * Value set definitions (from a fixed list or from a query, etc.) 22 * 23 * @copyright Copyright (C) 2010-2017 Combodo SARL 24 * @license http://opensource.org/licenses/AGPL-3.0 25 */ 26 27require_once('MyHelpers.class.inc.php'); 28 29/** 30 * ValueSetDefinition 31 * value sets API and implementations 32 * 33 * @package iTopORM 34 */ 35abstract class ValueSetDefinition 36{ 37 protected $m_bIsLoaded = false; 38 protected $m_aValues = array(); 39 40 41 // Displayable description that could be computed out of the std usage context 42 public function GetValuesDescription() 43 { 44 $aValues = $this->GetValues(array(), ''); 45 $aDisplayedValues = array(); 46 foreach($aValues as $key => $value) 47 { 48 $aDisplayedValues[] = "$key => $value"; 49 } 50 $sAllowedValues = implode(', ', $aDisplayedValues); 51 return $sAllowedValues; 52 } 53 54 55 public function GetValues($aArgs, $sContains = '', $sOperation = 'contains') 56 { 57 if (!$this->m_bIsLoaded) 58 { 59 $this->LoadValues($aArgs); 60 $this->m_bIsLoaded = true; 61 } 62 if (strlen($sContains) == 0) 63 { 64 // No filtering 65 $aRet = $this->m_aValues; 66 } 67 else 68 { 69 // Filter on results containing the needle <sContain> 70 $aRet = array(); 71 foreach ($this->m_aValues as $sKey=>$sValue) 72 { 73 if (stripos($sValue, $sContains) !== false) 74 { 75 $aRet[$sKey] = $sValue; 76 } 77 } 78 } 79 // Sort on the display value 80 asort($aRet); 81 return $aRet; 82 } 83 84 abstract protected function LoadValues($aArgs); 85} 86 87 88/** 89 * Set of existing values for an attribute, given a search filter 90 * 91 * @package iTopORM 92 */ 93class ValueSetObjects extends ValueSetDefinition 94{ 95 protected $m_sContains; 96 protected $m_sOperation; 97 protected $m_sFilterExpr; // in OQL 98 protected $m_sValueAttCode; 99 protected $m_aOrderBy; 100 protected $m_aExtraConditions; 101 private $m_bAllowAllData; 102 private $m_aModifierProperties; 103 private $m_bSort; 104 private $m_iLimit; 105 106 107 /** 108 * @param hash $aOrderBy Array of '[<classalias>.]attcode' => bAscending 109 */ 110 public function __construct($sFilterExp, $sValueAttCode = '', $aOrderBy = array(), $bAllowAllData = false, $aModifierProperties = array()) 111 { 112 $this->m_sContains = ''; 113 $this->m_sOperation = ''; 114 $this->m_sFilterExpr = $sFilterExp; 115 $this->m_sValueAttCode = $sValueAttCode; 116 $this->m_aOrderBy = $aOrderBy; 117 $this->m_bAllowAllData = $bAllowAllData; 118 $this->m_aModifierProperties = $aModifierProperties; 119 $this->m_aExtraConditions = array(); 120 $this->m_bSort = true; 121 $this->m_iLimit = 0; 122 } 123 124 public function SetModifierProperty($sPluginClass, $sProperty, $value) 125 { 126 $this->m_aModifierProperties[$sPluginClass][$sProperty] = $value; 127 } 128 129 public function AddCondition(DBSearch $oFilter) 130 { 131 $this->m_aExtraConditions[] = $oFilter; 132 } 133 134 public function ToObjectSet($aArgs = array(), $sContains = '', $iAdditionalValue = null) 135 { 136 if ($this->m_bAllowAllData) 137 { 138 $oFilter = DBObjectSearch::FromOQL_AllData($this->m_sFilterExpr); 139 } 140 else 141 { 142 $oFilter = DBObjectSearch::FromOQL($this->m_sFilterExpr); 143 } 144 foreach($this->m_aExtraConditions as $oExtraFilter) 145 { 146 $oFilter = $oFilter->Intersect($oExtraFilter); 147 } 148 foreach($this->m_aModifierProperties as $sPluginClass => $aProperties) 149 { 150 foreach ($aProperties as $sProperty => $value) 151 { 152 $oFilter->SetModifierProperty($sPluginClass, $sProperty, $value); 153 } 154 } 155 if ($iAdditionalValue > 0) 156 { 157 $oSearchAdditionalValue = new DBObjectSearch($oFilter->GetClass()); 158 $oSearchAdditionalValue->AddConditionExpression( new BinaryExpression( 159 new FieldExpression('id', $oSearchAdditionalValue->GetClassAlias()), 160 '=', 161 new VariableExpression('current_extkey_id')) 162 ); 163 $oSearchAdditionalValue->AllowAllData(); 164 $oSearchAdditionalValue->SetArchiveMode(true); 165 $oSearchAdditionalValue->SetInternalParams( array('current_extkey_id' => $iAdditionalValue) ); 166 167 $oFilter = new DBUnionSearch(array($oFilter, $oSearchAdditionalValue)); 168 } 169 170 return new DBObjectSet($oFilter, $this->m_aOrderBy, $aArgs); 171 } 172 173 /** 174 * @param $aArgs 175 * @param string $sContains 176 * @param string $sOperation for the values @see self::LoadValues() 177 * 178 * @return array 179 * @throws CoreException 180 * @throws OQLException 181 */ 182 public function GetValues($aArgs, $sContains = '', $sOperation = 'contains') 183 { 184 if (!$this->m_bIsLoaded || ($sContains != $this->m_sContains) || ($sOperation != $this->m_sOperation)) 185 { 186 $this->LoadValues($aArgs, $sContains, $sOperation); 187 $this->m_bIsLoaded = true; 188 } 189 // The results are already filtered and sorted (on friendly name) 190 $aRet = $this->m_aValues; 191 return $aRet; 192 } 193 194 /** 195 * @param $aArgs 196 * @param string $sContains 197 * @param string $sOperation 'contains' or 'equals_start_with' 198 * 199 * @return bool 200 * @throws \CoreException 201 * @throws \OQLException 202 */ 203 protected function LoadValues($aArgs, $sContains = '', $sOperation = 'contains') 204 { 205 $this->m_sContains = $sContains; 206 $this->m_sOperation = $sOperation; 207 208 $this->m_aValues = array(); 209 210 if ($this->m_bAllowAllData) 211 { 212 $oFilter = DBObjectSearch::FromOQL_AllData($this->m_sFilterExpr); 213 } 214 else 215 { 216 $oFilter = DBObjectSearch::FromOQL($this->m_sFilterExpr); 217 } 218 if (!$oFilter) return false; 219 foreach($this->m_aExtraConditions as $oExtraFilter) 220 { 221 $oFilter = $oFilter->Intersect($oExtraFilter); 222 } 223 foreach($this->m_aModifierProperties as $sPluginClass => $aProperties) 224 { 225 foreach ($aProperties as $sProperty => $value) 226 { 227 $oFilter->SetModifierProperty($sPluginClass, $sProperty, $value); 228 } 229 } 230 231 $oExpression = DBObjectSearch::GetPolymorphicExpression($oFilter->GetClass(), 'friendlyname'); 232 $aFields = $oExpression->ListRequiredFields(); 233 $sClass = $oFilter->GetClass(); 234 foreach($aFields as $sField) 235 { 236 $aFieldItems = explode('.', $sField); 237 if ($aFieldItems[0] != $sClass) 238 { 239 $sOperation = 'contains'; 240 break; 241 } 242 } 243 244 switch ($sOperation) 245 { 246 case 'equals': 247 $aAttributes = MetaModel::GetFriendlyNameAttributeCodeList($sClass); 248 $sClassAlias = $oFilter->GetClassAlias(); 249 $aFilters = array(); 250 $oValueExpr = new ScalarExpression($sContains); 251 foreach($aAttributes as $sAttribute) 252 { 253 $oNewFilter = $oFilter->DeepClone(); 254 $oNameExpr = new FieldExpression($sAttribute, $sClassAlias); 255 $oCondition = new BinaryExpression($oNameExpr, '=', $oValueExpr); 256 $oNewFilter->AddConditionExpression($oCondition); 257 $aFilters[] = $oNewFilter; 258 } 259 // Unions are much faster than OR conditions 260 $oFilter = new DBUnionSearch($aFilters); 261 break; 262 case 'start_with': 263 $aAttributes = MetaModel::GetFriendlyNameAttributeCodeList($sClass); 264 $sClassAlias = $oFilter->GetClassAlias(); 265 $aFilters = array(); 266 $oValueExpr = new ScalarExpression($sContains.'%'); 267 foreach($aAttributes as $sAttribute) 268 { 269 $oNewFilter = $oFilter->DeepClone(); 270 $oNameExpr = new FieldExpression($sAttribute, $sClassAlias); 271 $oCondition = new BinaryExpression($oNameExpr, 'LIKE', $oValueExpr); 272 $oNewFilter->AddConditionExpression($oCondition); 273 $aFilters[] = $oNewFilter; 274 } 275 // Unions are much faster than OR conditions 276 $oFilter = new DBUnionSearch($aFilters); 277 break; 278 279 default: 280 $oValueExpr = new ScalarExpression('%'.$sContains.'%'); 281 $oNameExpr = new FieldExpression('friendlyname', $oFilter->GetClassAlias()); 282 $oNewCondition = new BinaryExpression($oNameExpr, 'LIKE', $oValueExpr); 283 $oFilter->AddConditionExpression($oNewCondition); 284 break; 285 } 286 287 $oObjects = new DBObjectSet($oFilter, $this->m_aOrderBy, $aArgs, null, $this->m_iLimit, 0, $this->m_bSort); 288 if (empty($this->m_sValueAttCode)) 289 { 290 $aAttToLoad = array($oFilter->GetClassAlias() => array('friendlyname')); 291 } 292 else 293 { 294 $aAttToLoad = array($oFilter->GetClassAlias() => array($this->m_sValueAttCode)); 295 } 296 $oObjects->OptimizeColumnLoad($aAttToLoad); 297 while ($oObject = $oObjects->Fetch()) 298 { 299 if (empty($this->m_sValueAttCode)) 300 { 301 $this->m_aValues[$oObject->GetKey()] = $oObject->GetName(); 302 } 303 else 304 { 305 $this->m_aValues[$oObject->GetKey()] = $oObject->Get($this->m_sValueAttCode); 306 } 307 } 308 return true; 309 } 310 311 public function GetValuesDescription() 312 { 313 return 'Filter: '.$this->m_sFilterExpr; 314 } 315 316 public function GetFilterExpression() 317 { 318 return $this->m_sFilterExpr; 319 } 320 321 /** 322 * @param $iLimit 323 */ 324 public function SetLimit($iLimit) 325 { 326 $this->m_iLimit = $iLimit; 327 } 328 329 /** 330 * @param $bSort 331 */ 332 public function SetSort($bSort) 333 { 334 $this->m_bSort = $bSort; 335 } 336} 337 338 339/** 340 * Fixed set values (could be hardcoded in the business model) 341 * 342 * @package iTopORM 343 */ 344class ValueSetEnum extends ValueSetDefinition 345{ 346 protected $m_values; 347 348 public function __construct($Values) 349 { 350 $this->m_values = $Values; 351 } 352 353 // Helper to export the datat model 354 public function GetValueList() 355 { 356 $this->LoadValues($aArgs = array()); 357 return $this->m_aValues; 358 } 359 360 protected function LoadValues($aArgs) 361 { 362 if (is_array($this->m_values)) 363 { 364 $aValues = $this->m_values; 365 } 366 elseif (is_string($this->m_values) && strlen($this->m_values) > 0) 367 { 368 $aValues = array(); 369 foreach (explode(",", $this->m_values) as $sVal) 370 { 371 $sVal = trim($sVal); 372 $sKey = $sVal; 373 $aValues[$sKey] = $sVal; 374 } 375 } 376 else 377 { 378 $aValues = array(); 379 } 380 $this->m_aValues = $aValues; 381 return true; 382 } 383} 384 385/** 386 * Fixed set values, defined as a range: 0..59 (with an optional increment) 387 * 388 * @package iTopORM 389 */ 390class ValueSetRange extends ValueSetDefinition 391{ 392 protected $m_iStart; 393 protected $m_iEnd; 394 395 public function __construct($iStart, $iEnd, $iStep = 1) 396 { 397 $this->m_iStart = $iStart; 398 $this->m_iEnd = $iEnd; 399 $this->m_iStep = $iStep; 400 } 401 402 protected function LoadValues($aArgs) 403 { 404 $iValue = $this->m_iStart; 405 for($iValue = $this->m_iStart; $iValue <= $this->m_iEnd; $iValue += $this->m_iStep) 406 { 407 $this->m_aValues[$iValue] = $iValue; 408 } 409 return true; 410 } 411} 412 413 414/** 415 * Data model classes 416 * 417 * @package iTopORM 418 */ 419class ValueSetEnumClasses extends ValueSetEnum 420{ 421 protected $m_sCategories; 422 423 public function __construct($sCategories = '', $sAdditionalValues = '') 424 { 425 $this->m_sCategories = $sCategories; 426 parent::__construct($sAdditionalValues); 427 } 428 429 protected function LoadValues($aArgs) 430 { 431 // Call the parent to parse the additional values... 432 parent::LoadValues($aArgs); 433 434 // Translate the labels of the additional values 435 foreach($this->m_aValues as $sClass => $void) 436 { 437 if (MetaModel::IsValidClass($sClass)) 438 { 439 $this->m_aValues[$sClass] = MetaModel::GetName($sClass); 440 } 441 else 442 { 443 unset($this->m_aValues[$sClass]); 444 } 445 } 446 447 // Then, add the classes from the category definition 448 foreach (MetaModel::GetClasses($this->m_sCategories) as $sClass) 449 { 450 if (MetaModel::IsValidClass($sClass)) 451 { 452 $this->m_aValues[$sClass] = MetaModel::GetName($sClass); 453 } 454 else 455 { 456 unset($this->m_aValues[$sClass]); 457 } 458 } 459 460 return true; 461 } 462} 463