Textpattern | PHP Cross Reference | Content Management Systems |
Description: Diagnostics panel.
1 <?php 2 3 /* 4 * Textpattern Content Management System 5 * https://textpattern.com/ 6 * 7 * Copyright (C) 2020 The Textpattern Development Team 8 * 9 * This file is part of Textpattern. 10 * 11 * Textpattern is free software; you can redistribute it and/or 12 * modify it under the terms of the GNU General Public License 13 * as published by the Free Software Foundation, version 2. 14 * 15 * Textpattern is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with Textpattern. If not, see <https://www.gnu.org/licenses/>. 22 */ 23 24 /** 25 * Diagnostics panel. 26 * 27 * @package Admin\Diag 28 */ 29 30 if (!defined('txpinterface')) { 31 die('txpinterface is undefined.'); 32 } 33 34 /** 35 * @ignore 36 */ 37 38 define("cs", ': '); 39 40 /** 41 * @ignore 42 */ 43 44 define("ln", str_repeat('-', 24).n); 45 46 /** 47 * @ignore 48 */ 49 50 define("priv", '=== '); 51 52 global $files; 53 54 $files = check_file_integrity(); 55 56 if (!$files) { 57 $files = array(); 58 } else { 59 $files = array_keys($files); 60 } 61 62 if ($event == 'diag') { 63 require_privs('diag'); 64 65 $step = ($step) ? $step : gps('step'); 66 67 $available_steps = array( 68 'low' => true, 69 'high' => true, 70 'phpinfo' => true, 71 ); 72 73 if ($step && bouncer($step, $available_steps)) { 74 if ($step === 'phpinfo') { 75 phpinfo(); 76 exit; 77 } else { 78 doDiagnostics(); 79 } 80 } else { 81 doDiagnostics(); 82 } 83 } 84 85 /** 86 * Checks if the given Apache module is installed and active. 87 * 88 * @param string $m The module 89 * @return bool|null TRUE on success, NULL or FALSE on error 90 */ 91 92 function apache_module($m) 93 { 94 $modules = @apache_get_modules(); 95 96 if (is_array($modules)) { 97 return in_array($m, $modules); 98 } 99 } 100 101 /** 102 * Lists all database tables used by the Textpattern core. 103 * 104 * Returned tables include prefixes. 105 * 106 * @return array 107 */ 108 109 function list_txp_tables() 110 { 111 $table_names = array(PFX.'textpattern'); 112 $rows = getRows("SHOW TABLES LIKE '".PFX."txp\_%'"); 113 114 foreach ($rows as $row) { 115 $table_names[] = array_shift($row); 116 } 117 118 return $table_names; 119 } 120 121 /** 122 * Checks the status of the given database tables. 123 * 124 * @param array $tables The tables to check 125 * @param string $type Check type, either FOR UPGRADE, QUICK, FAST, MEDIUM, EXTENDED, CHANGED 126 * @param bool $warnings If TRUE, displays warnings 127 * @return array An array of table statuses 128 * @example 129 * print_r( 130 * check_tables(list_txp_tables()) 131 * ); 132 */ 133 134 function check_tables($tables, $type = 'FAST', $warnings = 0) 135 { 136 $msgs = array(); 137 138 foreach ($tables as $table) { 139 $rs = getRows("CHECK TABLE `$table` $type"); 140 if ($rs) { 141 foreach ($rs as $r) { 142 if ($r['Msg_type'] != 'status' and ($warnings or $r['Msg_type'] != 'warning')) { 143 $msgs[] = $table.cs.$r['Msg_type'].cs.$r['Msg_text']; 144 } 145 } 146 } 147 } 148 149 return $msgs; 150 } 151 152 /** 153 * Renders a diagnostics message block. 154 * 155 * @param string $msg The message 156 * @param string $type The message type 157 * @return string HTML 158 * @access private 159 */ 160 161 function diag_msg_wrap($msg, $type = 'e') 162 { 163 $classMap = array( 164 'e' => 'error', 165 'i' => 'information', 166 'w' => 'warning', 167 ); 168 169 $type = isset($classMap[$type]) ? $type : 'e'; 170 171 return span($msg, array('class' => $classMap[$type])); 172 } 173 174 /** 175 * Outputs a diagnostics report. 176 * 177 * This is the main panel. 178 */ 179 180 function doDiagnostics() 181 { 182 global $prefs, $files, $txpcfg, $event, $step, $theme, $DB, $txp_is_dev; 183 extract(get_prefs()); 184 185 $urlparts = parse_url(hu); 186 $mydomain = $urlparts['host']; 187 $path_to_index = $path_to_site."/index.php"; 188 $is_apache = stristr(serverSet('SERVER_SOFTWARE'), 'Apache') || is_callable('apache_get_version'); 189 $real_doc_root = (isset($_SERVER['DOCUMENT_ROOT'])) ? realpath($_SERVER['DOCUMENT_ROOT']) : ''; 190 191 $fail = $notReadable = array(); 192 $now = time(); 193 $heading = gTxt('tab_diagnostics'); 194 $isUpdate = defined('TXP_UPDATE_DONE'); 195 196 if (!$txp_is_dev) { 197 // Check for Textpattern updates, at most once every hour. 198 $lastCheck = json_decode(get_pref('last_update_check', ''), true); 199 200 if ($now > (@(int)$lastCheck['when'] + (60 * 60))) { 201 $lastCheck = checkUpdates(); 202 } 203 204 if (isset($lastCheck['response']) && $lastCheck['response'] === false) { 205 // Problem connecting to update server. 206 $fail['i'][] = array('update_server_inaccessible', $lastCheck['msg']); 207 } else { 208 if (!empty($lastCheck['msg'])) { 209 $txpver = empty($lastCheck['msgval']) ? array('{version}' => '') : $lastCheck['msgval']; 210 $fail['i'][] = array('textpattern_version_update', $lastCheck['msg'], $txpver); 211 } 212 213 if (!empty($lastCheck['msg2'])) { 214 $txpver = empty($lastCheck['msgval2']) ? array('{version}' => '') : $lastCheck['msgval2']; 215 $fail['i'][] = array('textpattern_version_update_beta', $lastCheck['msg2'], $txpver); 216 } 217 } 218 } 219 220 if (!is_callable('version_compare') || version_compare(PHP_VERSION, REQUIRED_PHP_VERSION, '<')) { 221 $fail['e'][] = array('php_version_required', 'php_version_required', array('{version}' => REQUIRED_PHP_VERSION)); 222 } 223 224 if (@gethostbyname($mydomain) === $mydomain) { 225 $fail['w'][] = array('dns_lookup_fails', 'dns_lookup_fails', array('{domain}' => $mydomain)); 226 } 227 228 if (!@is_dir($path_to_site)) { 229 $fail['e'][] = array('path_to_site_inaccessible', 'path_inaccessible', array('{path}' => $path_to_site)); 230 } 231 232 if (rtrim($siteurl, '/') != $siteurl) { 233 $fail['w'][] = array('site_trailing_slash', 'site_trailing_slash', array('{path}' => $path_to_site)); 234 } 235 236 if (!@is_file($path_to_index) || !@is_readable($path_to_index)) { 237 $fail['e'][] = array('index_inaccessible', 'path_inaccessible', array('{path}' => $path_to_index)); 238 } 239 240 if (!@is_writable($path_to_site.DS.$img_dir)) { 241 $notReadable[] = array('{dirtype}' => 'img_dir', '{path}' => $path_to_site.DS.$img_dir); 242 } 243 244 if (!@is_writable($file_base_path)) { 245 $notReadable[] = array('{dirtype}' => 'file_base_path', '{path}' => $file_base_path); 246 } 247 248 if (!@is_writable($path_to_site.DS.$skin_dir)) { 249 $notReadable[] = array('{dirtype}' => 'skin_dir', '{path}' => $path_to_site.DS.$skin_dir); 250 } 251 252 if (!@is_writable($tempdir)) { 253 $notReadable[] = array('{dirtype}' => 'tempdir', '{path}' => $tempdir); 254 } 255 256 if (!@is_writable(PLUGINPATH)) { 257 $notReadable[] = array('{dirtype}' => 'plugin_dir', '{path}' => PLUGINPATH); 258 } 259 260 if ($permlink_mode != 'messy' && $is_apache && !@is_readable($path_to_site.'/.htaccess')) { 261 $fail['e'][] = array('htaccess_missing'); 262 } 263 264 if ($permlink_mode != 'messy' && is_callable('apache_get_modules') && !apache_module('mod_rewrite')) { 265 $fail['e'][] = array('mod_rewrite_missing'); 266 } 267 268 if (!ini_get('file_uploads')) { 269 $fail['i'][] = array('file_uploads_disabled'); 270 } 271 272 if (isset($txpcfg['multisite_root_path'])) { 273 $basePath = $txpcfg['multisite_root_path'].DS.'admin'; 274 275 if (@is_dir($basePath.DS.'setup') && ($txp_is_dev || !Txp::get('\Textpattern\Admin\Tools')->removeFiles($basePath, 'setup'))) { 276 $fail['w'][] = array('setup_still_exists', 'still_exists', array('{path}' => $basePath.DS.'setup'.DS)); 277 } 278 } else { 279 if (@is_dir(txpath.DS.'setup') && ($txp_is_dev || !Txp::get('\Textpattern\Admin\Tools')->removeFiles(txpath, 'setup'))) { 280 $fail['w'][] = array('setup_still_exists', 'still_exists', array('{path}' => txpath.DS.'setup'.DS)); 281 } 282 } 283 284 if (empty($tempdir)) { 285 $fail['w'][] = array('no_temp_dir'); 286 } 287 288 if (is_disabled('mail')) { 289 $fail['e'][] = array('warn_mail_unavailable'); 290 } 291 292 if ($permlink_mode != 'messy') { 293 $rs = safe_column("name", 'txp_section', "1 = 1"); 294 295 foreach ($rs as $name) { 296 if ($name && @file_exists($path_to_site.'/'.$name)) { 297 $fail['e'][] = array('old_placeholder_exists', 'old_placeholder', array('{path}' => $path_to_site.DS.$name)); 298 } 299 } 300 } 301 302 $cs = check_file_integrity(INTEGRITY_REALPATH); 303 304 if (!$cs) { 305 $cs = array(); 306 } 307 308 // Files that don't match their checksums. 309 if (!$txp_is_dev && $modified_files = array_keys($cs, INTEGRITY_MODIFIED)) { 310 $fail['w'][] = array('modified_files', 'modified_files', array('{list}' => n.t.implode(', '.n.t, $modified_files))); 311 } 312 313 // Running development code in live mode is not recommended. 314 if (preg_match('/-dev$/', txp_version) && $production_status == 'live') { 315 $fail['w'][] = array('dev_version_live'); 316 } 317 318 // Missing files. 319 if ($missing = array_merge( 320 array_keys($cs, INTEGRITY_MISSING), 321 array_keys($cs, INTEGRITY_NOT_FILE), 322 array_keys($cs, INTEGRITY_NOT_READABLE) 323 )) { 324 $fail['e'][] = array('missing_files', 'missing_files', array('{list}' => n.t.implode(', '.n.t, $missing))); 325 } 326 327 // Anything might break if arbitrary functions are disabled. 328 if (ini_get('disable_functions')) { 329 $disabled_funcs = do_list_unique(ini_get('disable_functions')); 330 // Commonly disabled functions that we don't need. 331 $disabled_funcs = array_filter(array_diff($disabled_funcs, array( 332 'imagefilltoborder', 333 'escapeshellarg', 334 'escapeshellcmd', 335 'exec', 336 'passthru', 337 'proc_close', 338 'proc_get_status', 339 'proc_nice', 340 'proc_open', 341 'proc_terminate', 342 'shell_exec', 343 'system', 344 'popen', 345 'dl', 346 'chown', 347 )), function($func) {return strpos($func, 'pcntl_') !== 0;}); 348 349 if ($disabled_funcs) { 350 $fail['w'][] = array('some_php_functions_disabled', 'some_php_functions_disabled', array('{list}' => implode(', ', array_filter($disabled_funcs)))); 351 } 352 } 353 354 // Not sure about this one. 355 // if (strncmp(php_sapi_name(), 'cgi', 3) == 0 and ini_get('cgi.rfc2616_headers')) 356 // $fail['cgi_header_config'] = gTxt('cgi_header_config'); 357 358 $guess_site_url = $_SERVER['HTTP_HOST'].preg_replace('#[/\\\\]$#', '', dirname(dirname($_SERVER['SCRIPT_NAME']))); 359 360 if ($siteurl && strip_prefix($siteurl, 'www.') != strip_prefix($guess_site_url, 'www.')) { 361 // Skip warning if multi-site setup, as $guess_site_url and $siteurl will mismatch. 362 if (!isset($txpcfg['multisite_root_path'])) { 363 $fail['w'][] = array('site_url_mismatch', 'site_url_mismatch', array('{url}' => $guess_site_url)); 364 } 365 } 366 367 // Test clean URL server vars. 368 if (hu) { 369 if (ini_get('allow_url_fopen') && ($permlink_mode != 'messy')) { 370 $s = md5(uniqid(rand(), true)); 371 ini_set('default_socket_timeout', 10); 372 373 $pretext_data = @file(hu.$s.'/?txpcleantest=1'); 374 375 if ($pretext_data) { 376 $pretext_req = trim(@$pretext_data[0]); 377 378 if ($pretext_req != md5('/'.$s.'/?txpcleantest=1')) { 379 $fail['w'][] = array('clean_url_data_failed', 'clean_url_data_failed', array('{data}' => txpspecialchars($pretext_req))); 380 } 381 } else { 382 $fail['w'][] = array('clean_url_test_failed'); 383 } 384 } 385 } 386 387 if ($tables = list_txp_tables()) { 388 $table_errors = check_tables($tables); 389 390 if ($table_errors) { 391 $fail['e'][] = array('mysql_table_errors', 'mysql_table_errors', array('{list}' => n.t.implode(', '.n.t, $table_errors))); 392 } 393 } 394 395 $active_plugins = array(); 396 397 if (!$use_plugins && !$admin_side_plugins) { 398 $showTypes = '2'; 399 } elseif (!$admin_side_plugins) { 400 $showTypes = '0, 2, 5'; 401 } elseif (!$use_plugins) { 402 $showTypes = '1, 2, 3, 4, 5'; 403 } else { 404 $showTypes = '0, 1, 2, 3, 4, 5'; 405 } 406 407 if ($rows = safe_rows("name, version, code_md5, MD5(code) AS md5", 'txp_plugin', "status > 0 AND type IN (".$showTypes.") ORDER BY name")) { 408 foreach ($rows as $row) { 409 $n = $row['name'].'-'.$row['version']; 410 411 if (strtolower($row['md5']) != strtolower($row['code_md5'])) { 412 $n .= ' ('.gTxt('diag_modified').')'; 413 } 414 415 $active_plugins[] = $n; 416 } 417 } 418 419 $theme_manifest = $theme->manifest(); 420 421 // Check GD info. 422 if (function_exists('gd_info')) { 423 $gd_info = gd_info(); 424 425 $gd_support = array(); 426 427 if ($gd_info['GIF Create Support']) { 428 $gd_support[] = 'GIF'; 429 } 430 431 if ($gd_info['JPEG Support']) { 432 $gd_support[] = 'JPEG'; 433 } 434 435 if ($gd_info['PNG Support']) { 436 $gd_support[] = 'PNG'; 437 } 438 439 if (isset($gd_info['WebP Support']) && $gd_info['WebP Support']) { 440 $gd_support[] = 'WebP'; 441 } 442 443 if ($gd_support) { 444 $gd_support = implode(', ', $gd_support); 445 } else { 446 $gd_support = gTxt('diag_none'); 447 } 448 449 $gd = gTxt('diag_gd_info', array( 450 '{version}' => $gd_info['GD Version'], 451 '{supported}' => $gd_support, 452 )); 453 } else { 454 $gd = gTxt('diag_unavailable'); 455 } 456 457 if (realpath($prefs['tempdir']) === realpath($prefs['plugin_cache_dir'])) { 458 $fail['e'][] = array('tmp_plugin_paths_match'); 459 } 460 461 // Database server time. 462 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"))); 463 $db_server_timeoffset = $db_server_timestamp - $now; 464 465 echo pagetop(gTxt('tab_diagnostics'), ''); 466 467 echo n.'<div class="txp-layout">'. 468 n.tag( 469 hed($heading, 1, array('class' => 'txp-heading')), 470 'div', array('class' => 'txp-layout-1col') 471 ). 472 n.tag_start('div', array( 473 'class' => 'txp-layout-1col', 474 'id' => $event.'_container', 475 )). 476 n.tag_start('div', array('id' => 'pre_flight_check')). 477 hed(gTxt('preflight_check'), 2); 478 479 $thisLang = get_pref('language_ui', TEXTPATTERN_DEFAULT_LANG); 480 $siteLang = get_pref('language', TEXTPATTERN_DEFAULT_LANG); 481 $langs = array_unique(array('en', $thisLang)); 482 $pfcStrings = array(); 483 $langCounter = 0; 484 $txpLang = Txp::get('\Textpattern\L10n\Lang'); 485 486 foreach ($langs as $lang) { 487 // Overwrite the lang strings to English, then revert on second pass. 488 // This allows the pre-flight check to be displayed in the local 489 // language above the fold, and in English in the textarea. 490 $diagPack = $txpLang->getPack($lang, $event); 491 $diagStrings = array(); 492 $showPophelp = count($langs) === 1 || $langCounter > 0; 493 494 foreach ($diagPack as $key => $packBlock) { 495 $diagStrings[$key] = $packBlock['data']; 496 } 497 498 $txpLang->setPack($diagStrings, true); 499 $not_readable = array(); 500 501 foreach ($notReadable as $strings) { 502 $strings['{dirtype}'] = gTxt($strings['{dirtype}']); 503 $not_readable[] = diag_msg_wrap(gTxt('dir_not_writable', $strings)); 504 } 505 506 if ($fail || $not_readable) { 507 foreach ($fail as $type => $content) { 508 foreach ($content as $stringInfo) { 509 $help = $stringInfo[0]; 510 $message = isset($stringInfo[1]) ? $stringInfo[1] : $help; 511 $args = isset($stringInfo[2]) ? $stringInfo[2] : array(); 512 $pfcStrings[$lang][] = graf(nl2br(diag_msg_wrap(gTxt($message, $args), $type)).($showPophelp ? popHelp($help) : '')); 513 } 514 } 515 516 if ($not_readable) { 517 $pfcStrings[$lang][] = graf(nl2br(join(n, $not_readable).($showPophelp ? popHelp('dir_not_writable') : ''))); 518 } 519 } else { 520 $pfcStrings[$lang][] = graf('<span class="ui-icon ui-icon-check"></span>'.sp.gTxt('all_checks_passed'), array('class' => 'success')); 521 } 522 523 $langCounter++; 524 } 525 526 // The lang will now be back to the local lingo so we can use $lang 527 // to display the correct pre-flight check. 528 echo implode(n, $pfcStrings[$lang]); 529 530 // End of #pre_flight_check. 531 echo n.tag_end('div'); 532 533 $out = array(); 534 535 echo n.tag_start('div', array('id' => 'diagnostics')). 536 hed(gTxt('diagnostic_info'), 2); 537 538 $fmt_date = '%Y-%m-%d %H:%M:%S'; 539 $updateTime = ($dbupdatetime) ? gmstrftime($fmt_date, $dbupdatetime).'/' : ''; 540 541 $dets = array( 542 'low' => gTxt('low'), 543 'high' => gTxt('high'), 544 ); 545 546 $out = array( 547 form( 548 eInput('diag'). 549 href(gTxt('php_diagnostics').sp.span(gTxt('opens_external_link'), array('class' => 'ui-icon ui-icon-extlink')), array( 550 'event' => 'diag', 551 'step' => 'phpinfo', 552 '_txp_token' => form_token(), 553 ), array( 554 'rel' => 'external noopener', 555 'target' => '_blank', 556 )). 557 inputLabel( 558 'diag_detail_level', 559 selectInput('step', $dets, $step, 0, 1, 'diag_detail_level'), 560 'detail', 561 '', 562 array('class' => 'txp-form-field diagnostic-details-level'), 563 '' 564 ). 565 inputLabel( 566 'diag_clear_private', 567 checkbox('diag_clear_private', 1, false, 0, 'diag_clear_private'), 568 'diag_clear_private', 'diag_clear_private', array('class' => 'txp-form-field'), 569 '' 570 ) 571 ), 572 573 '<textarea class="code" id="diagnostics-detail" cols="'.INPUT_LARGE.'" rows="'.TEXTAREA_HEIGHT_LARGE.'" dir="ltr" readonly>', 574 '</textarea>', 575 576 (isset($txpcfg['multisite_root_path'])) 577 ? '<textarea class="code ui-helper-hidden" id="diagnostics-data" cols="'.INPUT_LARGE.'" data-txproot="'.dirname(dirname($txpcfg['multisite_root_path'])).'" dir="ltr" readonly>' 578 : '<textarea class="code ui-helper-hidden" id="diagnostics-data" cols="'.INPUT_LARGE.'" data-txproot="'.dirname(txpath).'" dir="ltr" readonly>', 579 580 gTxt('diag_txp_version').cs.txp_version.' ('.check_file_integrity(INTEGRITY_DIGEST).')'.n, 581 582 gTxt('diag_last_update').cs.$updateTime.gmstrftime($fmt_date, @filemtime(txpath.'/update/_update.php')).n, 583 584 priv.gTxt('diag_web_domain').cs.$siteurl.n, 585 586 (defined('ahu')) ? priv.gTxt('diag_admin_url').cs.rtrim(preg_replace('|^https?://|', '', ahu), '/').n : '', 587 588 (!empty($txpcfg['cookie_domain'])) ? priv.gTxt('diag_cookie_domain').cs.cookie_domain.n : '', 589 590 priv.gTxt('diag_document_root').cs.@$_SERVER['DOCUMENT_ROOT'].(($real_doc_root != @$_SERVER['DOCUMENT_ROOT']) ? ' ('.$real_doc_root.')' : '').n, 591 592 (isset($txpcfg['multisite_root_path'])) ? gTxt('diag_multisite_root_path').cs.$txpcfg['multisite_root_path'].n : '', 593 594 priv.'$path_to_site'.cs.$path_to_site.n, 595 596 gTxt('diag_txp_path').cs.txpath.n, 597 598 gTxt('diag_permlink_mode').cs.$permlink_mode.n, 599 600 gTxt('diag_production_status').cs.$production_status.n, 601 602 (ini_get('open_basedir')) ? 'open_basedir'.cs.ini_get('open_basedir').n : '', 603 604 (ini_get('upload_tmp_dir')) ? 'upload_tmp_dir'.cs.ini_get('upload_tmp_dir').n : '', 605 606 gTxt('diag_tempdir').cs.$tempdir.n, 607 608 gTxt('diag_php_version').cs.phpversion().n, 609 610 gTxt('diag_gd_library').cs.$gd.n, 611 612 gTxt('diag_server_timezone').cs.Txp::get('\Textpattern\Date\Timezone')->getTimeZone().n, 613 614 gTxt('diag_server_time').cs.strftime('%Y-%m-%d %H:%M:%S').n, 615 616 strip_tags(gTxt('diag_is_dst')).cs.$is_dst.n, 617 618 strip_tags(gTxt('diag_auto_dst')).cs.$auto_dst.n, 619 620 strip_tags(gTxt('diag_gmtoffset')).cs.$timezone_key.sp."($gmtoffset)".n, 621 622 'MySQL'.cs.$DB->version.' ('.getThing('SELECT @@GLOBAL.version_comment').') '.n, 623 624 gTxt('diag_db_server_time').cs.$db_server_time.n, 625 626 gTxt('diag_db_server_timeoffset').cs.$db_server_timeoffset.' s'.n, 627 628 gTxt('diag_db_global_timezone').cs.$db_global_timezone.n, 629 630 gTxt('diag_db_session_timezone').cs.$db_session_timezone.n, 631 632 gTxt('diag_locale').cs.$locale.n, 633 634 gTxt('diag_languages', array('{site_lang}' => $siteLang, '{admin_lang}' => $thisLang)).n, 635 636 (isset($_SERVER['SERVER_SOFTWARE'])) ? gTxt('diag_web_server').cs.$_SERVER['SERVER_SOFTWARE'].n : '', 637 638 (is_callable('apache_get_version')) ? gTxt('diag_apache_version').cs.@apache_get_version().n : '', 639 640 gTxt('diag_php_sapi_mode').cs.PHP_SAPI.n, 641 642 gTxt('diag_rfc2616_headers').cs.ini_get('cgi.rfc2616_headers').n, 643 644 gTxt('diag_server_os_version').cs.php_uname('s').' '.php_uname('r').n, 645 646 gTxt('diag_theme_name').cs.$theme_name.sp.@$theme_manifest['version'].n, 647 648 ($active_plugins ? gTxt('diag_active_plugins').cs.n.t.implode(n.t, $active_plugins).n : ''), 649 650 ($fail || $not_readable) 651 ? n.gTxt('diag_preflight_check').cs.n.ln.implode(n, doStripTags($pfcStrings['en'])).n.ln 652 : '', 653 654 ($is_apache && is_readable($path_to_site.'/.htaccess')) 655 ? n.gTxt('diag_htaccess_contents').cs.n.ln.txpspecialchars(implode('', file($path_to_site.'/.htaccess'))).n.ln 656 : '', 657 ); 658 659 if ($step == 'high') { 660 $lastCheck = json_decode(get_pref('last_update_check', ''), true); 661 662 if (!empty($lastCheck['msg']) || !empty($lastCheck['msg2'])) { 663 $relmain = empty($lastCheck['msgval']) ? array('{version}' => '') : $lastCheck['msgval']; 664 $relbeta = empty($lastCheck['msgval2']) ? array('{version}' => '') : $lastCheck['msgval2']; 665 $msgmain = empty($lastCheck['msg']) ? '' : strip_tags(gTxt($lastCheck['msg'], $relmain)); 666 $msgbeta = empty($lastCheck['msg2']) ? '' : strip_tags(gTxt($lastCheck['msg2'], $relbeta)); 667 $out[] = n.gTxt('diag_last_update_check').cs.strftime('%Y-%m-%d %H:%M:%S', $lastCheck['when']).', '.$msgmain.' '.$msgbeta.n; 668 } 669 670 $out[] = n.gTxt('diag_db_charset').cs.$DB->default_charset.'/'.$DB->charset.n; 671 672 $result = safe_query("SHOW variables WHERE Variable_name LIKE 'character_se%' OR Variable_name LIKE 'collation%'"); 673 674 while ($row = mysqli_fetch_row($result)) { 675 $out[] = $row[0].cs.$row[1].n; 676 677 if ($row[0] == 'character_set_connection') { 678 $conn_char = $row[1]; 679 } 680 } 681 682 $table_names = array(PFX.'textpattern'); 683 $result = safe_query("SHOW TABLES LIKE '".PFX."txp\_%'"); 684 685 while ($row = mysqli_fetch_row($result)) { 686 $table_names[] = $row[0]; 687 } 688 689 $table_msg = array(); 690 691 foreach ($table_names as $table) { 692 $ctr = safe_query("SHOW CREATE TABLE $table"); 693 694 if (!$ctr) { 695 unset($table_names[$table]); 696 continue; 697 } 698 699 $row = mysqli_fetch_assoc($ctr); 700 $ctcharset = preg_replace('#^CREATE TABLE.*SET=([^ ]+)[^)]*$#is', '\\1', $row['Create Table']); 701 702 if (isset($conn_char) && !stristr($ctcharset, 'CREATE') && ($conn_char != $ctcharset)) { 703 $table_msg[] = "$table is $ctcharset"; 704 } 705 706 $ctr = safe_query("CHECK TABLE $table"); 707 $row = mysqli_fetch_assoc($ctr); 708 709 if (in_array($row['Msg_type'], array('error', 'warning'))) { 710 $table_msg[] = $table.cs.$row['Msg_Text']; 711 } 712 } 713 714 if ($table_msg == array()) { 715 $table_msg = (count($table_names) < 17) ? array('-') : array('OK'); 716 } 717 718 $out[] = count($table_names).sp.gTxt('diag_db_tables').cs.implode(', ', $table_msg).n; 719 720 $cf = preg_grep('/^custom_\d+/', getThings("DESCRIBE `".PFX."textpattern`")); 721 $out[] = n.get_pref('max_custom_fields', 10).sp.gTxt('diag_custom').cs. 722 implode(', ', $cf).sp.'('.count($cf).')'.n; 723 724 $extns = get_loaded_extensions(); 725 $extv = array(); 726 727 foreach ($extns as $e) { 728 $extv[] = $e.(phpversion($e) ? '/'.phpversion($e) : ''); 729 } 730 731 if (is_callable('apache_get_modules')) { 732 $out[] = n.gTxt('diag_apache_modules').cs.implode(', ', apache_get_modules()).n; 733 } 734 735 if (@is_array($pretext_data) and count($pretext_data) > 1) { 736 $out[] = n.gTxt('diag_pretext_data').cs.txpspecialchars(implode('', array_slice($pretext_data, 1, 20))).n; 737 } 738 739 $out[] = n; 740 741 if ($md5s = check_file_integrity(INTEGRITY_MD5)) { 742 foreach ($md5s as $f => $checksum) { 743 $out[] = $f.cs.n.t.(!$checksum ? gTxt('diag_unknown') : $checksum).n; 744 } 745 } 746 747 $out[] = n.ln; 748 } 749 750 $out[] = callback_event('diag_results', $step).n; 751 $out[] = '</textarea>'; 752 753 echo implode('', $out), 754 n.tag_end('div'). // End of #diagnostics. 755 n.tag_end('div'). // End of .txp-layout-1col. 756 n.'</div>'; // End of .txp-layout.; 757 } 758 759 /** 760 * Checks for Textpattern updates. 761 * 762 * @return array|null When updates are found returns an array consisting keys 'version', 'msg' 763 * @example 764 * if ($updates = checkUpdates()) 765 * { 766 * echo "New version: {$updates['version']}"; 767 * } 768 */ 769 770 function checkUpdates() 771 { 772 $endpoint = 'https://textpattern.com/version.json'; 773 774 if (function_exists('curl_version')) { 775 $ch = curl_init($endpoint); 776 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 777 $contents = curl_exec($ch); 778 } else { 779 $contents = file_get_contents($endpoint); 780 } 781 782 $response = @json_decode($contents, true); 783 $release = @$response['textpattern-version']['release']; 784 $prerelease = @$response['textpattern-version']['prerelease']; 785 $version = get_pref('version'); 786 787 $lastCheck = array( 788 'when' => time(), 789 'msg' => '', 790 'msg2' => '', 791 'msgval' => array(), 792 'msgval2' => array(), 793 'response' => true, 794 ); 795 796 if (!empty($release)) { 797 if (version_compare($version, $release) < 0) { 798 $lastCheck['msg'] = 'textpattern_update_available'; 799 $lastCheck['msgval'] = array('{version}' => $release); 800 } 801 802 if (version_compare($version, $prerelease) < 0) { 803 $lastCheck['msg2'] = 'textpattern_update_available_beta'; 804 $lastCheck['msgval2'] = array('{version}' => $prerelease); 805 } 806 } else { 807 $lastCheck['msg'] = 'problem_connecting_update_server'; 808 $lastCheck['msgval'] = array(); 809 $lastCheck['response'] = false; 810 } 811 812 set_pref('last_update_check', json_encode($lastCheck, TEXTPATTERN_JSON), 'publish', PREF_HIDDEN, 'text_input'); 813 814 return $lastCheck; 815 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title