| 1 | <?php |
| 2 | /* |
| 3 | +--------------------------------------------------------------------+ |
| 4 | | CiviCRM version 4.6 | |
| 5 | +--------------------------------------------------------------------+ |
| 6 | | Copyright CiviCRM LLC (c) 2004-2015 | |
| 7 | +--------------------------------------------------------------------+ |
| 8 | | This file is a part of CiviCRM. | |
| 9 | | | |
| 10 | | CiviCRM is free software; you can copy, modify, and distribute it | |
| 11 | | under the terms of the GNU Affero General Public License | |
| 12 | | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. | |
| 13 | | | |
| 14 | | CiviCRM is distributed in the hope that it will be useful, but | |
| 15 | | WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 16 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
| 17 | | See the GNU Affero General Public License for more details. | |
| 18 | | | |
| 19 | | You should have received a copy of the GNU Affero General Public | |
| 20 | | License and the CiviCRM Licensing Exception along | |
| 21 | | with this program; if not, contact CiviCRM LLC | |
| 22 | | at info[AT]civicrm[DOT]org. If you have questions about the | |
| 23 | | GNU Affero General Public License or the licensing of CiviCRM, | |
| 24 | | see the CiviCRM license FAQ at http://civicrm.org/licensing | |
| 25 | +--------------------------------------------------------------------+ |
| 26 | */ |
| 27 | |
| 28 | /** |
| 29 | * Component stores all the static and dynamic information of the various |
| 30 | * CiviCRM components |
| 31 | * |
| 32 | * @package CRM |
| 33 | * @copyright CiviCRM LLC (c) 2004-2015 |
| 34 | * $Id$ |
| 35 | * |
| 36 | */ |
| 37 | class CRM_Core_Component { |
| 38 | |
| 39 | /** |
| 40 | * End part (filename) of the component information class'es name |
| 41 | * that needs to be present in components main directory. |
| 42 | */ |
| 43 | const COMPONENT_INFO_CLASS = 'Info'; |
| 44 | |
| 45 | private static $_info = NULL; |
| 46 | |
| 47 | static $_contactSubTypes = NULL; |
| 48 | |
| 49 | /** |
| 50 | * @param bool $force |
| 51 | * |
| 52 | * @return array|null |
| 53 | */ |
| 54 | private static function &_info($force = FALSE) { |
| 55 | if (self::$_info == NULL || $force) { |
| 56 | self::$_info = array(); |
| 57 | $c = array(); |
| 58 | |
| 59 | $config = CRM_Core_Config::singleton(); |
| 60 | $c = self::getComponents(); |
| 61 | |
| 62 | foreach ($c as $name => $comp) { |
| 63 | if (in_array($name, $config->enableComponents)) { |
| 64 | self::$_info[$name] = $comp; |
| 65 | } |
| 66 | } |
| 67 | } |
| 68 | |
| 69 | return self::$_info; |
| 70 | } |
| 71 | |
| 72 | /** |
| 73 | * @param string $name |
| 74 | * @param null $attribute |
| 75 | * |
| 76 | * @return mixed |
| 77 | */ |
| 78 | public static function get($name, $attribute = NULL) { |
| 79 | $comp = CRM_Utils_Array::value($name, self::_info()); |
| 80 | if ($attribute) { |
| 81 | return CRM_Utils_Array::value($attribute, $comp->info); |
| 82 | } |
| 83 | return $comp; |
| 84 | } |
| 85 | |
| 86 | /** |
| 87 | * @param bool $force |
| 88 | * |
| 89 | * @return array |
| 90 | * @throws Exception |
| 91 | */ |
| 92 | public static function &getComponents($force = FALSE) { |
| 93 | static $_cache = NULL; |
| 94 | |
| 95 | if (!$_cache || $force) { |
| 96 | $_cache = array(); |
| 97 | |
| 98 | $cr = new CRM_Core_DAO_Component(); |
| 99 | $cr->find(FALSE); |
| 100 | while ($cr->fetch()) { |
| 101 | $infoClass = $cr->namespace . '_' . self::COMPONENT_INFO_CLASS; |
| 102 | require_once str_replace('_', DIRECTORY_SEPARATOR, $infoClass) . '.php'; |
| 103 | $infoObject = new $infoClass($cr->name, $cr->namespace, $cr->id); |
| 104 | if ($infoObject->info['name'] !== $cr->name) { |
| 105 | CRM_Core_Error::fatal("There is a discrepancy between name in component registry and in info file ({$cr->name})."); |
| 106 | } |
| 107 | $_cache[$cr->name] = $infoObject; |
| 108 | unset($infoObject); |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | return $_cache; |
| 113 | } |
| 114 | |
| 115 | /** |
| 116 | * @return array |
| 117 | * Array(string $name => int $id). |
| 118 | */ |
| 119 | public static function &getComponentIDs() { |
| 120 | $componentIDs = array(); |
| 121 | |
| 122 | $cr = new CRM_Core_DAO_Component(); |
| 123 | $cr->find(FALSE); |
| 124 | while ($cr->fetch()) { |
| 125 | $componentIDs[$cr->name] = $cr->id; |
| 126 | } |
| 127 | |
| 128 | return $componentIDs; |
| 129 | } |
| 130 | |
| 131 | |
| 132 | /** |
| 133 | * @param bool $force |
| 134 | * |
| 135 | * @return array|null |
| 136 | */ |
| 137 | static public function &getEnabledComponents($force = FALSE) { |
| 138 | return self::_info($force); |
| 139 | } |
| 140 | |
| 141 | static public function flushEnabledComponents() { |
| 142 | self::getEnabledComponents(TRUE); |
| 143 | } |
| 144 | |
| 145 | /** |
| 146 | * @param bool $translated |
| 147 | * |
| 148 | * @return array |
| 149 | */ |
| 150 | public static function &getNames($translated = FALSE) { |
| 151 | $allComponents = self::getComponents(); |
| 152 | |
| 153 | $names = array(); |
| 154 | foreach ($allComponents as $name => $comp) { |
| 155 | if ($translated) { |
| 156 | $names[$comp->componentID] = $comp->info['translatedName']; |
| 157 | } |
| 158 | else { |
| 159 | $names[$comp->componentID] = $name; |
| 160 | } |
| 161 | } |
| 162 | return $names; |
| 163 | } |
| 164 | |
| 165 | /** |
| 166 | * @param $args |
| 167 | * @param $type |
| 168 | * |
| 169 | * @return bool |
| 170 | */ |
| 171 | public static function invoke(&$args, $type) { |
| 172 | $info = self::_info(); |
| 173 | $config = CRM_Core_Config::singleton(); |
| 174 | |
| 175 | $firstArg = CRM_Utils_Array::value(1, $args, ''); |
| 176 | $secondArg = CRM_Utils_Array::value(2, $args, ''); |
| 177 | foreach ($info as $name => $comp) { |
| 178 | if (in_array($name, $config->enableComponents) && |
| 179 | (($comp->info['url'] === $firstArg && $type == 'main') || |
| 180 | ($comp->info['url'] === $secondArg && $type == 'admin') |
| 181 | ) |
| 182 | ) { |
| 183 | if ($type == 'main') { |
| 184 | // also set the smarty variables to the current component |
| 185 | $template = CRM_Core_Smarty::singleton(); |
| 186 | $template->assign('activeComponent', $name); |
| 187 | if (!empty($comp->info[$name]['formTpl'])) { |
| 188 | $template->assign('formTpl', $comp->info[$name]['formTpl']); |
| 189 | } |
| 190 | if (!empty($comp->info[$name]['css'])) { |
| 191 | $styleSheets = '<style type="text/css">@import url(' . "{$config->resourceBase}css/{$comp->info[$name]['css']});</style>"; |
| 192 | CRM_Utils_System::addHTMLHead($styleSheet); |
| 193 | } |
| 194 | } |
| 195 | $inv = $comp->getInvokeObject(); |
| 196 | $inv->$type($args); |
| 197 | return TRUE; |
| 198 | } |
| 199 | } |
| 200 | return FALSE; |
| 201 | } |
| 202 | |
| 203 | /** |
| 204 | * @return array |
| 205 | */ |
| 206 | public static function xmlMenu() { |
| 207 | |
| 208 | // lets build the menu for all components |
| 209 | $info = self::getComponents(TRUE); |
| 210 | |
| 211 | $files = array(); |
| 212 | foreach ($info as $name => $comp) { |
| 213 | $files = array_merge($files, |
| 214 | $comp->menuFiles() |
| 215 | ); |
| 216 | } |
| 217 | |
| 218 | return $files; |
| 219 | } |
| 220 | |
| 221 | /** |
| 222 | * @return array |
| 223 | */ |
| 224 | public static function &menu() { |
| 225 | $info = self::_info(); |
| 226 | $items = array(); |
| 227 | foreach ($info as $name => $comp) { |
| 228 | $mnu = $comp->getMenuObject(); |
| 229 | |
| 230 | $ret = $mnu->permissioned(); |
| 231 | $items = array_merge($items, $ret); |
| 232 | |
| 233 | $ret = $mnu->main($task); |
| 234 | $items = array_merge($items, $ret); |
| 235 | } |
| 236 | return $items; |
| 237 | } |
| 238 | |
| 239 | /** |
| 240 | * @param $config |
| 241 | * @param bool $oldMode |
| 242 | * |
| 243 | * @return null |
| 244 | */ |
| 245 | public static function addConfig(&$config, $oldMode = FALSE) { |
| 246 | $info = self::_info(); |
| 247 | |
| 248 | foreach ($info as $name => $comp) { |
| 249 | $cfg = $comp->getConfigObject(); |
| 250 | $cfg->add($config, $oldMode); |
| 251 | } |
| 252 | return NULL; |
| 253 | } |
| 254 | |
| 255 | /** |
| 256 | * @param string $componentName |
| 257 | * |
| 258 | * @return mixed |
| 259 | */ |
| 260 | public static function getComponentID($componentName) { |
| 261 | $info = self::_info(); |
| 262 | if (!empty($info[$componentName])) { |
| 263 | return $info[$componentName]->componentID; |
| 264 | } |
| 265 | else { |
| 266 | return; |
| 267 | } |
| 268 | } |
| 269 | |
| 270 | /** |
| 271 | * @param int $componentID |
| 272 | * |
| 273 | * @return int|null|string |
| 274 | */ |
| 275 | public static function getComponentName($componentID) { |
| 276 | $info = self::_info(); |
| 277 | |
| 278 | $componentName = NULL; |
| 279 | foreach ($info as $compName => $component) { |
| 280 | if ($component->componentID == $componentID) { |
| 281 | $componentName = $compName; |
| 282 | break; |
| 283 | } |
| 284 | } |
| 285 | |
| 286 | return $componentName; |
| 287 | } |
| 288 | |
| 289 | /** |
| 290 | * @return array |
| 291 | */ |
| 292 | public static function &getQueryFields() { |
| 293 | $info = self::_info(); |
| 294 | $fields = array(); |
| 295 | foreach ($info as $name => $comp) { |
| 296 | if ($comp->usesSearch()) { |
| 297 | $bqr = $comp->getBAOQueryObject(); |
| 298 | $flds = $bqr->getFields(); |
| 299 | $fields = array_merge($fields, $flds); |
| 300 | } |
| 301 | } |
| 302 | return $fields; |
| 303 | } |
| 304 | |
| 305 | /** |
| 306 | * @param $query |
| 307 | * @param string $fnName |
| 308 | */ |
| 309 | public static function alterQuery(&$query, $fnName) { |
| 310 | $info = self::_info(); |
| 311 | |
| 312 | foreach ($info as $name => $comp) { |
| 313 | if ($comp->usesSearch()) { |
| 314 | $bqr = $comp->getBAOQueryObject(); |
| 315 | $bqr->$fnName($query); |
| 316 | } |
| 317 | } |
| 318 | } |
| 319 | |
| 320 | /** |
| 321 | * @param string $fieldName |
| 322 | * @param $mode |
| 323 | * @param $side |
| 324 | * |
| 325 | * @return null |
| 326 | */ |
| 327 | public static function from($fieldName, $mode, $side) { |
| 328 | $info = self::_info(); |
| 329 | |
| 330 | $from = NULL; |
| 331 | foreach ($info as $name => $comp) { |
| 332 | if ($comp->usesSearch()) { |
| 333 | $bqr = $comp->getBAOQueryObject(); |
| 334 | $from = $bqr->from($fieldName, $mode, $side); |
| 335 | if ($from) { |
| 336 | return $from; |
| 337 | } |
| 338 | } |
| 339 | } |
| 340 | return $from; |
| 341 | } |
| 342 | |
| 343 | /** |
| 344 | * @param $mode |
| 345 | * @param bool $includeCustomFields |
| 346 | * |
| 347 | * @return null |
| 348 | */ |
| 349 | public static function &defaultReturnProperties( |
| 350 | $mode, |
| 351 | $includeCustomFields = TRUE |
| 352 | ) { |
| 353 | $info = self::_info(); |
| 354 | |
| 355 | $properties = NULL; |
| 356 | foreach ($info as $name => $comp) { |
| 357 | if ($comp->usesSearch()) { |
| 358 | $bqr = $comp->getBAOQueryObject(); |
| 359 | $properties = $bqr->defaultReturnProperties($mode, $includeCustomFields); |
| 360 | if ($properties) { |
| 361 | return $properties; |
| 362 | } |
| 363 | } |
| 364 | } |
| 365 | return $properties; |
| 366 | } |
| 367 | |
| 368 | /** |
| 369 | * @param CRM_Core_Form $form |
| 370 | */ |
| 371 | public static function &buildSearchForm(&$form) { |
| 372 | $info = self::_info(); |
| 373 | |
| 374 | foreach ($info as $name => $comp) { |
| 375 | if ($comp->usesSearch()) { |
| 376 | $bqr = $comp->getBAOQueryObject(); |
| 377 | $bqr->buildSearchForm($form); |
| 378 | } |
| 379 | } |
| 380 | } |
| 381 | |
| 382 | /** |
| 383 | * @param $row |
| 384 | * @param int $id |
| 385 | */ |
| 386 | public static function searchAction(&$row, $id) { |
| 387 | $info = self::_info(); |
| 388 | |
| 389 | foreach ($info as $name => $comp) { |
| 390 | if ($comp->usesSearch()) { |
| 391 | $bqr = $comp->getBAOQueryObject(); |
| 392 | $bqr->searchAction($row, $id); |
| 393 | } |
| 394 | } |
| 395 | } |
| 396 | |
| 397 | /** |
| 398 | * @return array|null |
| 399 | */ |
| 400 | public static function &contactSubTypes() { |
| 401 | if (self::$_contactSubTypes == NULL) { |
| 402 | self::$_contactSubTypes = array(); |
| 403 | } |
| 404 | return self::$_contactSubTypes; |
| 405 | } |
| 406 | |
| 407 | |
| 408 | /** |
| 409 | * @param $subType |
| 410 | * @param $op |
| 411 | * |
| 412 | * @return null |
| 413 | */ |
| 414 | public static function &contactSubTypeProperties($subType, $op) { |
| 415 | $properties = self::contactSubTypes(); |
| 416 | if (array_key_exists($subType, $properties) && |
| 417 | array_key_exists($op, $properties[$subType]) |
| 418 | ) { |
| 419 | return $properties[$subType][$op]; |
| 420 | } |
| 421 | return CRM_Core_DAO::$_nullObject; |
| 422 | } |
| 423 | |
| 424 | /** |
| 425 | * FIXME: This function does not appear to do anything. The is_array() check runs on a bunch of objects and (always?) returns false |
| 426 | */ |
| 427 | public static function &taskList() { |
| 428 | $info = self::_info(); |
| 429 | |
| 430 | $tasks = array(); |
| 431 | foreach ($info as $name => $value) { |
| 432 | if (is_array($info[$name]) && isset($info[$name]['task'])) { |
| 433 | $tasks += $info[$name]['task']; |
| 434 | } |
| 435 | } |
| 436 | return $tasks; |
| 437 | } |
| 438 | |
| 439 | /** |
| 440 | * Handle table dependencies of components. |
| 441 | * |
| 442 | * @param array $tables |
| 443 | * Array of tables. |
| 444 | * |
| 445 | */ |
| 446 | public static function tableNames(&$tables) { |
| 447 | $info = self::_info(); |
| 448 | |
| 449 | foreach ($info as $name => $comp) { |
| 450 | if ($comp->usesSearch()) { |
| 451 | $bqr = $comp->getBAOQueryObject(); |
| 452 | $bqr->tableNames($tables); |
| 453 | } |
| 454 | } |
| 455 | } |
| 456 | |
| 457 | /** |
| 458 | * Get components info from info file. |
| 459 | */ |
| 460 | public static function getComponentsFromFile($crmFolderDir) { |
| 461 | $components = array(); |
| 462 | //traverse CRM folder and check for Info file |
| 463 | if (is_dir($crmFolderDir) && $dir = opendir($crmFolderDir)) { |
| 464 | while ($subDir = readdir($dir)) { |
| 465 | // skip the extensions diretory since it has an Info.php file also |
| 466 | if ($subDir == 'Extension') { |
| 467 | continue; |
| 468 | } |
| 469 | |
| 470 | $infoFile = $crmFolderDir . "/{$subDir}/" . self::COMPONENT_INFO_CLASS . '.php'; |
| 471 | if (file_exists($infoFile)) { |
| 472 | $infoClass = 'CRM_' . $subDir . '_' . self::COMPONENT_INFO_CLASS; |
| 473 | require_once str_replace('_', DIRECTORY_SEPARATOR, $infoClass) . '.php'; |
| 474 | $infoObject = new $infoClass(NULL, NULL, NULL); |
| 475 | $components[$infoObject->info['name']] = $infoObject; |
| 476 | unset($infoObject); |
| 477 | } |
| 478 | } |
| 479 | } |
| 480 | |
| 481 | return $components; |
| 482 | } |
| 483 | |
| 484 | } |