Textpattern | PHP Cross Reference | Content Management Systems |
Description: Diagnostics panel.
1 <?php 2 3 /* 4 * Textpattern Content Management System 5 * http://textpattern.com 6 * 7 * Copyright (C) 2005 Dean Allen 8 * Copyright (C) 2016 The Textpattern Development Team 9 * 10 * This file is part of Textpattern. 11 * 12 * Textpattern is free software; you can redistribute it and/or 13 * modify it under the terms of the GNU General Public License 14 * as published by the Free Software Foundation, version 2. 15 * 16 * Textpattern is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 * GNU General Public License for more details. 20 * 21 * You should have received a copy of the GNU General Public License 22 * along with Textpattern. If not, see <http://www.gnu.org/licenses/>. 23 */ 24 25 /** 26 * Diagnostics panel. 27 * 28 * @package Admin\Diag 29 */ 30 31 if (!defined('txpinterface')) { 32 die('txpinterface is undefined.'); 33 } 34 35 /** 36 * @ignore 37 */ 38 39 define("cs", ': '); 40 41 /** 42 * @ignore 43 */ 44 45 define("ln", str_repeat('-', 24).n); 46 47 global $files; 48 49 $files = check_file_integrity(); 50 51 if (!$files) { 52 $files = array(); 53 } else { 54 $files = array_keys($files); 55 } 56 57 if ($event == 'diag') { 58 require_privs('diag'); 59 60 $step = gps('step'); 61 doDiagnostics(); 62 } 63 64 /** 65 * Checks if the given Apache module is installed and active. 66 * 67 * @param string $m The module 68 * @return bool|null TRUE on success, NULL or FALSE on error 69 */ 70 71 function apache_module($m) 72 { 73 $modules = @apache_get_modules(); 74 75 if (is_array($modules)) { 76 return in_array($m, $modules); 77 } 78 } 79 80 /** 81 * Verifies temporary directory status. 82 * 83 * This function verifies that the given temporary directory is writeable. 84 * 85 * @param string $dir The directory to check 86 * @return bool|null NULL on error, TRUE on success 87 */ 88 89 function test_tempdir($dir) 90 { 91 $f = realpath(tempnam($dir, 'txp_')); 92 93 if (is_file($f)) { 94 @unlink($f); 95 96 return true; 97 } 98 } 99 100 /** 101 * Lists all database tables used by the Textpattern core. 102 * 103 * Returned tables include prefixes. 104 * 105 * @return array 106 */ 107 108 function list_txp_tables() 109 { 110 $table_names = array(PFX.'textpattern'); 111 $rows = getRows("SHOW TABLES LIKE '".PFX."txp\_%'"); 112 113 foreach ($rows as $row) { 114 $table_names[] = array_shift($row); 115 } 116 117 return $table_names; 118 } 119 120 /** 121 * Checks the status of the given database tables. 122 * 123 * @param array $tables The tables to check 124 * @param string $type Check type, either FOR UPGRADE, QUICK, FAST, MEDIUM, EXTENDED, CHANGED 125 * @param bool $warnings If TRUE, displays warnings 126 * @return array An array of table statuses 127 * @example 128 * print_r( 129 * check_tables(list_txp_tables()) 130 * ); 131 */ 132 133 function check_tables($tables, $type = 'FAST', $warnings = 0) 134 { 135 $msgs = array(); 136 137 foreach ($tables as $table) { 138 $rs = getRows("CHECK TABLE `$table` $type"); 139 if ($rs) { 140 foreach ($rs as $r) { 141 if ($r['Msg_type'] != 'status' and ($warnings or $r['Msg_type'] != 'warning')) { 142 $msgs[] = $table.cs.$r['Msg_type'].cs.$r['Msg_text']; 143 } 144 } 145 } 146 } 147 148 return $msgs; 149 } 150 151 /** 152 * Renders a diagnostics message block. 153 * 154 * @param string $msg The message 155 * @param string $type The message type 156 * @return string HTML 157 * @access private 158 */ 159 160 function diag_msg_wrap($msg, $type = 'error') 161 { 162 return span($msg, array('class' => $type)); 163 } 164 165 /** 166 * Outputs a diagnostics report. 167 * 168 * This is the main panel. 169 */ 170 171 function doDiagnostics() 172 { 173 global $prefs, $files, $txpcfg, $event, $step, $theme, $DB, $txp_using_svn; 174 extract(get_prefs()); 175 176 $urlparts = parse_url(hu); 177 $mydomain = $urlparts['host']; 178 179 $is_apache = stristr(serverSet('SERVER_SOFTWARE'), 'Apache') || is_callable('apache_get_version'); 180 $real_doc_root = (isset($_SERVER['DOCUMENT_ROOT'])) ? realpath($_SERVER['DOCUMENT_ROOT']) : ''; 181 182 // ini_get() returns string values passed via php_value as a string, 183 // not boolean. 184 $is_register_globals = ((strcasecmp(ini_get('register_globals'), 'on') === 0) or (ini_get('register_globals') === '1')); 185 186 $fail = array(); 187 $now = time(); 188 189 if (!$txp_using_svn) { 190 // Check for Textpattern updates, at most once every 24 hours. 191 $updateInfo = unserialize(get_pref('last_update_check', '')); 192 193 if (!$updateInfo || ($now > ($updateInfo['when'] + (60 * 60 * 24)))) { 194 $updates = checkUpdates(); 195 $updateInfo['msg'] = ($updates) ? gTxt($updates['msg'], array('{version}' => $updates['version'])) : ''; 196 $updateInfo['when'] = $now; 197 set_pref('last_update_check', serialize($updateInfo), 'publish', PREF_HIDDEN, 'text_input'); 198 } 199 200 if (!empty($updateInfo['msg'])) { 201 $fail['textpattern_version_update'] = diag_msg_wrap($updateInfo['msg'], 'information'); 202 } 203 } 204 205 if (!is_callable('version_compare') || version_compare(PHP_VERSION, REQUIRED_PHP_VERSION, '<')) { 206 $fail['php_version_required'] = diag_msg_wrap(gTxt('php_version_required', array('{version}' => REQUIRED_PHP_VERSION))); 207 } 208 209 if (@gethostbyname($mydomain) === $mydomain) { 210 $fail['dns_lookup_fails'] = diag_msg_wrap(gTxt('dns_lookup_fails').cs.$mydomain, 'warning'); 211 } 212 213 if (!@is_dir($path_to_site)) { 214 $fail['path_to_site_inacc'] = diag_msg_wrap(gTxt('path_to_site_inacc').cs.$path_to_site); 215 } 216 217 if (rtrim($siteurl, '/') != $siteurl) { 218 $fail['site_trailing_slash'] = diag_msg_wrap(gTxt('site_trailing_slash').cs.$path_to_site, 'warning'); 219 } 220 221 if (!@is_file($path_to_site."/index.php") || !@is_readable($path_to_site."/index.php")) { 222 $fail['index_inaccessible'] = diag_msg_wrap("{$path_to_site}/index.php ".gTxt('is_inaccessible')); 223 } 224 225 $not_readable = array(); 226 227 if (!@is_writable($path_to_site.'/'.$img_dir)) { 228 $not_readable[] = diag_msg_wrap(str_replace('{dirtype}', gTxt('img_dir'), gTxt('dir_not_writable')).": {$path_to_site}/{$img_dir}", 'warning'); 229 } 230 231 if (!@is_writable($file_base_path)) { 232 $not_readable[] = diag_msg_wrap(str_replace('{dirtype}', gTxt('file_base_path'), gTxt('dir_not_writable')).": {$file_base_path}", 'warning'); 233 } 234 235 if (!@is_writable($tempdir)) { 236 $not_readable[] = diag_msg_wrap(str_replace('{dirtype}', gTxt('tempdir'), gTxt('dir_not_writable')).": {$tempdir}", 'warning'); 237 } 238 239 if ($not_readable) { 240 $fail['dir_not_writable'] = join(n, $not_readable); 241 } 242 243 if ($permlink_mode != 'messy' && !$is_apache) { 244 $fail['cleanurl_only_apache'] = diag_msg_wrap(gTxt('cleanurl_only_apache'), 'information'); 245 } 246 247 if ($permlink_mode != 'messy' and !@is_readable($path_to_site.'/.htaccess')) { 248 $fail['htaccess_missing'] = diag_msg_wrap(gTxt('htaccess_missing')); 249 } 250 251 if ($permlink_mode != 'messy' and is_callable('apache_get_modules') and !apache_module('mod_rewrite')) { 252 $fail['mod_rewrite_missing'] = diag_msg_wrap(gTxt('mod_rewrite_missing')); 253 } 254 255 if (!ini_get('file_uploads')) { 256 $fail['file_uploads_disabled'] = diag_msg_wrap(gTxt('file_uploads_disabled'), 'information'); 257 } 258 259 if (@is_dir(txpath.DS.'setup')) { 260 $fail['setup_still_exists'] = diag_msg_wrap(txpath.DS."setup".DS.' '.gTxt('still_exists'), 'warning'); 261 } 262 263 if (empty($tempdir)) { 264 $fail['no_temp_dir'] = diag_msg_wrap(gTxt('no_temp_dir'), 'warning'); 265 } 266 267 if (is_disabled('mail')) { 268 $fail['warn_mail_unavailable'] = diag_msg_wrap(gTxt('warn_mail_unavailable'), 'warning'); 269 } 270 271 if ($is_register_globals) { 272 $fail['warn_register_globals_or_update'] = diag_msg_wrap(gTxt('warn_register_globals_or_update'), 'warning'); 273 } 274 275 if ($permlink_mode != 'messy') { 276 $rs = safe_column("name", 'txp_section', "1 = 1"); 277 278 foreach ($rs as $name) { 279 if ($name and @file_exists($path_to_site.'/'.$name)) { 280 $fail['old_placeholder_exists'] = diag_msg_wrap(gTxt('old_placeholder').": {$path_to_site}/{$name}"); 281 } 282 } 283 } 284 285 $cs = check_file_integrity(INTEGRITY_REALPATH); 286 287 if (!$cs) { 288 $cs = array(); 289 } 290 291 // Files that don't match their checksums. 292 if (!$txp_using_svn and $modified_files = array_keys($cs, INTEGRITY_MODIFIED)) { 293 $fail['modified_files'] = diag_msg_wrap(gTxt('modified_files').cs.n.t.join(', '.n.t, $modified_files), 'warning'); 294 } 295 296 // Running development code in live mode is not recommended. 297 if (preg_match('/-dev$/', txp_version) and $production_status == 'live') { 298 $fail['dev_version_live'] = diag_msg_wrap(gTxt('dev_version_live'), 'warning'); 299 } 300 301 // Missing files. 302 if ($missing = array_merge( 303 array_keys($cs, INTEGRITY_MISSING), 304 array_keys($cs, INTEGRITY_NOT_FILE), 305 array_keys($cs, INTEGRITY_NOT_READABLE) 306 )) { 307 $fail['missing_files'] = diag_msg_wrap(gTxt('missing_files').cs.n.t.join(', '.n.t, $missing)); 308 } 309 310 // Anything might break if arbitrary functions are disabled. 311 if (ini_get('disable_functions')) { 312 $disabled_funcs = array_map('trim', explode(',', ini_get('disable_functions'))); 313 // Commonly disabled functions that we don't need. 314 $disabled_funcs = array_diff($disabled_funcs, array( 315 'imagefilltoborder', 316 'escapeshellarg', 317 'escapeshellcmd', 318 'exec', 319 'passthru', 320 'proc_close', 321 'proc_get_status', 322 'proc_nice', 323 'proc_open', 324 'proc_terminate', 325 'shell_exec', 326 'system', 327 'popen', 328 'dl', 329 'chown', 330 )); 331 332 if ($disabled_funcs) { 333 $fail['some_php_functions_disabled'] = diag_msg_wrap(gTxt('some_php_functions_disabled').cs.join(', ', $disabled_funcs), 'warning'); 334 } 335 } 336 337 // Not sure about this one. 338 // if (strncmp(php_sapi_name(), 'cgi', 3) == 0 and ini_get('cgi.rfc2616_headers')) 339 // $fail['cgi_header_config'] = gTxt('cgi_header_config'); 340 341 $guess_site_url = $_SERVER['HTTP_HOST'].preg_replace('#[/\\\\]$#', '', dirname(dirname($_SERVER['SCRIPT_NAME']))); 342 343 if ($siteurl and strip_prefix($siteurl, 'www.') != strip_prefix($guess_site_url, 'www.')) { 344 $fail['site_url_mismatch'] = diag_msg_wrap(gTxt('site_url_mismatch').cs.$guess_site_url, 'warning'); 345 } 346 347 // Test clean URL server vars. 348 if (hu) { 349 if (ini_get('allow_url_fopen') and ($permlink_mode != 'messy')) { 350 $s = md5(uniqid(rand(), true)); 351 ini_set('default_socket_timeout', 10); 352 353 $pretext_data = @file(hu.$s.'/?txpcleantest=1'); 354 355 if ($pretext_data) { 356 $pretext_req = trim(@$pretext_data[0]); 357 358 if ($pretext_req != md5('/'.$s.'/?txpcleantest=1')) { 359 $fail['clean_url_data_failed'] = diag_msg_wrap(gTxt('clean_url_data_failed').cs.txpspecialchars($pretext_req), 'warning'); 360 } 361 } else { 362 $fail['clean_url_test_failed'] = diag_msg_wrap(gTxt('clean_url_test_failed'), 'warning'); 363 } 364 } 365 } 366 367 if ($tables = list_txp_tables()) { 368 $table_errors = check_tables($tables); 369 if ($table_errors) { 370 $fail['mysql_table_errors'] = diag_msg_wrap(gTxt('mysql_table_errors').cs.n.t.join(', '.n.t, $table_errors)); 371 } 372 } 373 374 $active_plugins = array(); 375 if ($rows = safe_rows("name, version, code_md5, MD5(code) AS md5", 'txp_plugin', "status > 0")) { 376 foreach ($rows as $row) { 377 $n = $row['name'].'-'.$row['version']; 378 379 if (strtolower($row['md5']) != strtolower($row['code_md5'])) { 380 $n .= 'm'; 381 } 382 383 $active_plugins[] = $n; 384 } 385 } 386 387 $theme_manifest = $theme->manifest(); 388 389 // Check GD info. 390 if (function_exists('gd_info')) { 391 $gd_info = gd_info(); 392 393 $gd_support = array(); 394 395 if ($gd_info['GIF Create Support']) { 396 $gd_support[] = 'GIF'; 397 } 398 399 // Aside: In PHP 5.3, they chose to add a previously unemployed capital 400 // "E" to the array key. 401 if (!empty($gd_info['JPEG Support']) || !empty($gd_info['JPG Support'])) { 402 $gd_support[] = 'JPG'; 403 } 404 405 if ($gd_info['PNG Support']) { 406 $gd_support[] = 'PNG'; 407 } 408 409 if ($gd_support) { 410 $gd_support = join(', ', $gd_support); 411 } else { 412 $gd_support = gTxt('none'); 413 } 414 415 $gd = gTxt('gd_info', array( 416 '{version}' => $gd_info['GD Version'], 417 '{supported}' => $gd_support, 418 )); 419 } else { 420 $gd = gTxt('gd_unavailable'); 421 } 422 423 if (realpath($prefs['tempdir']) === realpath($prefs['plugin_cache_dir'])) { 424 $fail['tmp_plugin_paths_match'] = diag_msg_wrap(gTxt('tmp_plugin_paths_match')); 425 } 426 427 // Database server time. 428 extract(doSpecial(getRow("SELECT @@global.time_zone AS db_global_timezone, @@session.time_zone AS db_session_timezone, NOW() AS db_server_time, UNIX_TIMESTAMP(NOW()) AS db_server_timestamp"))); 429 $db_server_timeoffset = $db_server_timestamp - $now; 430 431 echo pagetop(gTxt('tab_diagnostics'), ''); 432 433 echo n.'<div class="txp-layout">'. 434 n.tag( 435 hed(gTxt('tab_diagnostics'), 1, array('class' => 'txp-heading')), 436 'div', array('class' => 'txp-layout-1col') 437 ). 438 n.tag_start('div', array( 439 'class' => 'txp-layout-1col', 440 'id' => $event.'_container', 441 )). 442 n.tag_start('div', array('id' => 'pre_flight_check')). 443 hed(gTxt('preflight_check'), 2); 444 445 if ($fail) { 446 foreach ($fail as $help => $message) { 447 echo graf(nl2br($message).popHelp($help)); 448 } 449 } else { 450 echo graf(diag_msg_wrap(gTxt('all_checks_passed'), 'success')); 451 } 452 453 echo n.tag_end('div'). // End of #pre_flight_check. 454 n.tag_start('div', array('id' => 'diagnostics')). 455 hed(gTxt('diagnostic_info'), 2); 456 457 $fmt_date = '%Y-%m-%d %H:%M:%S'; 458 459 $dets = array( 460 'low' => gTxt('low'), 461 'high' => gTxt('high'), 462 ); 463 464 $out = array( 465 form( 466 eInput('diag'). 467 inputLabel( 468 'diag_detail_level', 469 selectInput('step', $dets, $step, 0, 1, 'diag_detail_level'), 470 'detail', 471 '', 472 array('class' => 'txp-form-field diagnostic-details-level'), 473 '' 474 ) 475 ), 476 477 '<textarea class="code" id="diagnostics-detail" cols="'.INPUT_LARGE.'" rows="'.TEXTAREA_HEIGHT_LARGE.'" dir="ltr" readonly>', 478 479 gTxt('txp_version').cs.txp_version.' ('.check_file_integrity(INTEGRITY_DIGEST).')'.n, 480 481 gTxt('last_update').cs.gmstrftime($fmt_date, $dbupdatetime).'/'.gmstrftime($fmt_date, @filemtime(txpath.'/update/_update.php')).n, 482 483 gTxt('document_root').cs.@$_SERVER['DOCUMENT_ROOT'].(($real_doc_root != @$_SERVER['DOCUMENT_ROOT']) ? ' ('.$real_doc_root.')' : '').n, 484 485 '$path_to_site'.cs.$path_to_site.n, 486 487 gTxt('txp_path').cs.txpath.n, 488 489 gTxt('permlink_mode').cs.$permlink_mode.n, 490 491 (ini_get('open_basedir')) ? 'open_basedir: '.ini_get('open_basedir').n : '', 492 493 (ini_get('upload_tmp_dir')) ? 'upload_tmp_dir: '.ini_get('upload_tmp_dir').n : '', 494 495 gTxt('tempdir').cs.$tempdir.n, 496 497 gTxt('web_domain').cs.$siteurl.n, 498 499 gTxt('php_version').cs.phpversion().n, 500 501 ($is_register_globals) ? gTxt('register_globals').cs.$is_register_globals.n : '', 502 503 gTxt('gd_library').cs.$gd.n, 504 505 gTxt('server').' TZ: '.Txp::get('\Textpattern\Date\Timezone')->getTimeZone().n, 506 gTxt('server_time').cs.strftime('%Y-%m-%d %H:%M:%S').n, 507 strip_tags(gTxt('is_dst')).cs.$is_dst.n, 508 strip_tags(gTxt('auto_dst')).cs.$auto_dst.n, 509 strip_tags(gTxt('gmtoffset')).cs.$timezone_key.sp."($gmtoffset)".n, 510 511 'MySQL'.cs.mysqli_get_server_info($DB->link).n, 512 gTxt('db_server_time').cs.$db_server_time.n, 513 gTxt('db_server_timeoffset').cs.$db_server_timeoffset.' s'.n, 514 gTxt('db_global_timezone').cs.$db_global_timezone.n, 515 gTxt('db_session_timezone').cs.$db_session_timezone.n, 516 517 gTxt('locale').cs.$locale.n, 518 519 (isset($_SERVER['SERVER_SOFTWARE'])) ? gTxt('server').cs.$_SERVER['SERVER_SOFTWARE'].n : '', 520 521 (is_callable('apache_get_version')) ? gTxt('apache_version').cs.@apache_get_version().n : '', 522 523 gTxt('php_sapi_mode').cs.PHP_SAPI.n, 524 525 gTxt('rfc2616_headers').cs.ini_get('cgi.rfc2616_headers').n, 526 527 gTxt('os_version').cs.php_uname('s').' '.php_uname('r').n, 528 529 ($active_plugins ? gTxt('active_plugins').cs.join(', ', $active_plugins).n : ''), 530 531 gTxt('theme_name').cs.$theme_name.sp.$theme_manifest['version'].n, 532 533 $fail 534 ? n.gTxt('preflight_check').cs.n.ln.join("\n", doStripTags($fail)).n.ln 535 : '', 536 537 (is_readable($path_to_site.'/.htaccess')) 538 ? n.gTxt('htaccess_contents').cs.n.ln.txpspecialchars(join('', file($path_to_site.'/.htaccess'))).n.ln 539 : '', 540 ); 541 542 if ($step == 'high') { 543 $out[] = n.'Charset (default/config)'.cs.$DB->default_charset.'/'.$DB->charset.n; 544 545 $result = safe_query("SHOW variables LIKE 'character_se%'"); 546 547 while ($row = mysqli_fetch_row($result)) { 548 $out[] = $row[0].cs.$row[1].n; 549 550 if ($row[0] == 'character_set_connection') { 551 $conn_char = $row[1]; 552 } 553 } 554 555 $table_names = array(PFX.'textpattern'); 556 $result = safe_query("SHOW TABLES LIKE '".PFX."txp\_%'"); 557 558 while ($row = mysqli_fetch_row($result)) { 559 $table_names[] = $row[0]; 560 } 561 562 $table_msg = array(); 563 564 foreach ($table_names as $table) { 565 $ctr = safe_query("SHOW CREATE TABLE $table"); 566 if (!$ctr) { 567 unset($table_names[$table]); 568 continue; 569 } 570 571 $row = mysqli_fetch_assoc($ctr); 572 $ctcharset = preg_replace('#^CREATE TABLE.*SET=([^ ]+)[^)]*$#is', '\\1', $row['Create Table']); 573 if (isset($conn_char) && !stristr($ctcharset, 'CREATE') && ($conn_char != $ctcharset)) { 574 $table_msg[] = "$table is $ctcharset"; 575 } 576 577 $ctr = safe_query("CHECK TABLE $table"); 578 $row = mysqli_fetch_assoc($ctr); 579 if (in_array($row['Msg_type'], array('error', 'warning'))) { 580 $table_msg[] = $table.cs.$row['Msg_Text']; 581 } 582 } 583 584 if ($table_msg == array()) { 585 $table_msg = (count($table_names) < 17) ? array('-') : array('OK'); 586 } 587 588 $out[] = count($table_names).' Tables'.cs.implode(', ', $table_msg).n; 589 590 $cf = preg_grep('/^custom_\d+/', getThings("DESCRIBE `".PFX."textpattern`")); 591 $out[] = n.get_pref('max_custom_fields', 10).sp.gTxt('custom').cs. 592 implode(', ', $cf).sp.'('.count($cf).')'.n; 593 594 $extns = get_loaded_extensions(); 595 $extv = array(); 596 597 foreach ($extns as $e) { 598 $extv[] = $e.(phpversion($e) ? '/'.phpversion($e) : ''); 599 } 600 601 $out[] = n.gTxt('php_extensions').cs.join(', ', $extv).n; 602 603 if (is_callable('apache_get_modules')) { 604 $out[] = n.gTxt('apache_modules').cs.join(', ', apache_get_modules()).n; 605 } 606 607 if (@is_array($pretext_data) and count($pretext_data) > 1) { 608 $out[] = n.gTxt('pretext_data').cs.txpspecialchars(join('', array_slice($pretext_data, 1, 20))).n; 609 } 610 611 $out[] = n; 612 613 if ($md5s = check_file_integrity(INTEGRITY_MD5)) { 614 foreach ($md5s as $f => $checksum) { 615 $out[] = $f.cs.n.t.(!$checksum ? gTxt('unknown') : $checksum).n; 616 } 617 } 618 619 $out[] = n.ln; 620 } 621 622 $out[] = callback_event('diag_results', $step).n; 623 $out[] = '</textarea>'; 624 625 echo join('', $out), 626 n.tag_end('div'). // End of #diagnostics. 627 n.tag_end('div'). // End of .txp-layout-1col. 628 n.'</div>'; // End of .txp-layout.; 629 } 630 631 /** 632 * Checks for Textpattern updates. 633 * 634 * This function uses XML-RPC to do an active remote connection to 635 * rpc.textpattern.com. Created connections are not cached, scheduled or 636 * delayed, and each subsequent call to the function creates a new connection. 637 * 638 * These connections do not transmit any identifiable information. Just an 639 * anonymous UID assigned for the installation on the first run. 640 * 641 * @return array|null When updates are found returns an array consisting keys 'version', 'msg' 642 * @example 643 * if ($updates = checkUpdates()) 644 * { 645 * echo "New version: {$updates['version']}"; 646 * } 647 */ 648 649 function checkUpdates() 650 { 651 require_once txpath.'/lib/IXRClass.php'; 652 $client = new IXR_Client('http://rpc.textpattern.com'); 653 654 if (!$client->query('tups.getTXPVersion', get_pref('blog_uid'))) { 655 return array('version' => 0, 'msg' => 'problem_connecting_rpc_server'); 656 } else { 657 $out = array(); 658 $response = $client->getResponse(); 659 660 if (is_array($response)) { 661 ksort($response); 662 $version = get_pref('version'); 663 664 // Go through each available branch (x.y), but only return the 665 // _highest_ version. 666 foreach ($response as $key => $val) { 667 if (version_compare($version, $val) < 0) { 668 $out = array('version' => $val, 'msg' => 'textpattern_update_available'); 669 } 670 } 671 672 return $out; 673 } 674 } 675 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title