| [ Index ] |
PHP Cross Reference of Drupal 6 (gatewave) |
[Summary view] [Print] [Text view]
1 <?php 2 // $Id: ctools.module,v 1.27.2.50 2010/10/15 22:11:09 merlinofchaos Exp $ 3 4 /** 5 * @file 6 * CTools primary module file. 7 * 8 * Most of the CTools tools are in their own .inc files. This contains 9 * nothing more than a few convenience functions and some hooks that 10 * must be implemented in the module file. 11 */ 12 13 define('CTOOLS_API_VERSION', '1.8'); 14 15 /** 16 * Test the CTools API version. 17 * 18 * This function can always be used to safely test if CTools has the minimum 19 * API version that your module can use. It can also try to protect you from 20 * running if the CTools API version is too new, but if you do that you need 21 * to be very quick about watching CTools API releases and release new versions 22 * of your software as soon as the new release is made, or people might end up 23 * updating CTools and having your module shut down without any recourse. 24 * 25 * It is recommended that every hook of your module that might use CTools or 26 * might lead to a use of CTools be guarded like this: 27 * 28 * @code 29 * if (!module_invoke('ctools', 'api_version', '1.0')) { 30 * return; 31 * } 32 * @endcode 33 * 34 * Note that some hooks such as _menu() or _theme() must return an array(). 35 * 36 * You can use it in your hook_requirements to report this error condition 37 * like this: 38 * 39 * @code 40 * define('MODULENAME_MINIMUM_CTOOLS_API_VERSION', '1.0'); 41 * define('MODULENAME_MAXIMUM_CTOOLS_API_VERSION', '1.1'); 42 * 43 * function MODULENAME_requirements($phase) { 44 * $requirements = array(); 45 * if (!module_invoke('ctools', 'api_version', MODULENAME_MINIMUM_CTOOLS_API_VERSION, MODULENAME_MAXIMUM_CTOOLS_API_VERSION)) { 46 * $requirements['MODULENAME_ctools'] = array( 47 * 'title' => $t('MODULENAME required Chaos Tool Suite (CTools) API Version'), 48 * 'value' => t('Between @a and @b', array('@a' => MODULENAME_MINIMUM_CTOOLS_API_VERSION, '@b' => MODULENAME_MAXIMUM_CTOOLS_API_VERSION)), 49 * 'severity' => REQUIREMENT_ERROR, 50 * ); 51 * } 52 * return $requirements; 53 * } 54 * @endcode 55 * 56 * Please note that the version is a string, not an floating point number. 57 * This will matter once CTools reaches version 1.10. 58 * 59 * A CTools API changes history will be kept in API.txt. Not every new 60 * version of CTools will necessarily update the API version. 61 * @param $minimum 62 * The minimum version of CTools necessary for your software to run with it. 63 * @param $maximum 64 * The maximum version of CTools allowed for your software to run with it. 65 */ 66 function ctools_api_version($minimum, $maximum = NULL) { 67 if (version_compare(CTOOLS_API_VERSION, $minimum, '<')) { 68 return FALSE; 69 } 70 71 if (isset($maximum) && version_compare(CTOOLS_API_VERSION, $maximum, '>')) { 72 return FALSE; 73 } 74 75 return TRUE; 76 } 77 78 // ----------------------------------------------------------------------- 79 // General utility functions 80 81 /** 82 * Include .inc files as necessary. 83 * 84 * This fuction is helpful for including .inc files for your module. The 85 * general case is including ctools funcitonality like this: 86 * 87 * @code 88 * ctools_include('plugins'); 89 * @endcode 90 * 91 * Similar funcitonality can be used for other modules by providing the $module 92 * and $dir arguments like this: 93 * 94 * @code 95 * // include mymodule/includes/import.inc 96 * ctools_include('import', 'mymodule'); 97 * // include mymodule/plugins/foobar.inc 98 * ctools_include('foobar', 'mymodule', 'plugins'); 99 * @endcode 100 * 101 * @param $file 102 * The base file name to be included. 103 * @param $module 104 * Optional module containing the include. 105 * @param $dir 106 * Optional subdirectory containing the include file. 107 */ 108 function ctools_include($file, $module = 'ctools', $dir = 'includes') { 109 static $used = array(); 110 111 $dir = '/' . ($dir ? $dir . '/' : ''); 112 113 if (!isset($used[$module][$dir][$file])) { 114 require_once './' . drupal_get_path('module', $module) . "$dir$file.inc"; 115 $used[$module][$dir][$file] = true; 116 } 117 } 118 119 /** 120 * Provide the proper path to an image as necessary. 121 * 122 * This helper function is used by ctools but can also be used in other 123 * modules in the same way as explained in the comments of ctools_include. 124 * 125 * @param $image 126 * The base file name (with extension) of the image to be included. 127 * @param $module 128 * Optional module containing the include. 129 * @param $dir 130 * Optional subdirectory containing the include file. 131 */ 132 function ctools_image_path($image, $module = 'ctools', $dir = 'images') { 133 return drupal_get_path('module', $module) . "/$dir/" . $image; 134 } 135 136 /** 137 * Include css files as necessary. 138 * 139 * This helper function is used by ctools but can also be used in other 140 * modules in the same way as explained in the comments of ctools_include. 141 * 142 * @param $file 143 * The base file name to be included. 144 * @param $module 145 * Optional module containing the include. 146 * @param $dir 147 * Optional subdirectory containing the include file. 148 */ 149 function ctools_add_css($file, $module = 'ctools', $dir = 'css') { 150 drupal_add_css(drupal_get_path('module', $module) . "/$dir/$file.css"); 151 } 152 153 /** 154 * Include js files as necessary. 155 * 156 * This helper function is used by ctools but can also be used in other 157 * modules in the same way as explained in the comments of ctools_include. 158 * 159 * @param $file 160 * The base file name to be included. 161 * @param $module 162 * Optional module containing the include. 163 * @param $dir 164 * Optional subdirectory containing the include file. 165 */ 166 function ctools_add_js($file, $module = 'ctools', $dir = 'js') { 167 drupal_add_js(drupal_get_path('module', $module) . "/$dir/$file.js"); 168 } 169 170 /** 171 * Central static variable storage. Modeled after Drupal 7's drupal_static(). 172 * 173 * @param $name 174 * Globally unique name for the variable. For a function with only one static, 175 * variable, the function name (e.g. via the PHP magic __FUNCTION__ constant) 176 * is recommended. For a function with multiple static variables add a 177 * distinguishing suffix to the function name for each one. 178 * @param $default_value 179 * Optional default value. 180 * @param $reset 181 * TRUE to reset a specific named variable, or all variables if $name is NULL. 182 * Resetting every variable should only be used, for example, for running unit 183 * tests with a clean environment. Should be used only though via function 184 * ctools_static_reset(). 185 */ 186 function &ctools_static($name, $default_value = NULL, $reset = FALSE) { 187 static $data = array(); 188 189 // Reset a single value, or all values. 190 if ($reset) { 191 if (isset($name)) { 192 unset($data[$name]); 193 } 194 else { 195 $data = array(); 196 } 197 // We must return a reference to a variable. 198 $dummy = NULL; 199 return $dummy; 200 } 201 202 if (!isset($data[$name])) { 203 $data[$name] = $default_value; 204 } 205 206 return $data[$name]; 207 } 208 209 /** 210 * Reset one or all centrally stored static variable(s). 211 * Modeled after Drupal 7's drupal_static_reset(). 212 * 213 * @param $name 214 * Name of the static variable to reset. Omit to reset all variables. 215 */ 216 function ctools_static_reset($name) { 217 ctools_static($name, NULL, TRUE); 218 } 219 220 /** 221 * Get a list of roles in the system. 222 * 223 * @return 224 * An array of role names keyed by role ID. 225 */ 226 function ctools_get_roles() { 227 static $roles = NULL; 228 if (!isset($roles)) { 229 $roles = array(); 230 $result = db_query("SELECT r.rid, r.name FROM {role} r ORDER BY r.name"); 231 while ($obj = db_fetch_object($result)) { 232 $roles[$obj->rid] = $obj->name; 233 } 234 } 235 236 return $roles; 237 } 238 239 /* 240 * Break x,y,z and x+y+z into an array. Numeric only. 241 * 242 * @param $str 243 * The string to parse. 244 * 245 * @return $object 246 * An object containing 247 * - operator: Either 'and' or 'or' 248 * - value: An array of numeric values. 249 */ 250 function ctools_break_phrase($str) { 251 $object = new stdClass(); 252 253 if (preg_match('/^([0-9]+[+ ])+[0-9]+$/', $str)) { 254 // The '+' character in a query string may be parsed as ' '. 255 $object->operator = 'or'; 256 $object->value = preg_split('/[+ ]/', $str); 257 } 258 else if (preg_match('/^([0-9]+,)*[0-9]+$/', $str)) { 259 $object->operator = 'and'; 260 $object->value = explode(',', $str); 261 } 262 263 // Keep an 'error' value if invalid strings were given. 264 if (!empty($str) && (empty($object->value) || !is_array($object->value))) { 265 $object->value = array(-1); 266 $object->invalid_input = TRUE; 267 return $object; 268 } 269 270 if (empty($object->value)) { 271 $object->value = array(); 272 } 273 274 // Doubly ensure that all values are numeric only. 275 foreach ($object->value as $id => $value) { 276 $object->value[$id] = intval($value); 277 } 278 279 return $object; 280 } 281 282 /** 283 * Set a token/value pair to be replaced later in the request, specifically in 284 * ctools_preprocess_page(). 285 * 286 * @param $token 287 * The token to be replaced later, during page rendering. This should 288 * ideally be a string inside of an HTML comment, so that if there is 289 * no replacement, the token will not render on the page. 290 * @param $type 291 * The type of the token. Can be either 'variable', which will pull data 292 * directly from the page variables 293 * @param $argument 294 * If $type == 'variable' then argument should be the key to fetch from 295 * the $variables. If $type == 'callback' then it should either be the 296 * callback, or an array that will be sent to call_user_func_array(). 297 * 298 * @return 299 * A array of token/variable names to be replaced. 300 */ 301 function ctools_set_page_token($token = NULL, $type = NULL, $argument = NULL) { 302 static $tokens = array(); 303 304 if (isset($token)) { 305 $tokens[$token] = array($type, $argument); 306 } 307 308 return $tokens; 309 } 310 311 /** 312 * Easily set a token from the page variables. 313 * 314 * This function can be used like this: 315 * $token = ctools_set_variable_token('tabs'); 316 * 317 * $token will then be a simple replacement for the 'tabs' about of the 318 * variables available in the page template. 319 */ 320 function ctools_set_variable_token($token) { 321 $string = '<!-- ctools-page-' . $token . ' -->'; 322 ctools_set_page_token($string, 'variable', $token); 323 return $string; 324 } 325 326 /** 327 * Easily set a token from the page variables. 328 * 329 * This function can be used like this: 330 * $token = ctools_set_variable_token('id', 'mymodule_myfunction'); 331 */ 332 function ctools_set_callback_token($token, $callback) { 333 $string = '<!-- ctools-page-' . $token . ' -->'; 334 ctools_set_page_token($string, 'callback', $callback); 335 return $string; 336 } 337 338 // ----------------------------------------------------------------------- 339 // Drupal core hooks 340 341 /** 342 * Implement hook_init to keep our global CSS at the ready. 343 */ 344 function ctools_init() { 345 ctools_add_css('ctools'); 346 // If we are sure that CTools' AJAX is in use, change the error handling. 347 if (!empty($_REQUEST['ctools_ajax'])) { 348 ini_set('display_errors', 0); 349 register_shutdown_function('ctools_shutdown_handler'); 350 } 351 352 // Clear plugin cache on the module page submit. 353 if ($_GET['q'] == 'admin/build/modules/list/confirm' && !empty($_POST)) { 354 cache_clear_all('ctools_plugin_files:', 'cache', TRUE); 355 } 356 } 357 358 /** 359 * Shutdown handler used during ajax operations to help catch fatal errors. 360 */ 361 function ctools_shutdown_handler() { 362 if (function_exists('error_get_last') AND ($error = error_get_last())){ 363 switch($error['type']){ 364 case E_ERROR: 365 case E_CORE_ERROR: 366 case E_COMPILE_ERROR: 367 case E_USER_ERROR: 368 // Do this manually because including files here is dangerous. 369 $commands = array( 370 array( 371 'command' => 'alert', 372 'title' => t('Error'), 373 'text' => t('Unable to complete operation. Fatal error in @file on line @line: @message', array( 374 '@file' => $error['file'], 375 '@line' => $error['line'], 376 '@message' => $error['message'], 377 )), 378 ), 379 ); 380 381 // Change the status code so that the client will read the AJAX returned. 382 header('HTTP/1.1 200 OK'); 383 drupal_json($commands); 384 } 385 } 386 } 387 388 /** 389 * Implementation of hook_theme(). 390 */ 391 function ctools_theme() { 392 ctools_include('utility'); 393 $items = array(); 394 _ctools_passthrough($items, 'theme'); 395 return $items; 396 } 397 398 /** 399 * Implementation of hook_menu(). 400 */ 401 function ctools_menu() { 402 ctools_include('utility'); 403 $items = array(); 404 _ctools_passthrough($items, 'menu'); 405 return $items; 406 } 407 408 /** 409 * Implementation of hook_cron. Clean up old caches. 410 */ 411 function ctools_cron() { 412 ctools_include('utility'); 413 _ctools_passthrough($items, 'cron'); 414 } 415 416 /** 417 * Ensure the CTools CSS cache is flushed whenever hook_flush_caches is invoked. 418 */ 419 function ctools_flush_caches() { 420 // Do not actually flush caches if running on cron. Drupal uses this hook 421 // in an inconsistent fashion and it does not necessarily mean to *flush* 422 // caches when running from cron. Instead it's just getting a list of cache 423 // tables and may not do any flushing. 424 if (variable_get('cron_semaphore', FALSE)) { 425 return; 426 } 427 428 ctools_include('css'); 429 ctools_css_flush_caches(); 430 } 431 432 /** 433 * Provide a search form with a different id so that form_alters will miss it 434 * and thus not get advanced search settings. 435 */ 436 function ctools_forms() { 437 $forms['ctools_search_form']= array( 438 'callback' => 'search_form', 439 ); 440 441 return $forms; 442 } 443 444 /** 445 * Implementation of hook_file_download() 446 * 447 * When using the private file system, we have to let Drupal know it's ok to 448 * download CSS and image files from our temporary directory. 449 */ 450 function ctools_file_download($filepath) { 451 if (strpos($filepath, 'ctools') === 0) { 452 $mime = file_get_mimetype($filepath); 453 // For safety's sake, we allow only text and images. 454 if (strpos($mime, 'text') === 0 || strpos($mime, 'image') === 0) { 455 return array('Content-type:' . $mime); 456 } 457 } 458 } 459 460 // ----------------------------------------------------------------------- 461 // CTools hook implementations. 462 463 /** 464 * Implementation of hook_ctools_plugin_directory() to let the system know 465 * where all our own plugins are. 466 */ 467 function ctools_ctools_plugin_directory($owner, $plugin_type) { 468 if ($owner == 'ctools') { 469 return 'plugins/' . $plugin_type; 470 } 471 } 472 473 /** 474 * Implementation of hook_js_replacements(). 475 * This is a hook that is not a standard yet. We hope jquery_update and others 476 * will expose this hook to inform modules which scripts they are modifying 477 * in the theme layer. 478 * The return format is $scripts[$type][$old_path] = $new_path. 479 */ 480 function ctools_js_replacements() { 481 $replacements = array(); 482 // Until jquery_update is released with its own replacement hook, we will 483 // take those replacements into account here. 484 if (module_exists('jquery_update')) { 485 $replacements = array_merge_recursive($replacements, jquery_update_get_replacements()); 486 foreach ($replacements as $type => $type_replacements) { 487 foreach ($type_replacements as $old_path => $new_filename) { 488 $replacements[$type][$old_path] = JQUERY_UPDATE_REPLACE_PATH . "/$new_filename"; 489 } 490 } 491 $replacements['core']['misc/jquery.js'] = jquery_update_jquery_path(); 492 } 493 return $replacements; 494 } 495 496 /** 497 * Inform CTools that the layout plugin can be loaded from themes. 498 */ 499 function ctools_ctools_plugin_access() { 500 return array( 501 'child plugins' => TRUE, 502 ); 503 } 504 505 // ----------------------------------------------------------------------- 506 // Drupal theme preprocess hooks that must be in the .module file. 507 508 /** 509 * Override or insert PHPTemplate variables into the templates. 510 * 511 * This needs to be in the .module file to ensure availability; we can't change the 512 * paths or it won't be able to find templates. 513 */ 514 function ctools_garland_preprocess_page(&$vars) { 515 $vars['tabs2'] = ctools_menu_secondary_local_tasks(); 516 517 // Hook into color.module 518 if (module_exists('color')) { 519 _color_page_alter($vars); 520 } 521 } 522 523 /** 524 * A theme preprocess function to automatically allow panels-based node 525 * templates based upon input when the panel was configured. 526 */ 527 function ctools_preprocess_node(&$vars) { 528 // The 'panel_identifier' attribute of the node is added when the pane is 529 // rendered. 530 if (!empty($vars['node']->panel_identifier)) { 531 $vars['panel_identifier'] = check_plain($vars['node']->panel_identifier); 532 $vars['template_files'][] = 'node-panel-' . check_plain($vars['node']->panel_identifier); 533 } 534 } 535 536 /** 537 * A theme preprocess function to allow content type plugins to use page 538 * template variables which are not yet available when the content type is 539 * rendered. 540 */ 541 function ctools_preprocess_page(&$variables) { 542 $tokens = ctools_set_page_token(); 543 if (!empty($tokens)) { 544 foreach ($tokens as $token => $key) { 545 list($type, $argument) = $key; 546 switch ($type) { 547 case 'variable': 548 $tokens[$token] = isset($variables[$argument]) ? $variables[$argument] : ''; 549 break; 550 case 'callback': 551 if (is_string($argument) && function_exists($argument)) { 552 $tokens[$token] = $argument($variables); 553 } 554 if (is_array($argument) && function_exists($argument[0])) { 555 $function = array_shift($argument); 556 $argument = array_merge(array(&$variables), $argument); 557 $tokens[$token] = call_user_func_array($function, $argument); 558 } 559 break; 560 } 561 } 562 $variables['content'] = strtr($variables['content'], $tokens); 563 } 564 565 if (defined('CTOOLS_AJAX_INCLUDED')) { 566 ctools_ajax_page_preprocess($variables); 567 } 568 } 569 570 // ----------------------------------------------------------------------- 571 // Menu callbacks that must be in the .module file. 572 573 /** 574 * Determine if the current user has access via a plugin. 575 * 576 * This function is meant to be embedded in the Drupal menu system, and 577 * therefore is in the .module file since sub files can't be loaded, and 578 * takes arguments a little bit more haphazardly than ctools_access(). 579 * 580 * @param $access 581 * An access control array which contains the following information: 582 * - 'logic': and or or. Whether all tests must pass or one must pass. 583 * - 'plugins': An array of access plugins. Each contains: 584 * - - 'name': The name of the plugin 585 * - - 'settings': The settings from the plugin UI. 586 * - - 'context': Which context to use. 587 * @param ... 588 * zero or more context arguments generated from argument plugins. These 589 * contexts must have an 'id' attached to them so that they can be 590 * properly associated. The argument plugin system should set this, but 591 * if the context is coming from elsewhere it will need to be set manually. 592 * 593 * @return 594 * TRUE if access is granted, false if otherwise. 595 */ 596 function ctools_access_menu($access) { 597 // Short circuit everything if there are no access tests. 598 if (empty($access['plugins'])) { 599 return TRUE; 600 } 601 602 $contexts = array(); 603 foreach (func_get_args() as $arg) { 604 if (is_object($arg) && get_class($arg) == 'ctools_context') { 605 $contexts[$arg->id] = $arg; 606 } 607 } 608 609 ctools_include('context'); 610 return ctools_access($access, $contexts); 611 } 612 613 /** 614 * Determine if the current user has access via checks to multiple different 615 * permissions. 616 * 617 * This function is a thin wrapper around user_access that allows multiple 618 * permissions to be easily designated for use on, for example, a menu callback. 619 * 620 * @param ... 621 * An indexed array of zero or more permission strings to be checked by 622 * user_access(). 623 * 624 * @return 625 * Iff all checks pass will this function return TRUE. If an invalid argument 626 * is passed (e.g., not a string), this function errs on the safe said and 627 * returns FALSE. 628 */ 629 function ctools_access_multiperm() { 630 foreach (func_get_args() as $arg) { 631 if (!is_string($arg) || !user_access($arg)) { 632 return FALSE; 633 } 634 } 635 return TRUE; 636 } 637 638 /** 639 * Check to see if the incoming menu item is js capable or not. 640 * 641 * This can be used as %ctools_js as part of a path in hook menu. CTools 642 * ajax functions will automatically change the phrase 'nojs' to 'ajax' 643 * when it attaches ajax to a link. This can be used to autodetect if 644 * that happened. 645 */ 646 function ctools_js_load($js) { 647 if ($js == 'ajax') { 648 return TRUE; 649 } 650 return 0; 651 } 652 653 /** 654 * Menu _load hook. 655 * 656 * This function will be called to load an object as a replacement for 657 * %ctools_export_ui in menu paths. 658 */ 659 function ctools_export_ui_load($item_name, $plugin_name) { 660 $return = &ctools_static(__FUNCTION__, FALSE); 661 662 if (!$return) { 663 ctools_include('export-ui'); 664 $plugin = ctools_get_export_ui($plugin_name); 665 666 if ($plugin) { 667 // Get the load callback. 668 $item = ctools_export_crud_load($plugin['schema'], $item_name); 669 return empty($item) ? FALSE : $item; 670 } 671 } 672 673 return $return; 674 } 675 676 // ----------------------------------------------------------------------- 677 // Caching callbacks on behalf of export-ui. 678 679 /** 680 * Menu access callback for various tasks of export-ui. 681 */ 682 function ctools_export_ui_task_access($plugin_name, $op, $item = NULL) { 683 ctools_include('export-ui'); 684 $plugin = ctools_get_export_ui($plugin_name); 685 $handler = ctools_export_ui_get_handler($plugin); 686 687 if ($handler) { 688 return $handler->access($op, $item); 689 } 690 691 // Deny access if the handler cannot be found. 692 return FALSE; 693 } 694 695 /** 696 * Cache callback on behalf of ctools_export_ui. 697 */ 698 function ctools_export_ui_context_cache_get($plugin_name, $key) { 699 ctools_include('export-ui'); 700 $plugin = ctools_get_export_ui($plugin_name); 701 $handler = ctools_export_ui_get_handler($plugin); 702 if ($handler) { 703 $item = $handler->edit_cache_get($key); 704 if (!$item) { 705 $item = ctools_export_crud_load($handler->plugin['schema'], $key); 706 } 707 return $item; 708 } 709 } 710 711 /** 712 * Cache callback on behalf of ctools_export_ui. 713 */ 714 function ctools_export_ui_context_cache_set($plugin_name, $key, $item) { 715 ctools_include('export-ui'); 716 $plugin = ctools_get_export_ui($plugin_name); 717 $handler = ctools_export_ui_get_handler($plugin); 718 if ($handler) { 719 return $handler->edit_cache_set_key($item, $key); 720 } 721 } 722 723 /** 724 * Callback for access control ajax form on behalf of export ui. 725 * 726 * Returns the cached access config and contexts used. 727 * Note that this is assuming that access will be in $item->access -- if it 728 * is not, an export UI plugin will have to make its own callbacks. 729 */ 730 function ctools_export_ui_ctools_access_get($argument) { 731 ctools_include('export-ui'); 732 list($plugin_name, $key) = explode(':', $argument); 733 734 $plugin = ctools_get_export_ui($plugin_name); 735 $handler = ctools_export_ui_get_handler($plugin); 736 737 if ($handler) { 738 ctools_include('context'); 739 $item = $handler->edit_cache_get($key); 740 if (!$item) { 741 $item = ctools_export_crud_load($handler->plugin['schema'], $key); 742 } 743 744 $contexts = ctools_context_load_contexts($item); 745 return array($item->access, $contexts); 746 } 747 } 748 749 /** 750 * Callback for access control ajax form on behalf of export ui 751 * 752 * Returns the cached access config and contexts used. 753 * Note that this is assuming that access will be in $item->access -- if it 754 * is not, an export UI plugin will have to make its own callbacks. 755 */ 756 function ctools_export_ui_ctools_access_set($argument, $access) { 757 ctools_include('export-ui'); 758 list($plugin_name, $key) = explode(':', $argument); 759 760 $plugin = ctools_get_export_ui($plugin_name); 761 $handler = ctools_export_ui_get_handler($plugin); 762 763 if ($handler) { 764 ctools_include('context'); 765 $item = $handler->edit_cache_get($key); 766 if (!$item) { 767 $item = ctools_export_crud_load($handler->plugin['schema'], $key); 768 } 769 $item->access = $access; 770 return $handler->edit_cache_set_key($item, $key); 771 } 772 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Thu Mar 24 11:18:33 2011 | Cross-referenced by PHPXref 0.7 |