| [ Index ] |
PHP Cross Reference of Drupal 6 (gatewave) |
[Summary view] [Print] [Text view]
1 <?php 2 // $Id: content.install,v 1.85.2.33 2009/07/14 22:17:05 yched Exp $ 3 4 function content_requirements($phase) { 5 $requirements = array(); 6 // Ensure translations don't break at install time 7 $t = get_t(); 8 if (module_exists('views') && (!function_exists('views_api_version') || views_api_version() < 2.0)) { 9 $requirements['cck_views'] = array( 10 'title' => $t('CCK - No Views integration'), 11 'description' => $t("CCK integration with Views module requires Views 6.x-2.0-rc2 or greater."), 12 'severity' => REQUIREMENT_ERROR, 13 ); 14 } 15 return $requirements; 16 } 17 18 /** 19 * 'Safe' version of content_types() to use in updates and installs. 20 * 21 * Can't safely use content_fields() or content_types() in an update to get 22 * a fields array, especially without knowing what field modules are enabled, 23 * or the current state of the database and cache, so create a fields array 24 * from database info that is limited to fields from modules that are 25 * currently enabled. 26 */ 27 function content_types_install() { 28 drupal_load('module', 'content'); 29 module_load_include('inc', 'content', '/includes/content.crud'); 30 $module_field_types = $module_widgets = array(); 31 foreach (module_list() as $module) { 32 if ($field_type = module_invoke($module, 'field_info')) { 33 $module_field_types[$module] = $field_type; 34 } 35 if ($widget_type = module_invoke($module, 'widget_info')) { 36 $module_widgets[$module] = $widget_type; 37 } 38 } 39 $fields = array(); 40 $db_result = db_query("SELECT * FROM {". content_instance_tablename() ."} nfi ". 41 " LEFT JOIN {". content_field_tablename() ."} nf ON nf.field_name = nfi.field_name"); 42 while ($row = db_fetch_array($db_result)) { 43 $field = array_merge($row, unserialize($row['global_settings'])); 44 unset($field['global_settings']); 45 46 // There may be module data available for currently disabled modules, 47 // or missing module data for currently enabled modules, so start over 48 // to get only field info for enabled modules. 49 unset($field['module']); 50 unset($field['widget_module']); 51 // 'columns' is a reserved word in MySQL4, so our column is named 'db_columns'. 52 $field['columns'] = isset($field['db_columns']) ? $field['db_columns'] : array(); 53 unset($field['db_columns']); 54 55 foreach ($module_field_types as $module => $types) { 56 foreach ($types as $type_name => $type) { 57 if ($field['type'] == $type_name) { 58 $field['module'] = $module; 59 } 60 } 61 } 62 foreach ($module_widgets as $module => $types) { 63 foreach ($types as $type_name => $type) { 64 if ($field['widget_type'] == $type_name) { 65 $field['widget_module'] = $module; 66 } 67 } 68 } 69 if (!empty($field['module']) && !empty($field['widget_module'])) { 70 $field['widget_settings'] = unserialize($field['widget_settings']); 71 $field['display_settings'] = unserialize($field['display_settings']); 72 $field['columns'] = (array) module_invoke($field['module'], 'field_settings', 'database columns', $field); 73 $field = content_field_instance_expand($field); 74 $fields[$field['type_name']][$field['field_name']] = $field; 75 } 76 } 77 return $fields; 78 } 79 80 /** 81 * Implementation of hook_install(). 82 */ 83 function content_install() { 84 variable_set('content_schema_version', 6009); 85 drupal_install_schema('content'); 86 } 87 88 89 /** 90 * Implementation of hook_uninstall(). 91 */ 92 function content_uninstall() { 93 drupal_uninstall_schema('content'); 94 // The variable is used during the uninstall process, 95 // so we removed it at the very end. 96 variable_del('content_schema_version'); 97 // Remove extra weights. 98 foreach (node_get_types('names') as $type_name) { 99 variable_del("content_extra_weights_$type_name"); 100 } 101 } 102 103 /** 104 * Implementation of hook_enable(). 105 */ 106 function content_enable() { 107 // Make sure old data is emptied out of the caches, since it 108 // may no longer be valid since the module was last enabled, 109 // especially if not all the same field modules are enabled 110 // as before. Especially needed during updates. 111 cache_clear_all('*', 'cache_content', TRUE); 112 content_clear_type_cache(TRUE); 113 } 114 115 /** 116 * Implementation of hook_disable(). 117 */ 118 function content_disable() { 119 // Make sure old data is emptied out of the caches, since it 120 // may no longer be valid when the module is re-enabled. 121 cache_clear_all('*', 'cache_content', TRUE); 122 content_clear_type_cache(TRUE); 123 } 124 125 /** 126 * Implementation of hook_schema. 127 */ 128 function content_schema() { 129 130 // Static (meta) tables. 131 132 $schema['content_node_field'] = array( 133 'fields' => array( 134 'field_name' => array('type' => 'varchar', 'length' => 32, 'not null' => TRUE, 'default' => ''), 135 'type' => array('type' => 'varchar', 'length' => 127, 'not null' => TRUE, 'default' => ''), 136 'global_settings' => array('type' => 'text', 'size' => 'medium', 'not null' => TRUE, 'serialize' => TRUE), 137 'required' => array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 0), 138 'multiple' => array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 0), 139 'db_storage' => array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 1), 140 'module' => array('type' => 'varchar', 'length' => 127, 'not null' => TRUE, 'default' => ''), 141 'db_columns' => array('type' => 'text', 'size' => 'medium', 'not null' => TRUE, 'serialize' => TRUE), 142 'active' => array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 0), 143 'locked' => array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 0), 144 ), 145 'primary key' => array('field_name'), 146 ); 147 $schema['content_node_field_instance'] = array( 148 'fields' => array( 149 'field_name' => array('type' => 'varchar', 'length' => 32, 'not null' => TRUE, 'default' => ''), 150 'type_name' => array('type' => 'varchar', 'length' => 32, 'not null' => TRUE, 'default' => ''), 151 'weight' => array('type' => 'int', 'not null' => TRUE, 'default' => 0), 152 'label' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''), 153 'widget_type' => array('type' => 'varchar', 'length' => 32, 'not null' => TRUE, 'default' => ''), 154 'widget_settings' => array('type' => 'text', 'size' => 'medium', 'not null' => TRUE, 'serialize' => TRUE), 155 'display_settings' => array('type' => 'text', 'size' => 'medium', 'not null' => TRUE, 'serialize' => TRUE), 156 'description' => array('type' => 'text', 'size' => 'medium', 'not null' => TRUE), 157 'widget_module' => array('type' => 'varchar', 'length' => 127, 'not null' => TRUE, 'default' => ''), 158 'widget_active' => array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 0), 159 ), 160 'primary key' => array('field_name', 'type_name'), 161 ); 162 $schema['cache_content'] = drupal_get_schema_unprocessed('system', 'cache'); 163 164 // When the module is first installed, the remaining code in the schema 165 // will create errors, since these tables have not yet been created. 166 // We don't need to create data tables on initial installation anyway 167 // since no fields have been created yet, so just return with this much 168 // of the schema. 169 170 if (!db_table_exists('content_node_field') || !db_table_exists('content_node_field_instance')) { 171 return $schema; 172 } 173 174 // Dynamic (data) tables. 175 176 drupal_load('module', 'content'); 177 178 // We can't use many helper functions here, like content_fields() or 179 // content_types() or we risk creating a fatal loop from circular 180 // logic when they call other functions that use this schema, so create 181 // the schema directly from a fresh query of the database. 182 183 // content_table_schema() and content_database_info() have no 184 // circular logic and are safe to use here. 185 186 $db_result = db_query("SELECT * FROM {". content_instance_tablename() ."} nfi ". 187 " LEFT JOIN {". content_field_tablename() ."} nf ON nf.field_name = nfi.field_name WHERE nf.active = 1 AND nfi.widget_active = 1"); 188 while ($field = db_fetch_array($db_result)) { 189 // 'columns' is a reserved word in MySQL4, so our db column is named 'db_columns'. 190 $field['columns'] = unserialize($field['db_columns']); 191 unset($field['db_columns']); 192 193 $content_table = _content_tablename($field['type_name'], CONTENT_DB_STORAGE_PER_CONTENT_TYPE); 194 $field_table = _content_tablename($field['field_name'], CONTENT_DB_STORAGE_PER_FIELD); 195 196 197 // We always add a 'per content type' table for each content type that 198 // has fields. 199 if (!isset($schema[$content_table])) { 200 $schema[$content_table] = content_table_schema(); 201 } 202 203 $base_schema = content_table_schema($field); 204 if ($field['db_storage'] == CONTENT_DB_STORAGE_PER_FIELD) { 205 // Per-field storage: add the 'per field' table if needed. 206 if (!isset($schema[$field_table])) { 207 $schema[$field_table] = $base_schema; 208 } 209 } 210 else { 211 // Per-type storage: merge the information for the field 212 // in the existing table. 213 $schema[$content_table]['fields'] = array_merge($schema[$content_table]['fields'], $base_schema['fields']); 214 $schema[$content_table]['content fields'] = array_merge($schema[$content_table]['content fields'], $base_schema['content fields']); 215 } 216 } 217 return $schema; 218 } 219 220 function content_update_last_removed() { 221 return 1008; 222 } 223 224 /** 225 * Helper function for module updates : 226 * - checks no updates are pending for content.module 227 * - checks content module and the module being updated are both enabled. 228 * 229 * @param $module 230 * The name of the module being updated. 231 */ 232 function content_check_update($module = NULL) { 233 $ret = array(); 234 // Check that modules are enabled before running their updates. 235 if (!module_exists('content') || ($module && !module_exists($module))) { 236 drupal_set_message(t("Updates for CCK-related modules are not run until the modules are enabled on the <a href=\"@admin-modules-path\">administer modules page</a>. When you enable them, you'll need to return to <a href=\"@update-php\">update.php</a> and run the remaining updates.", array('@admin-modules-path' => url('admin/build/modules'), '@update-php' => base_path() .'update.php?op=selection')), 'warning', FALSE); 237 // The content module is not enabled, nothing else can happen. 238 if ($module && !module_exists('content') && module_exists($module)) { 239 $query_message = t('!module.module has updates but cannot be updated because content.module is not enabled.<br />If and when content.module is enabled, you will need to re-run the update script. You will continue to see this message until the module is enabled and updates are run.', array('!module' => $module)); 240 } 241 // The requested module is not enabled, which may be intentional. 242 // Just let the user know there are updates to be processed if enabled later. 243 else { 244 $query_message = t('!module.module has updates and is available in the modules folder but is not enabled.<br />If and when it is enabled, you will need to re-run the update script. You will continue to see this message until the module is enabled and updates are run.', array('!module' => $module ? $module : 'content')); 245 } 246 $ret['#abort'] = array('success' => FALSE, 'query' => $query_message); 247 return $ret; 248 } 249 // Check that content.module is up-to-date before running field module updates. 250 if ($module && (drupal_get_installed_schema_version('content', TRUE) < max(drupal_get_schema_versions('content')))) { 251 drupal_set_message(t('Some updates are still pending. Please return to <a href="@update-php">update.php</a> and run the remaining updates.', array('@update-php' => base_path() .'update.php?op=selection')), 'warning', FALSE); 252 $ret['#abort'] = array('success' => FALSE, 'query' => t('Some updates are still pending.<br/>Please re-run the update script.')); 253 return $ret; 254 } 255 // If everything is OK and updates are not aborted, make sure 256 // content_associate_fields() gets run. With all the complexity of 257 // the dependent updates, it can get missed when an update is aborted. 258 // It won't hurt anything to do this more than once in order to be sure 259 // it doesn't get skipped. Without this step, we can end up with 260 // field modules that are enabled and updated, but not marked as active 261 // in the content_node_field table. 262 if ($module and module_exists($module)) { 263 content_associate_fields($module); 264 } 265 } 266 267 /** 268 * Add module name to fields table to make it easier to identify the fields to delete when a module 269 * is uninstalled. 270 * 271 * Needed because the value drops out of content_info() when module is disabled, so there 272 * is no other way to find the associated fields. 273 */ 274 function content_update_6000() { 275 if ($abort = content_check_update()) { 276 return $abort; 277 } 278 279 $ret = array(); 280 281 drupal_load('module', 'content'); 282 if (db_column_exists(content_field_tablename(), 'active')) { 283 return $ret; 284 } 285 db_add_field($ret, content_field_tablename(), 'module', array('type' => 'varchar', 'length' => 127, 'not null' => TRUE, 'default' => '')); 286 db_add_field($ret, content_field_tablename(), 'db_columns', array('type' => 'text', 'size' => 'medium', 'not null' => TRUE, 'initial' => '')); 287 db_add_field($ret, content_field_tablename(), 'active', array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 0)); 288 db_add_field($ret, content_instance_tablename(), 'widget_module', array('type' => 'varchar', 'length' => 127, 'not null' => TRUE, 'default' => '')); 289 db_add_field($ret, content_instance_tablename(), 'widget_active', array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 0)); 290 291 // This will update the table for any modules enabled at this time. 292 foreach (module_list() as $module) { 293 content_associate_fields($module); 294 } 295 296 // Fix the cache_content schema 297 if (db_table_exists('cache_content')) { 298 db_drop_table($ret, 'cache_content'); 299 } 300 db_create_table($ret, 'cache_content', drupal_get_schema_unprocessed('system', 'cache')); 301 variable_set('content_schema_version', 6000); 302 303 // The cache table had to be used to store data until this update ran, 304 // so clear cache out now that we're switching back to the cache_content table. 305 $ret[] = update_sql('DELETE FROM {cache}'); 306 307 return $ret; 308 } 309 310 /** 311 * Rename node_field and node_field_instance tables. 312 * 313 * This is a carryover from when the data tables were renamed, 314 * postponed so we wouldn't create any more havoc than necessary 315 * until a major version change. 316 * 317 * Using 'content_node_field' instead of 'content_field' 318 * to avoid conflicts with field tables that will be prefixed 319 * with 'content_field'. 320 */ 321 function content_update_6001() { 322 if ($abort = content_check_update()) { 323 return $abort; 324 } 325 326 $ret = array(); 327 drupal_load('module', 'content'); 328 if (db_table_exists('content_node_field')) { 329 return $ret; 330 } 331 db_rename_table($ret, 'node_field', 'content_node_field'); 332 db_rename_table($ret, 'node_field_instance', 'content_node_field_instance'); 333 variable_set('content_schema_version', 6001); 334 content_clear_type_cache(TRUE); 335 return $ret; 336 } 337 338 /** 339 * Get rid of automatic per content tables for content types that have no fields. 340 * Switching to adding those tables only when needed. 341 */ 342 function content_update_6002() { 343 if ($abort = content_check_update()) { 344 return $abort; 345 } 346 347 $ret = array(); 348 349 drupal_load('module', 'content'); 350 $db_types = content_types_install(); 351 $field_types = array(); 352 353 $result = db_query("SELECT DISTINCT type_name FROM {". content_instance_tablename() ."}"); 354 while ($type = db_fetch_array($result)) { 355 $field_types[] = $type['type_name']; 356 } 357 358 foreach ($db_types as $content_type => $content_info) { 359 if (!in_array($content_type, $field_types)) { 360 $table = _content_tablename($content_type, CONTENT_DB_STORAGE_PER_CONTENT_TYPE); 361 if (db_table_exists($table)) { 362 db_drop_table($ret, $table); 363 } 364 } 365 } 366 variable_set('content_schema_version', 6002); 367 content_clear_type_cache(TRUE); 368 return $ret; 369 } 370 371 /** 372 * 'db_columns' column 1st got introduced as 'columns', which is forbidden in MySQL 4. 373 * This update function will only be useful for early D6 testers... 374 */ 375 function content_update_6003() { 376 if ($abort = content_check_update()) { 377 return $abort; 378 } 379 380 $ret = array(); 381 if (db_column_exists('content_node_field', 'columns')) { 382 db_change_field($ret, 'content_node_field', 'columns', 'db_columns', array('type' => 'text', 'size' => 'medium', 'not null' => TRUE)); 383 } 384 variable_set('content_schema_version', 6003); 385 return $ret; 386 } 387 388 /** 389 * Index the 'nid' column on data tables to optimize node deletion. 390 * Large tables might deserve a multipass update. 391 */ 392 function content_update_6004(&$sandbox) { 393 if ($abort = content_check_update()) { 394 return $abort; 395 } 396 397 $ret = array(); 398 399 // Do nothing if the indexes were already created by D5's content_update_1009. 400 if (variable_get('content_update_1009', FALSE)) { 401 return $ret; 402 } 403 404 // Gather list of tables. 405 if (!isset($sandbox['tables'])) { 406 drupal_load('module', 'content'); 407 $sandbox['tables'] = array(); 408 $result = db_query('SELECT * FROM {'. content_instance_tablename() .'} nfi '. 409 ' LEFT JOIN {'. content_field_tablename() .'} nf ON nf.field_name = nfi.field_name'); 410 while ($field = db_fetch_array($result)) { 411 if ($field['db_storage'] == CONTENT_DB_STORAGE_PER_FIELD) { 412 $table = _content_tablename($field['field_name'], CONTENT_DB_STORAGE_PER_FIELD); 413 } 414 else { 415 $table = _content_tablename($field['type_name'], CONTENT_DB_STORAGE_PER_CONTENT_TYPE); 416 } 417 $sandbox['tables'][$table] = $table; 418 } 419 $sandbox['count'] = count($sandbox['tables']); 420 } 421 422 // One pass : add index on one table. 423 if ($table = array_shift($sandbox['tables'])) { 424 db_add_index($ret, $table, 'nid', array('nid')); 425 } 426 427 if ($sandbox['count']) { 428 $ret['#finished'] = 1 - count($sandbox['tables']) / $sandbox['count']; 429 } 430 variable_set('content_schema_version', 6004); 431 return $ret; 432 } 433 434 /** 435 * Add 'locked' property for fields. 436 */ 437 function content_update_6005() { 438 if ($abort = content_check_update()) { 439 return $abort; 440 } 441 442 $ret = array(); 443 drupal_load('module', 'content'); 444 db_add_field($ret, content_field_tablename(), 'locked', array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 0)); 445 variable_set('content_schema_version', 6005); 446 return $ret; 447 } 448 449 /** 450 * Make sure the 'locked' column is NOT NULL (error in previous content_update_6005(). 451 */ 452 function content_update_6006() { 453 if ($abort = content_check_update()) { 454 return $abort; 455 } 456 457 $ret = array(); 458 drupal_load('module', 'content'); 459 db_change_field($ret, content_field_tablename(), 'locked', 'locked', array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 0)); 460 variable_set('content_schema_version', 6006); 461 return $ret; 462 } 463 464 /** 465 * Dummy update function to make sure the theme registry and css / JS aggregated files 466 * are updated. 467 */ 468 function content_update_6007() { 469 if ($abort = content_check_update()) { 470 return $abort; 471 } 472 473 variable_set('content_schema_version', 6007); 474 return array(); 475 } 476 477 /** 478 * Dummy update function to make sure schema version gets updated. 479 */ 480 function content_update_6008() { 481 if ($abort = content_check_update()) { 482 return $abort; 483 } 484 485 variable_set('content_schema_version', 6008); 486 return array(); 487 } 488 489 /** 490 * Add the 'exclude from $content' display setting to all existing field instances. 491 */ 492 function content_update_6009() { 493 if ($abort = content_check_update()) { 494 return $abort; 495 } 496 497 $ret = array(); 498 $result = db_query("SELECT * FROM {content_node_field_instance}"); 499 while ($type = db_fetch_array($result)) { 500 $new_settings = array(); 501 $display_settings = unserialize($type['display_settings']); 502 if (!empty($display_settings)) { 503 foreach ($display_settings as $key => $val) { 504 $new_settings[$key] = $val; 505 if ($key !== 'label' && is_array($val)) { 506 $new_settings[$key]['exclude'] = 0; 507 } 508 } 509 } 510 else { 511 $new_settings = array( 512 'label' => array('format' => 'above'), 513 'full' => array('format' => 'default', 'exclude' => 0), 514 'teaser' => array('format' => 'default', 'exclude' => 0), 515 ); 516 } 517 db_query("UPDATE {content_node_field_instance} SET display_settings='%s' WHERE field_name='%s' AND type_name='%s'", serialize($new_settings), $type['field_name'], $type['type_name']); 518 } 519 variable_set('content_schema_version', 6009); 520 return $ret; 521 } 522 523 /** 524 * Fix multiple serialization caused by per-field to per-type migration. 525 * See http://drupal.org/node/407446. 526 */ 527 function content_update_6010(&$sandbox) { 528 if ($abort = content_check_update()) { 529 return $abort; 530 } 531 $ret = array(); 532 533 drupal_load('module', 'content'); 534 535 // Gather list of tables and columns that need to be updated. 536 if (!isset($sandbox['tables'])) { 537 $sandbox['tables'] = array(); 538 $fields = content_fields(); 539 foreach ($fields as $name => $field) { 540 $db_info = content_database_info($field); 541 foreach ($db_info['columns'] as $column => $attributes) { 542 if (isset($attributes['serialize']) && $attributes['serialize']) { 543 $sandbox['tables'][$db_info['table']]['table'] = $db_info['table']; 544 $sandbox['tables'][$db_info['table']]['columns'][] = $attributes['column']; 545 $sandbox['tables'][$db_info['table']]['multiple'] = $field['multiple']; 546 } 547 } 548 } 549 $sandbox['count'] = count($sandbox['tables']); 550 $sandbox['current_vid'] = 0; 551 $sandbox['current_delta'] = 0; 552 } 553 554 // Number of rows to fix in one pass. 555 $limit = 500; 556 // Start correcting data. 557 if ($table_info = array_shift($sandbox['tables'])) { 558 $table = $table_info['table']; 559 $columns = $table_info['columns']; 560 561 if ($table_info['multiple']) { 562 $query = "SELECT * FROM {" . $table . "} WHERE (vid = %d AND delta > %d) OR (vid > %d) ORDER BY vid ASC, delta ASC"; 563 $args = array($sandbox['current_vid'], $sandbox['current_delta'], $sandbox['current_vid']); 564 } 565 else { 566 $query = "SELECT * FROM {" . $table . "} WHERE vid > %d ORDER BY vid ASC"; 567 $args = array($sandbox['current_vid']); 568 } 569 $result = db_query_range($query, $args, 0, $limit); 570 $count = 0; 571 while ($row = db_fetch_array($result)) { 572 $update_query = $update_args = array(); 573 foreach ($columns as $column) { 574 $data = $row[$column]; 575 // No need to do anything if the data is NULL. 576 if (!empty($data)) { 577 // Unserialize until we get something that is not a string 578 while (is_string($data)) { 579 $unserialized = @unserialize($data); 580 if ($unserialized !== FALSE) { 581 $data = $unserialized; 582 } 583 else { 584 // TODO : test with a serialized string, just in case... 585 break; 586 } 587 } 588 // Re-serialize once. 589 $data = serialize($data); 590 // If we end up with something different than what we started with, update. 591 if ($data !== $row[$column]) { 592 $update_query[] = "$column = '%s'"; 593 $update_args[] = $data; 594 } 595 } 596 } 597 if ($update_query) { 598 $update_args[] = $row['vid']; 599 db_query("UPDATE {" . $table . "} SET ". implode(', ', $update_query) ." WHERE vid = %d", $update_args); 600 } 601 $sandbox['current_vid'] = $row['vid']; 602 $sandbox['current_delta'] = isset($row['delta']) ? $row['delta'] : 0; 603 $count++; 604 } 605 if ($count == $limit) { 606 // Add the table back into the list of tables to be processed if rows remain. 607 array_unshift($sandbox['tables'], $table_info); 608 } 609 else { 610 // Done with this table: reset vid and delta markers. 611 $sandbox['current_vid'] = 0; 612 $sandbox['current_delta'] = 0; 613 $ret[] = array('success' => TRUE, 'query' => "Fixed serialized values in table $table"); 614 } 615 } 616 617 if ($sandbox['count']) { 618 $ret['#finished'] = 1 - count($sandbox['tables']) / $sandbox['count']; 619 } 620 return $ret; 621 }
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 |