diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..f595bd9d --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.claude/ +docs/ +phpstan.neon +phpstan-baseline.neon diff --git a/ProcessTracyAdminer.module.php b/ProcessTracyAdminer.module.php index f8c65356..6dcca7b7 100644 --- a/ProcessTracyAdminer.module.php +++ b/ProcessTracyAdminer.module.php @@ -1,4 +1,4 @@ - __('Adminer page for TracyDebugger.', __FILE__), 'author' => 'Adrian Jones', 'href' => 'https://processwire.com/talk/topic/12208-tracy-debugger/', - 'version' => '2.0.3', + 'version' => '2.0.4', 'autoload' => false, 'singular' => true, 'icon' => 'database', - 'requires' => 'ProcessWire>=2.7.2, PHP>=5.4.4, TracyDebugger, ProcessTracyAdminerRenderer', + 'requires' => 'ProcessWire>=3.0.0, PHP>=7.1.0, TracyDebugger, ProcessTracyAdminerRenderer', 'installs' => array('ProcessTracyAdminerRenderer'), 'page' => array( 'name' => 'adminer', @@ -30,8 +30,8 @@ public function ___execute() { } else { return ' - - ", $event->return); + $event->return = str_replace("", "window.HttpRootUrl = '".$this->wire('config')->urls->httpRoot."'; window.AdminerUrl = '".$adminerUrl."'; window.AdminerRendererUrl = '".$adminerRendererUrl."'; window.TracyMaxAjaxRows = ".$this->data['maxAjaxRows']."; window.TracyPanelZIndex = " . ($this->data['panelZindex'] + 1) . "; window.TracyColorWarn = '" . TracyDebugger::COLOR_WARN . "'; document.addEventListener('click', function(e) { var el = e.target; while(el && el.tagName !== 'A') el = el.parentNode; if(el && el.href && /^(adminer|tracy|tracyexception):\/\//.test(el.href)) e.preventDefault(); }, true);", $event->return); $tracyWarnings = Debugger::getBar()->getPanel('Tracy:warnings') ? Debugger::getBar()->getPanel('Tracy:warnings') : Debugger::getBar()->getPanel('Tracy:errors'); if(!is_array($tracyWarnings->data) || count($tracyWarnings->data) === 0) { if(($this->data['hideDebugBar'] && !$this->wire('input')->cookie->tracyShow) || $this->wire('input')->cookie->tracyHidden == 1) { $hideBar = ' - '; + }); } + Debugger::getBlueScreen()->maxDepth = $this->data['maxDepth']; Debugger::getBlueScreen()->maxLength = $this->data['maxLength']; Debugger::getBlueScreen()->maxItems = $this->data['maxItems']; @@ -1534,11 +1494,13 @@ function bsZIndex() { // ENABLE TRACY if($this->tracyEnabled) { - if($this->data['use_php_session'] === 1 && !$this->wire('modules')->isInstalled('SessionHandlerDB') && self::$tracyVersion != '2.7.x' && self::$tracyVersion != '2.5.x') { + if($this->data['use_php_session'] === 1 && !$this->wire('modules')->isInstalled('SessionHandlerDB') && self::$tracyVersion != '2.7.x') { if (session_status() === PHP_SESSION_NONE) { session_start(); } - Debugger::setSessionStorage(new Tracy\NativeSession); + if(method_exists(Debugger::class, 'setSessionStorage')) { + Debugger::setSessionStorage(new \Tracy\NativeSession); + } } Debugger::enable($outputMode, $logFolder, $this->data['fromEmail'] != '' && $this->data['email'] != '' ? $this->data['email'] : null); @@ -1571,7 +1533,7 @@ function bsZIndex() { } // fixes for when SessionHandlerDB module is installed - if(($this->data['use_php_session'] === 1 && !$this->wire('modules')->isInstalled('SessionHandlerDB')) || self::$tracyVersion == '2.7.x' || self::$tracyVersion == '2.5.x') { + if(($this->data['use_php_session'] === 1 && !$this->wire('modules')->isInstalled('SessionHandlerDB')) || self::$tracyVersion == '2.7.x') { if($this->wire('modules')->isInstalled('SessionHandlerDB') && Debugger::$showBar) { // ensure Tracy can show AJAX bars when SessionHandlerDB module is installed and debugbar is showing @@ -1593,6 +1555,9 @@ function bsZIndex() { } } + // PW VERSION SWITCHER — marker cleanup (must be at the VERY END of init) + PwVersionSwitcher::cleanupSwitchMarker($this); + } @@ -1601,6 +1566,9 @@ function bsZIndex() { */ public function ready() { + // PW VERSION SWITCHER — show auto-revert notification + PwVersionSwitcher::showRevertNotification($this); + // USER BAR PAGE VERSIONS if(!static::$inAdmin && !$this->wire('config')->ajax && $this->wire('user')->isLoggedin() && $this->wire('user')->hasPermission('tracy-page-versions') && $this->data['showUserBar'] && in_array('pageVersions', $this->data['userBarFeatures'])) { static::$pageVersion = $this->findPageTemplateInCookie($this->wire('input')->cookie->tracyPageVersion); @@ -1662,64 +1630,7 @@ public function ready() { // FILE/TEMPLATE EDITOR - if(static::$allowedSuperuser || self::$validLocalUser || self::$validSwitchedUser) { - if($this->wire('input')->post->fileEditorFilePath) { - $rawCode = base64_decode($this->wire('input')->post->tracyFileEditorRawCode); - if(static::$inAdmin && - $this->data['referencePageEdited'] && - $this->wire('input')->get('id') && - $this->wire('pages')->get($this->wire('input')->get('id'))->template->filename === $this->wire('config')->paths->root . $this->wire('input')->post->fileEditorFilePath - ) { - $p = $this->wire('pages')->get($this->wire('input')->get('id')); - } - else { - $p = $this->wire('page'); - } - - $templateExt = pathinfo($p->template->filename, PATHINFO_EXTENSION); - $this->tempTemplateFilename = str_replace('.'.$templateExt, '-tracytemp.'.$templateExt, $p->template->filename); - // if changes to the template of the current page are submitted - // test - if($this->wire('input')->post->tracyTestTemplateCode) { - if(!$this->wire('files')->filePutContents($this->tempTemplateFilename, $rawCode, LOCK_EX)) { - throw new WireException("Unable to write file: " . $this->tempTemplateFilename); - } - $p->template->filename = $this->tempTemplateFilename; - } - - // if changes to any other file are submitted - if($this->wire('input')->post->tracyTestFileCode || $this->wire('input')->post->tracySaveFileCode || $this->wire('input')->post->tracyChangeTemplateCode) { - if($this->wire('input')->post->fileEditorFilePath != '' && strpos($this->wire('input')->post->fileEditorFilePath, '..') === false) { - $filePath = $this->wire('config')->paths->root . $this->wire('input')->post->fileEditorFilePath; - $rawCode = base64_decode($this->wire('input')->post->tracyFileEditorRawCode); - - // backup old version to Tracy cache directory - $cachePath = $this->tracyCacheDir . $this->wire('input')->post->fileEditorFilePath; - if(!is_dir($cachePath)) if(!wireMkdir(pathinfo($cachePath, PATHINFO_DIRNAME), true)) { - throw new WireException("Unable to create cache path: $cachePath"); - } - copy($filePath, $cachePath); - - if(!$this->wire('files')->filePutContents($filePath, $rawCode, LOCK_EX)) { - throw new WireException("Unable to write file: " . $filePath); - } - if($this->wire('config')->chmodFile) chmod($filePath, octdec($this->wire('config')->chmodFile)); - - if($this->wire('input')->post->tracyTestFileCode) setcookie('tracyTestFileEditor', $this->wire('input')->post->fileEditorFilePath, time() + (10 * 365 * 24 * 60 * 60), '/'); - } - $this->wire('session')->redirect($this->httpReferer); - } - } - - // if file editor restore - if($this->wire('input')->post->tracyRestoreFileEditorBackup) { - $this->filePath = $this->wire('config')->paths->root . ($this->wire('input')->post->fileEditorFilePath ?: $this->wire('input')->cookie->tracyTestFileEditor); - $this->cachePath = $this->tracyCacheDir . ($this->wire('input')->post->fileEditorFilePath ?: $this->wire('input')->cookie->tracyTestFileEditor); - copy($this->cachePath, $this->filePath); - unlink($this->cachePath); - $this->wire('session')->redirect($this->httpReferer); - } - } + require_once(__DIR__ . '/includes/post_processors/FileEditorPost.php'); // DEBUG BAR & PANELS @@ -1743,9 +1654,9 @@ public function ready() { // check for any "bd_" wire variables and barDump them - $pwVars = function_exists('wire') ? $this->fuel : \ProcessWire\wire('all'); + $pwVars = $this->wire('all'); foreach($pwVars->getArray() as $key => $val) { - if(strpos($key, 'bd_') !== false) \TD::barDump($val, '$'.$key); + if(strpos($key, 'bd_') !== false) TD::barDump($val, '$'.$key); } @@ -1764,12 +1675,21 @@ public function ready() { if(!empty(static::$restrictedUserDisabledPanels) && in_array($panel, static::$restrictedUserDisabledPanels)) continue; $panelName = ucfirst($panel).'Panel'; + $fullPanelClass = __NAMESPACE__ . '\\' . $panelName; if(file_exists(__DIR__ . '/panels/'.$panelName.'.php')) { require_once __DIR__ . '/panels/'.$panelName.'.php'; + // namespace migration: bridge old non-namespaced panel class + if(!class_exists($fullPanelClass, false) && class_exists($panelName, false)) { + class_alias($panelName, $fullPanelClass); + } } else { // external panels include_once static::$externalPanels[$panel]; + // namespace migration: bridge old non-namespaced external panel class + if(!class_exists($fullPanelClass, false) && class_exists($panelName, false)) { + class_alias($panelName, $fullPanelClass); + } } switch($panel) { case 'performance': @@ -1798,136 +1718,42 @@ public function ready() { // auto appended/prepended files $this->replaceAutoAppendedPreprendedTemplateFiles($this->getFileSuffix(static::$templatePath)); } - Debugger::getBar()->addPanel(new $panelName); + Debugger::getBar()->addPanel(new $fullPanelClass); break; default: - Debugger::getBar()->addPanel(new $panelName); + Debugger::getBar()->addPanel(new $fullPanelClass); break; } } // load custom replacement Dumps panel - this is not optional/configurable // at the end so it has access to bd() calls in any of the other panels - helpful for debugging these panels $panelName = 'DumpsPanel'; + $fullPanelClass = __NAMESPACE__ . '\\' . $panelName; require_once __DIR__ . '/panels/'.$panelName.'.php'; - Debugger::getBar()->addPanel(new $panelName); + if(!class_exists($fullPanelClass, false) && class_exists($panelName, false)) { + class_alias($panelName, $fullPanelClass); + } + Debugger::getBar()->addPanel(new $fullPanelClass); - // even if dumpsRecorder isn't enabled, but there are recorded dumps to show, enable it anyway - if(!in_array('dumpsRecorder', static::$showPanels) && file_exists($this->wire('config')->paths->cache . 'TracyDebugger/dumps.json')) { + // even if dumpsRecorder isn't enabled, but there are recorded dumps to show or guest dumps is active, enable it anyway + if(!in_array('dumpsRecorder', static::$showPanels) && ($this->data['recordGuestDumps'] || file_exists($this->wire('config')->paths->cache . 'TracyDebugger/dumps.json'))) { $panelName = 'DumpsRecorderPanel'; + $fullPanelClass = __NAMESPACE__ . '\\' . $panelName; static::$showPanels[] = 'dumpsRecorder'; require_once __DIR__ . '/panels/'.$panelName.'.php'; - Debugger::getBar()->addPanel(new $panelName); - } - - - // USER SWITCHER - // process userSwitcher if panel open and switch initiated - if(in_array('userSwitcher', static::$showPanels) && $this->wire('input')->post->userSwitcher) { - // if user is superuser and session length is set, save to config settings - if(static::$allowedSuperuser && ($this->wire('input')->post->userSwitcher || $this->wire('input')->post->logoutUserSwitcher) && $this->wire('session')->CSRF->validate()) { - // cleanup expired sessions - if(isset($this->data['userSwitchSession'])) { - foreach($this->data['userSwitchSession'] as $id => $expireTime) { - if($expireTime < time()) unset($this->data['userSwitchSession'][$id]); - } - } - // if no existing session ID, start a new session - if(!$this->wire('session')->tracyUserSwitcherId) { - $pass = new Password(); - $challenge = $pass->randomBase64String(32); - $this->wire('session')->tracyUserSwitcherId = $challenge; - - $configData = $this->wire('modules')->getModuleConfigData("TracyDebugger"); - $this->data['originalUserSwitcher'][$this->wire('session')->tracyUserSwitcherId] = $this->wire('user')->name; - $configData['originalUserSwitcher'] = $this->data['originalUserSwitcher']; - $this->wire('modules')->saveModuleConfigData($this, $configData); - - } - // save session ID and expiry time in module config settings - $configData = $this->wire('modules')->getModuleConfigData("TracyDebugger"); - $this->data['userSwitchSession'][$this->wire('session')->tracyUserSwitcherId] = time() + $this->wire('config')->sessionExpireSeconds; - $configData['userSwitchSession'] = $this->data['userSwitchSession']; - $this->wire('modules')->saveModuleConfigData($this, $configData); - } - - // if logout button clicked - if($this->wire('input')->post->logoutUserSwitcher && $this->wire('session')->CSRF->validate()) { - if($this->wire('session')->tracyUserSwitcherId) { - // if session variable exists, grab it and add to the new session after logging out - $tracyUserSwitcherId = $this->wire('session')->tracyUserSwitcherId; - $this->wire('session')->logout(); - $this->wire('session')->tracyUserSwitcherId = $tracyUserSwitcherId; - } - else { - $this->wire('session')->logout(); - } - $this->wire('session')->redirect($this->httpReferer); - } - // if end session clicked, remove session variable and config settings entry - elseif($this->wire('input')->post->endSessionUserSwitcher && $this->wire('session')->CSRF->validate()) { - $this->wire('session')->remove("tracyUserSwitcherId"); - $configData = $this->wire('modules')->getModuleConfigData("TracyDebugger"); - unset($this->data['userSwitchSession'][$this->wire('session')->tracyUserSwitcherId]); - unset($configData['userSwitchSession'][$this->wire('session')->tracyUserSwitcherId]); - $this->wire('modules')->saveModuleConfigData($this, $configData); - $this->wire('session')->redirect($this->httpReferer); - } - // if session not expired, switch to original user - elseif($this->wire('input')->post->revertOriginalUserSwitcher && $this->wire('session')->CSRF->validate()) { - if(isset($this->data['userSwitchSession'][$this->wire('session')->tracyUserSwitcherId]) && $this->data['userSwitchSession'][$this->wire('session')->tracyUserSwitcherId] > time() && $this->wire('session')->tracyUserSwitcherId) { - // if session variable exists, grab it and add to the new session after logging out - // and forceLogin the original user - $tracyUserSwitcherId = $this->wire('session')->tracyUserSwitcherId; - if($this->wire('user')->isLoggedin()) $this->wire('session')->logout(); - $this->wire('session')->forceLogin($this->data['originalUserSwitcher'][$tracyUserSwitcherId]); - $this->wire('session')->tracyUserSwitcherId = $tracyUserSwitcherId; - } - $this->wire('session')->redirect($this->httpReferer); - } - // if session not expired, switch to requested user - elseif($this->wire('input')->post->userSwitcher && $this->wire('session')->CSRF->validate()) { - if(isset($this->data['userSwitchSession'][$this->wire('session')->tracyUserSwitcherId]) && $this->data['userSwitchSession'][$this->wire('session')->tracyUserSwitcherId] > time() && $this->wire('session')->tracyUserSwitcherId) { - // if session variable exists, grab it and add to the new session after logging out - // and forceLogin the new switched user - $tracyUserSwitcherId = $this->wire('session')->tracyUserSwitcherId; - if($this->wire('user')->isLoggedin()) $this->wire('session')->logout(); - $this->wire('session')->forceLogin($this->wire('input')->post->userSwitcher); - $this->wire('session')->tracyUserSwitcherId = $tracyUserSwitcherId; - } - $this->wire('session')->redirect($this->httpReferer); + if(!class_exists($fullPanelClass, false) && class_exists($panelName, false)) { + class_alias($panelName, $fullPanelClass); } + Debugger::getBar()->addPanel(new $fullPanelClass); } - // LANGUAGE SWITCHER PANEL - // process languageSwitcher if panel open and switch initiated - if(in_array('languageSwitcher', static::$showPanels) && ($this->wire('input')->post->tracyLanguageSwitcher || $this->wire('session')->tracyLanguageSwitcher)) { - $langId = $this->wire('input')->post->int('tracyLanguageSwitcher'); - if($langId) { - // compare language setting from session with users profile - if($this->wire('user')->language->id === $langId) { - // language is users profile language -> reset session - $this->wire('session')->remove('tracyLanguageSwitcher'); - } - else { - // language is different from profile -> save it - $this->wire('session')->set('tracyLanguageSwitcher', $langId); - } - } + // USER SWITCHER + require_once(__DIR__ . '/includes/post_processors/UserSwitcherPost.php'); - // reset cache for nav - // thx @toutouwai https://github.com/Toutouwai/CustomAdminMenus/blob/8dfdfa7d07c40ab2d93e3191d2d960e317738169/CustomAdminMenus.module#L35 - $this->wire('session')->removeFor('AdminThemeUikit', 'prnav'); - $this->wire('session')->removeFor('AdminThemeUikit', 'sidenav'); - if($this->wire('input')->post->tracyResetLanguageSwitcher) { - $this->wire('session')->remove('tracyLanguageSwitcher'); - } - // set users language dynamically from session value - elseif($sessionLangId = $this->wire('session')->get('tracyLanguageSwitcher')) { - $this->wire('user')->language = $this->wire('languages')->get($sessionLangId); - } - } + // LANGUAGE SWITCHER + require_once(__DIR__ . '/includes/post_processors/LanguageSwitcherPost.php'); // if it's an ajax request from the Tracy Console panel snippets, then process and return @@ -1992,13 +1818,16 @@ protected function addEnableButton($event) { height: 16px !important; } - -
+
wire('input')->cookie->tracyIncludeCode, true); - if($options['when'] !== $when) return; + if(!is_array($options) || !isset($options['when']) || $options['when'] !== $when) return; // populate API variables, eg so $page equals $this->wire('page') - $pwVars = function_exists('wire') ? $this->fuel : \ProcessWire\wire('all'); + $pwVars = $this->wire('all'); foreach($pwVars->getArray() as $key => $value) { $$key = $value; } - $page = $pages->get((int)$options['pid']); + $page = $this->wire('pages')->get((int)$options['pid']); $consoleCodeFile = $this->tracyCacheDir . 'consoleCode.php'; if(file_exists($consoleCodeFile)) { require_once($consoleCodeFile); @@ -2498,7 +2339,7 @@ public function logRequests(HookEvent $event) { * get a standardized array or object of the current request * * @param HookEvent $event - * @return array | object + * @return void */ public function getRequestData($event) { $page = $event->object; @@ -2695,7 +2536,7 @@ public static function isJson($string) { public static function templateVars($vars) { $pwVars = array('fuel','options'); - foreach(wire('config')->version >= 2.8 ? wire('fuel') : wire()->fuel as $key => $value) { + foreach(wire('fuel') as $key => $value) { if(!is_object($value)) continue; $pwVars[] = $key; } @@ -2795,6 +2636,18 @@ public static function forwardSlashPath($path) { } + /** + * Strip the site root path from an absolute path. + * + * @param string $path The absolute file path + * @param string $prefix What to replace the root with (default '/') + * @return string + */ + public static function stripRootPath($path, $prefix = '/') { + return str_replace(wire('config')->paths->root, $prefix, $path); + } + + /** * Insert generated time and size for a panel * @@ -2885,7 +2738,7 @@ public static function getDataValue($property) { return self::$_data->$property; } else { - return trim(self::$_data->$property); + return trim(self::$_data->$property ?? ''); } } @@ -3174,33 +3027,20 @@ public static function arrayDiffAssocMultidimensional(array $array1, array $arra } - /** - * Renames files and folders appending -$n as required for the Processwire Versions panel. - * - * @param string Old Path - * @param string New Path - * - */ - public function renamePwVersions($oldPath, $newPath, $addN = false) { - if(file_exists($newPath) && $addN) { - $n = 0; - do { $newPath2 = $newPath . "-" . (++$n); } while(file_exists($newPath2)); - rename($newPath, $newPath2); - } - rename($oldPath, $newPath); - } - - public function __destruct() { // if using Test mode in File Editor on regular files, then immediately replace loaded file with backed up version // this is here instead of ProcessWire::finished because this works if test version has fatal error - if(isset($_COOKIE['tracyTestFileEditor'])) { - $this->filePath = $this->wire('config')->paths->root . ($this->wire('input')->post->fileEditorFilePath ?: $this->wire('input')->cookie->tracyTestFileEditor); - $this->cachePath = $this->tracyCacheDir . ($this->wire('input')->post->fileEditorFilePath ?: $this->wire('input')->cookie->tracyTestFileEditor); - if(file_exists($this->cachePath)) { - copy($this->cachePath, $this->filePath); - unlink($this->cachePath); + if(isset($_COOKIE['tracyTestFileEditor']) && (static::$allowedSuperuser || self::$validLocalUser || self::$validSwitchedUser)) { + $rootPath = $this->wire('config')->paths->root; + $editorPath = $this->wire('input')->post->fileEditorFilePath ?: $this->wire('input')->cookie->tracyTestFileEditor; + $resolvedFilePath = str_replace('\\', '/', realpath($rootPath . $editorPath)); + $resolvedCachePath = str_replace('\\', '/', realpath($this->tracyCacheDir . $editorPath)); + if($resolvedFilePath !== false && strpos($resolvedFilePath, $rootPath) === 0 && + $resolvedCachePath !== false && strpos($resolvedCachePath, $this->tracyCacheDir) === 0 && + file_exists($resolvedCachePath)) { + copy($resolvedCachePath, $resolvedFilePath); + unlink($resolvedCachePath); } } @@ -3208,28 +3048,11 @@ public function __destruct() { // this is from the File Editor panel if(isset($this->tempTemplateFilename) && file_exists($this->tempTemplateFilename)) unlink($this->tempTemplateFilename); - // modify paths to /wire/, index.php, and .htaccess when using PW Version Switcher - if($this->wire('input') && $this->wire('input')->post->tracyPwVersion && $this->wire('input')->post->tracyPwVersion != $this->wire('config')->version) { - - $rootPath = $this->wire('config')->paths->root; - - // rename wire - $this->renamePwVersions($rootPath.'wire', $rootPath.'.wire-'.$this->wire('config')->version, true); - $this->renamePwVersions($rootPath.'.wire-'.$this->wire('input')->post->tracyPwVersion, $rootPath.'wire'); - - // rename .htaccess if previously replaced - if(file_exists($rootPath.'.htaccess-'.$this->wire('input')->post->tracyPwVersion)) { - $this->renamePwVersions($rootPath.'.htaccess', $rootPath.'.htaccess-'.$this->wire('config')->version, true); - $this->renamePwVersions($rootPath.'.htaccess-'.$this->wire('input')->post->tracyPwVersion, $rootPath.'.htaccess'); - } - // rename index.php if previously replaced - if(file_exists($rootPath.'.index-'.$this->wire('input')->post->tracyPwVersion.'.php')) { - $this->renamePwVersions($rootPath.'index.php', $rootPath.'.index-'.$this->wire('config')->version.'.php', true); - $this->renamePwVersions($rootPath.'.index-'.$this->wire('input')->post->tracyPwVersion.'.php', $rootPath.'index.php'); - } - } + // PW VERSION SWITCHER — perform file swap + PwVersionSwitcher::performFileSwap($this); } + /** * Prepare an individual link for the Links panel * @@ -3571,16 +3394,6 @@ public function getModuleConfigInputfields(array $data) { $f->attr('value', $data['reservedMemorySize']); $fieldset->add($f); - if(method_exists('Tracy\Debugger', 'getFireLogger')) { - $f = $this->wire('modules')->get("InputfieldCheckbox"); - $f->attr('name', 'showFireLogger'); - $f->label = __('Send data to FireLogger', __FILE__); - $f->description = __('When checked, certain errors and `fl()` calls will be sent to FireLogger in the browser console.', __FILE__); - $f->notes = __('If you are running on nginx and don\'t have access to adjust `fastcgi_buffers` and `fastcgi_buffer_size` settings, you may want to uncheck this to avoid 502 bad gateway errors because of `upstream sent too big header while reading response header from upstream` issues.', __FILE__); - $f->attr('checked', $data['showFireLogger'] == '1' ? 'checked' : ''); - $fieldset->add($f); - } - $f = $this->wire('modules')->get("InputfieldCheckbox"); $f->attr('name', 'referencePageEdited'); $f->label = __('Reference page being edited', __FILE__); @@ -4378,10 +4191,10 @@ public function getModuleConfigInputfields(array $data) { if($data['todoSpecifiedDirectories']) $f->attr('value', $data['todoSpecifiedDirectories']); $fieldset->add($f); - // ProcessWire and Tracy Log Panels + // Log & Exceptions Panels $fieldset = $this->wire('modules')->get("InputfieldFieldset"); - $fieldset->attr('name+id', 'processwireAndTracyLogsPanels'); - $fieldset->label = __('ProcessWire and Tracy Log panels', __FILE__); + $fieldset->attr('name+id', 'logExceptionsPanels'); + $fieldset->label = __('Log and Exceptions panels', __FILE__); $wrapper->add($fieldset); $f = $this->wire('modules')->get("InputfieldAsmSelect"); @@ -4404,10 +4217,10 @@ public function getModuleConfigInputfields(array $data) { $f->notes = __('Useful if you have logs that are written to regularly on user interaction that are overwhelming more useful alert/warning/error logs.', __FILE__); $f->columnWidth = 50; $f->setAsmSelectOption('sortable', false); - if(!class_exists('\TracyLogsPanel')) { + if(!class_exists(TracyLogsPanel::class)) { require_once($this->config->paths->siteModules . 'TracyDebugger/panels/TracyLogsPanel.php'); } - foreach((new \TracyLogsPanel())->getLogs() as $k => $v) { + foreach((new TracyLogsPanel())->getLogs() as $k => $v) { $f->addOption($k); } if($data['excludedTracyLogFiles']) $f->attr('value', $data['excludedTracyLogFiles']); @@ -4418,9 +4231,19 @@ public function getModuleConfigInputfields(array $data) { $f->label = __('Number of log entries', __FILE__); $f->description = __('Set the number of log entries to be displayed for the Tracy and ProcessWire log viewer panels.', __FILE__); $f->notes = __('Default: 100', __FILE__); + $f->columnWidth = 50; $f->attr('value', $data['numLogEntries']); $fieldset->add($f); + $f = $this->wire('modules')->get("InputfieldInteger"); + $f->attr('name', 'numExceptions'); + $f->label = __('Number of exceptions', __FILE__); + $f->description = __('Set the number of exceptions to be displayed for the Tracy Exceptions panel.', __FILE__); + $f->notes = __('Default: 25', __FILE__); + $f->columnWidth = 50; + $f->attr('value', $data['numExceptions']); + $fieldset->add($f); + // Template Resources Panel $fieldset = $this->wire('modules')->get("InputfieldFieldset"); $fieldset->attr('name+id', 'templateResourcesPanel'); @@ -4760,10 +4583,6 @@ public function getModuleConfigInputfields(array $data) { $f->addOption('d', 'd() for TD::dump()'); $f->addOption('dumpBig', 'dumpBig() for TD::dumpBig()'); $f->addOption('db', 'db() for TD::dumpBig()'); - if(method_exists('Tracy\Debugger', 'getFireLogger')) { - $f->addOption('fireLog', 'fireLog() for TD::fireLog()'); - $f->addOption('fl', 'fl() for TD::fireLog()'); - } $f->addOption('l', 'l() for TD::log()'); $f->addOption('templateVars', 'templateVars() for TD::templateVars()'); $f->addOption('tv', 'tv() for TD::templateVars()'); @@ -4846,10 +4665,15 @@ public function getModuleConfigInputfields(array $data) { foreach(static::$externalPanels as $name => $path) { $className = ucfirst($name) . 'Panel'; - if(!class_exists($className)) { + $fullClassName = __NAMESPACE__ . '\\' . $className; + if(!class_exists($fullClassName)) { include_once $path; + // namespace migration: bridge old non-namespaced external panel class + if(!class_exists($fullClassName, false) && class_exists($className, false)) { + class_alias($className, $fullClassName); + } } - $externalPanel = new $className; + $externalPanel = new $fullClassName; if(method_exists($externalPanel, 'addSettings')) { $externalPanelSettings = $externalPanel->addSettings(); $wrapper->add($externalPanelSettings); diff --git a/composer.json b/composer.json index a252cdab..0c9b585c 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,7 @@ } ], "require": { - "php": ">=5.4.4", + "php": ">=7.1.0", "wireframe-framework/processwire-composer-installer": "^1.0.0" } } \ No newline at end of file diff --git a/includes/AceKeyboardShortcuts.php b/includes/AceKeyboardShortcuts.php index 3d410f60..1b62f6ba 100644 --- a/includes/AceKeyboardShortcuts.php +++ b/includes/AceKeyboardShortcuts.php @@ -1,4 +1,4 @@ - @@ -93,4 +93,4 @@ Font Size Decrease -'; \ No newline at end of file +'; diff --git a/includes/BasePanel.php b/includes/BasePanel.php index 40cea6fc..d9aab4e1 100644 --- a/includes/BasePanel.php +++ b/includes/BasePanel.php @@ -1,11 +1,138 @@ -' . $this->icon + . (TracyDebugger::getDataValue('showPanelLabels') ? ' ' . $label : '') + . $badge . ''; } -} \ No newline at end of file + /** + * Build the panel header (h1 + optional resize button). + * + * @param string $title The panel title text + * @param bool $showResize Whether to include the maximize/restore button + * @param bool $showAdditionalBar Whether to append the additional bar identifier + * @return string + */ + protected function buildPanelHeader($title, $showResize = false, $showAdditionalBar = false) { + $isAdditionalBar = $showAdditionalBar ? TracyDebugger::isAdditionalBar() : false; + $out = '

' . $this->icon . ' ' . $title + . ($isAdditionalBar ? ' (' . $isAdditionalBar . ')' : '') . '

'; + if($showResize) { + $className = (new \ReflectionClass($this))->getShortName(); + $out .= '' + . ''; + } + return $out; + } + + /** + * Open the panel inner div. + * + * @param string $extraClass Additional CSS class(es) + * @param string $style Inline style string + * @return string + */ + protected function openPanel($extraClass = '', $style = '') { + return '
'; + } + + /** + * Close the panel with footer and closing div. + * + * @param string $out The accumulated panel HTML (used for size calculation) + * @param string $panelName The panel identifier for timer/footer + * @param string|null $settingsFieldsetId Optional settings fieldset link ID + * @return string The complete panel HTML ready to return from getPanel() + */ + protected function closePanel($out, $panelName, $settingsFieldsetId = null) { + $out .= TracyDebugger::generatePanelFooter($panelName, Debugger::timer($panelName), strlen($out), $settingsFieldsetId); + $out .= '
'; + return $out; + } + + /** + * Generate a CSRF hidden input field. + * + * @return string + */ + protected function csrfInput() { + return ''; + } + + /** + * Get the reference page (edited page in admin, or current page on frontend). + * + * @param array|null $processTypes Process class names to check (defaults to common set) + * @return Page + */ + protected function getReferencePage($processTypes = null) { + if($processTypes === null) { + $processTypes = array('ProcessPageEdit', 'ProcessUser', 'ProcessRole', 'ProcessPermission', 'ProcessLanguage'); + } + if(TracyDebugger::getDataValue('referencePageEdited') + && $this->wire('input')->get('id') + && in_array((string)$this->wire('process'), $processTypes) + ) { + $p = $this->wire('process')->getPage(); + if(!$p || $p instanceof NullPage) { + $p = $this->wire('pages')->get((int) $this->wire('input')->get('id')); + } + if(!$p || $p instanceof NullPage) { + return $this->wire('page'); + } + return $p; + } + return $this->wire('page'); + } + + /** + * Build a section header with a table and column headings. + * + * @param array $columnNames Column heading labels + * @param string $thStyle Optional inline style for th elements + * @return string + */ + protected function sectionEnd() { + return '
'; + } + + protected function sectionHeader($columnNames = array()) { + $out = '
'; + foreach($columnNames as $columnName) { + $out .= ''; + } + $out .= ''; + return $out; + } + + /** + * Strip the site root path from an absolute path. + * + * @param string $path The absolute file path + * @param string $prefix What to replace the root with (default '/') + * @return string + */ + protected function stripRootPath($path, $prefix = '/') { + return TracyDebugger::stripRootPath($path, $prefix); + } + +} diff --git a/includes/CodeProcessor.php b/includes/CodeProcessor.php index 1385626b..e1c19813 100644 --- a/includes/CodeProcessor.php +++ b/includes/CodeProcessor.php @@ -1,44 +1,93 @@ -wire('input')->cookie->tracyCodeError); setcookie("tracyCodeError", "", time()-3600); +// Background execution support: accept run ID and make PHP resilient to connection abort +$tracyRunId = isset($_POST['runId']) ? preg_replace('/[^a-zA-Z0-9_.]/', '', $_POST['runId']) : ''; +$tracyRunStatusDir = $this->wire('config')->paths->assets . 'TracyDebugger/console_runs/'; +$tracyRunCacheDir = $this->wire('config')->paths->cache . 'TracyDebugger/console_runs/'; +if($tracyRunId) { + if(!is_dir($tracyRunStatusDir)) { + wireMkdir($tracyRunStatusDir, true); + file_put_contents($tracyRunStatusDir . '.htaccess', "Options -Indexes\n\n RewriteEngine On\n RewriteRule ^(.+)\\.[0-9]+\\.json$ $1.json [L]\n\n\n Header set Cache-Control \"no-cache, no-store, must-revalidate\"\n\n"); + } + if(!is_dir($tracyRunCacheDir)) wireMkdir($tracyRunCacheDir, true); + /* write initial "running" status marker (no sensitive data — publicly accessible) */ + file_put_contents($tracyRunStatusDir . $tracyRunId . '.json', json_encode(array( + 'status' => 'running' + ))); + ignore_user_abort(true); + set_time_limit(0); +} +TracyDebugger::$consoleRunId = $tracyRunId; +TracyDebugger::$consoleRunStatusDir = $tracyRunStatusDir; +TracyDebugger::$consoleRunCacheDir = $tracyRunCacheDir; if($this->wire('input')->post->allowBluescreen !== 'true') { - set_error_handler('tracyConsoleErrorHandler'); - set_exception_handler('tracyConsoleExceptionHandler'); + set_error_handler(__NAMESPACE__.'\tracyConsoleErrorHandler'); + set_exception_handler(__NAMESPACE__.'\tracyConsoleExceptionHandler'); } -if(\TracyDebugger::getDataValue('use_php_session') === 1 || \TracyDebugger::$tracyVersion == '2.7.x' || \TracyDebugger::$tracyVersion == '2.5.x') { - \Tracy\Debugger::$disableShutdownHandler = true; +if(TracyDebugger::getDataValue('use_php_session') === 1 || TracyDebugger::$tracyVersion == '2.7.x') { + Debugger::$disableShutdownHandler = true; } -register_shutdown_function('tracyConsoleShutdownHandler'); +register_shutdown_function(__NAMESPACE__.'\tracyConsoleShutdownHandler'); // remove location links from dumps - not really meaningful for console -\TracyDebugger::$fromConsole = true; +TracyDebugger::$fromConsole = true; // populate API variables, eg so $page equals $this->wire('page') -$pwVars = function_exists('wire') ? $this->fuel : \ProcessWire\wire('all'); +$pwVars = $this->wire('all'); foreach($pwVars->getArray() as $key => $value) { $$key = $value; } -if(\TracyDebugger::$allowedSuperuser || \TracyDebugger::$validLocalUser || \TracyDebugger::$validSwitchedUser) { +if(TracyDebugger::$allowedSuperuser || TracyDebugger::$validLocalUser || TracyDebugger::$validSwitchedUser) { + + // validate CSRF token + $csrfToken = isset($_POST['csrfToken']) ? $_POST['csrfToken'] : ''; + if(!$csrfToken || !hash_equals((string)$this->wire('session')->tracyConsoleToken, $csrfToken)) { + http_response_code(403); + echo 'CSRF token validation failed'; + exit; + } + + /* cache all session data we need, then release session lock immediately + so the user isn't blocked from browsing while code executes */ + $tracySessionCache = array( + 'includedFiles' => $this->wire('session')->tracyIncludedFiles, + 'getData' => $this->wire('session')->tracyGetData, + 'postData' => $this->wire('session')->tracyPostData, + 'whitelistData' => $this->wire('session')->tracyWhitelistData, + ); + session_write_close(); + /* release Tracy's own FileSession lock — it holds LOCK_EX for the + entire request and blocks other tabs that share the tracy-session cookie */ + $tracySessionStorage = Debugger::getSessionStorage(); + if($tracySessionStorage instanceof \Tracy\FileSession) { + $ref = new \ReflectionProperty($tracySessionStorage, 'file'); + $fileHandle = $ref->getValue($tracySessionStorage); + if(is_resource($fileHandle)) { + flock($fileHandle, LOCK_UN); + } + } $page = $pages->get((int)$_POST['pid']); if(isset($_POST['tracyConsole'])) { $code = $_POST['code']; } else { - $code = null; + $code = ''; } - // ready.php and finished.php weren't being loaded, so include here to monitor any bd() etc calls they might have - // the other approach to fix this is to call an external CodeProcessor.php file via ajax as per PM with @bernhard - $readyPath = $this->wire('config')->paths->root . 'site/ready.php'; - $finishedPath = $this->wire('config')->paths->root . 'site/finished.php'; - if(file_exists($readyPath)) include_once($readyPath); - if(file_exists($finishedPath)) include_once($finishedPath); + // ready.php and finished.php are already executed by PW during normal bootstrap + // before this code runs (CodeProcessor is invoked from a ProcessWire::ready hook). + // Re-including them here would risk duplicate hook registrations and side effects. + // Any bd() calls in those files are already captured by Tracy during bootstrap. $cachePath = $this->wire('config')->paths->cache . 'TracyDebugger/'; - $this->file = $cachePath.'consoleCode.php'; + $this->file = $cachePath.'consoleCode' . ($tracyRunId ? '_' . $tracyRunId : '') . '.php'; $tokens = token_get_all($code); $nextStringIsNamespace = false; $nameSpace = null; @@ -65,7 +114,7 @@ $nameSpace = 'namespace ' . $nameSpace . ';'; $code = str_replace($nameSpace, '', $code); } - elseif(version_compare($this->wire('config')->version, '3', '>=')) { + else { $nameSpace = 'namespace ProcessWire;'; } @@ -88,13 +137,21 @@ if(!$this->wire('files')->filePutContents($this->file, $code, LOCK_EX)) { throw new WireException("Unable to write file: $this->file"); } + if($tracyRunId) { + $this->wire('files')->filePutContents($cachePath . 'consoleCode.php', $code, LOCK_EX); + } if($this->wire('input')->cookie->tracyCodeReturn != "no") { if($this->wire('input')->post->dbBackup === "true") { - setcookie('tracyDbBackup', 1, time() + 3600, '/'); - setcookie('tracyDbBackupFilename', $input->post->text('backupFilename'), time() + 3600, '/'); + if(PHP_VERSION_ID >= 70300) { + setcookie('tracyDbBackup', 1, ['expires' => time() + 3600, 'path' => '/', 'samesite' => 'Strict']); + setcookie('tracyDbBackupFilename', $input->post->text('backupFilename'), ['expires' => time() + 3600, 'path' => '/', 'samesite' => 'Strict']); + } else { + setcookie('tracyDbBackup', 1, time() + 3600, '/'); + setcookie('tracyDbBackupFilename', $input->post->text('backupFilename'), time() + 3600, '/'); + } $backupDir = $this->wire('config')->paths->assets . 'backups/database/'; $filename = basename($this->wire('sanitizer')->filename($input->post('backupFilename')), '.sql'); @@ -103,7 +160,7 @@ $filename = 'tracy-console-' . date('Y-m-d-H-i-s'); $files = glob($backupDir . "tracy-console-*"); if($files) { - if(count($files) >= \TracyDebugger::getDataValue('consoleBackupLimit')) { + if(count($files) >= TracyDebugger::getDataValue('consoleBackupLimit')) { array_multisort( array_map('filemtime', $files), SORT_NUMERIC, @@ -142,7 +199,8 @@ $currentVars = get_defined_vars(); // get vars from the page's template file ob_start(); - foreach($this->wire('session')->tracyIncludedFiles as $key => $path) { + $includedFiles = $tracySessionCache['includedFiles']; + foreach($includedFiles as $key => $path) { if($path != $this->file && $path != $page->template->filename) { include_once($path); } @@ -170,14 +228,15 @@ } // re-populate various $input properties from version stored in session - if($this->wire('session')->tracyGetData) { - foreach($this->wire('session')->tracyGetData as $k => $v) { + $getData = $tracySessionCache['getData']; + if($getData) { + foreach($getData as $k => $v) { $this->wire('input')->get->$k = $v; } } - if($this->wire('session')->tracyPostData) { - $postData = $this->wire('session')->tracyPostData; + $postData = $tracySessionCache['postData']; + if($postData) { foreach($this->wire('input')->post as $k => $v) { unset($this->wire('input')->post->$k); } @@ -186,39 +245,61 @@ } } - if($this->wire('session')->tracyWhitelistData) { - foreach($this->wire('session')->tracyWhitelistData as $k => $v) { + $whitelistData = $tracySessionCache['whitelistData']; + if($whitelistData) { + foreach($whitelistData as $k => $v) { $this->wire('input')->whitelist->$k = $v; } } - // if in admin then $t won't have been instantiated above so do it now if(!isset($t) || !$t instanceof TemplateFile) $t = new TemplateFile($this->file); - \Tracy\Debugger::timer('consoleCode'); + Debugger::timer('consoleCode'); $initialMemory = memory_get_usage(); - // output rendered result of code + + // capture output so we can write it to cache file for background polling + ob_start(); try { echo $t->render(); } catch (\Exception $e) { tracyConsoleExceptionHandler($e); } - echo ' + $renderedOutput = ob_get_clean(); + + $timeStr = TracyDebugger::formatTime(Debugger::timer('consoleCode'), false); + $memStr = TracyDebugger::human_filesize((max((memory_get_usage() - $initialMemory), 0)), false); + $metricsHtml = '
' . - \TracyDebugger::formatTime(\Tracy\Debugger::timer('consoleCode'), false) . ', ' . - \TracyDebugger::human_filesize((max((memory_get_usage() - $initialMemory), 0)), false) . ' + $timeStr . ', ' . $memStr . '
'; + // write result to protected cache file, update public status marker + if($tracyRunId) { + $result = array( + 'status' => 'complete', + 'output' => $renderedOutput . $metricsHtml, + 'time' => $timeStr, + 'memory' => $memStr + ); + file_put_contents($tracyRunCacheDir . $tracyRunId . '.json', json_encode($result)); + file_put_contents($tracyRunStatusDir . $tracyRunId . '.json', json_encode(array('status' => 'complete'))); + } + + // also output inline for normal (non-timed-out) responses + echo $renderedOutput; + echo $metricsHtml; + // fix for updating AJAX bar - if(\TracyDebugger::$tracyVersion == '2.7.x' || \TracyDebugger::$tracyVersion == '2.5.x') { - \Tracy\Debugger::getBar()->render(); - \Tracy\Debugger::$showBar = false; + if(TracyDebugger::$tracyVersion == '2.7.x') { + Debugger::getBar()->render(); + Debugger::$showBar = false; } } + if($tracyRunId) @unlink($this->file); exit; } @@ -295,13 +376,30 @@ function tracyConsoleShutdownHandler() { function writeError($error) { $customErrStr = $error['message'] . ' on line: ' . (strpos($error['file'], 'cache'.DIRECTORY_SEPARATOR.'TracyDebugger') !== false ? $error['line'] - 1 : $error['line']) . (strpos($error['file'], 'cache'.DIRECTORY_SEPARATOR.'TracyDebugger') !== false ? '' : ' in ' . str_replace(wire('config')->paths->cache . 'FileCompiler'.DIRECTORY_SEPARATOR, '../', $error['file'])); $customErrStrLog = $customErrStr . (strpos($error['file'], 'cache'.DIRECTORY_SEPARATOR.'TracyDebugger') !== false ? ' in Tracy Console Panel' : ''); - \TD::fireLog($customErrStrLog); - \TD::log($customErrStrLog, 'error'); + TD::log($customErrStrLog, 'error'); - setcookie('tracyCodeError', $error['type'].': '.$customErrStr, time() + (10 * 365 * 24 * 60 * 60), '/'); + if(PHP_VERSION_ID >= 70300) { + setcookie('tracyCodeError', $error['type'].': '.$customErrStr, ['expires' => time() + (10 * 365 * 24 * 60 * 60), 'path' => '/', 'samesite' => 'Strict']); + } else { + setcookie('tracyCodeError', $error['type'].': '.$customErrStr, time() + (10 * 365 * 24 * 60 * 60), '/'); + } // echo and exit approach allows us to send error to Tracy console dump area // this means that the browser will receive a 200 when it may have been a 500, // but think that is ok in this case echo $error['type'].': '.$customErrStr; + // write error to protected cache, update public status marker + $runId = TracyDebugger::$consoleRunId; + $statusDir = TracyDebugger::$consoleRunStatusDir; + $cacheDir = TracyDebugger::$consoleRunCacheDir; + if($runId && $statusDir && $cacheDir) { + $result = array( + 'status' => 'error', + 'output' => $error['type'].': '.$customErrStr, + 'time' => '', + 'memory' => '' + ); + file_put_contents($cacheDir . $runId . '.json', json_encode($result)); + file_put_contents($statusDir . $runId . '.json', json_encode(array('status' => 'error'))); + } } diff --git a/includes/ConsoleSnippets.php b/includes/ConsoleSnippets.php index 44e3113b..921d4084 100644 --- a/includes/ConsoleSnippets.php +++ b/includes/ConsoleSnippets.php @@ -1,19 +1,30 @@ -wire('config')->paths->site.\TracyDebugger::getDataValue('snippetsPath').'/TracyDebugger/snippets/'; +// validate CSRF token +$csrfToken = isset($_POST['csrfToken']) ? $_POST['csrfToken'] : ''; +if(!$csrfToken || !hash_equals((string)$this->wire('session')->tracyConsoleToken, $csrfToken)) { + http_response_code(403); + echo 'CSRF token validation failed'; + exit; +} + +$snippetsPath = $this->wire('config')->paths->site.TracyDebugger::getDataValue('snippetsPath').'/TracyDebugger/snippets/'; if(!is_dir($snippetsPath)) if(!wireMkdir($snippetsPath, true)) { throw new WireException("Unable to create snippets path: $snippetsPath"); } -if(isset($_POST['deletesnippet']) && file_exists($snippetsPath.$_POST['snippetname'])) { - unlink($snippetsPath.$_POST['snippetname']); +// sanitize snippet name to prevent path traversal +$snippetName = basename($_POST['snippetname']); + +if(isset($_POST['deletesnippet']) && file_exists($snippetsPath.$snippetName)) { + unlink($snippetsPath.$snippetName); } elseif(isset($_POST['snippetcode'])) { - $this->wire('files')->filePutContents($snippetsPath.$_POST['snippetname'], \TracyDebugger::getDataValue('consoleCodePrefix') . json_decode($_POST['snippetcode'])); + $this->wire('files')->filePutContents($snippetsPath.$snippetName, TracyDebugger::getDataValue('consoleCodePrefix') . json_decode($_POST['snippetcode'])); } else { - echo str_replace(\TracyDebugger::getDataValue('consoleCodePrefix'), '', file_get_contents($snippetsPath.$_POST['snippetname'])); + echo str_replace(TracyDebugger::getDataValue('consoleCodePrefix'), '', file_get_contents($snippetsPath.$snippetName)); } exit; diff --git a/includes/GetFileDetails.php b/includes/GetFileDetails.php index b92e9bf9..4d298698 100644 --- a/includes/GetFileDetails.php +++ b/includes/GetFileDetails.php @@ -1,27 +1,47 @@ -wire('input')->get('id') && - ($this->wire('process') == 'ProcessPageEdit' || - $this->wire('process') == 'ProcessUser' || - $this->wire('process') == 'ProcessRole' || - $this->wire('process') == 'ProcessPermission' - ) - ) { - $p = $this->wire('process')->getPage(); - if($p instanceof NullPage) { - $p = $this->wire('pages')->get((int) $this->wire('input')->get('id')); - } +if(TracyDebugger::$allowedSuperuser || TracyDebugger::$validLocalUser || TracyDebugger::$validSwitchedUser) { + + // validate CSRF token + $csrfToken = isset($_POST['csrfToken']) ? $_POST['csrfToken'] : ''; + if(!$csrfToken || !hash_equals((string)$this->wire('session')->tracyFileEditorToken, $csrfToken)) { + http_response_code(403); + echo json_encode(array('error' => 'CSRF token validation failed')); + exit; } - else { - $p = $this->wire('page'); + + $rootPath = $this->wire('config')->paths->root; + $filePath = $_POST['filePath']; + $resolvedPath = str_replace('\\', '/', realpath($rootPath . $filePath)); + if($resolvedPath === false || $resolvedPath === '') { + $resolvedPath = str_replace('\\', '/', realpath($filePath)); } - $fileData = array(); - $fileData['writeable'] = is_writable($_POST['filePath']); - $fileData['backupExists'] = file_exists($this->wire('config')->paths->cache . 'TracyDebugger/' . $_POST['filePath']) ? true : false; - $fileData['isTemplateFile'] = $this->wire('config')->paths->root . $_POST['filePath'] === $p->template->filename; - $fileData['contents'] = file_get_contents($this->wire('config')->paths->root . $_POST['filePath']); - echo json_encode($fileData); + if($resolvedPath !== false && strpos($resolvedPath, $rootPath) === 0) { + + if(TracyDebugger::getDataValue('referencePageEdited') && $this->wire('input')->get('id') && + ($this->wire('process') == 'ProcessPageEdit' || + $this->wire('process') == 'ProcessUser' || + $this->wire('process') == 'ProcessRole' || + $this->wire('process') == 'ProcessPermission' + ) + ) { + $p = $this->wire('process')->getPage(); + if($p instanceof NullPage) { + $p = $this->wire('pages')->get((int) $this->wire('input')->get('id')); + } + } + else { + $p = $this->wire('page'); + } + + $fileData = array(); + $fileData['writeable'] = is_writable($resolvedPath); + $relPath = str_replace($rootPath, '', $resolvedPath); + $fileData['backupExists'] = file_exists($this->wire('config')->paths->cache . 'TracyDebugger/' . $relPath) ? true : false; + $fileData['isTemplateFile'] = $resolvedPath === $p->template->filename; + $fileData['contents'] = file_get_contents($resolvedPath); + echo json_encode($fileData); + } } exit; diff --git a/includes/GetPageById.php b/includes/GetPageById.php index eb96b9c0..df4c7c01 100644 --- a/includes/GetPageById.php +++ b/includes/GetPageById.php @@ -1,14 +1,14 @@ -wire('pages')->get((int)$_POST['goToPage']); $pageInfo = array(); if($p->id) { $pageInfo['id'] = $p->id; - $pageInfo['title'] = truncateText($p->title ?: $p->name, 50); + $pageInfo['title'] = truncateText(htmlspecialchars($p->title ?: $p->name ?: '', ENT_QUOTES, 'UTF-8'), 50); $pageInfo['url'] = $p->url; $pageInfo['template_id'] = $p->template->id; $pageInfo['template_name'] = $p->template->name; - $pageInfo['template_label'] = $p->template->label; + $pageInfo['template_label'] = htmlspecialchars($p->template->label ?? '', ENT_QUOTES, 'UTF-8'); $pageInfo['path'] = $p->path; $pageInfo['unpublished'] = $p->isUnpublished(); $pageInfo['hidden'] = $p->isHidden(); diff --git a/includes/GetTemplateResources.php b/includes/GetTemplateResources.php index b756ebae..1614a667 100644 --- a/includes/GetTemplateResources.php +++ b/includes/GetTemplateResources.php @@ -1,23 +1,23 @@ -wire('config')->paths->site) !== false && strpos($includedFile, DIRECTORY_SEPARATOR.'site'.DIRECTORY_SEPARATOR.'modules'.DIRECTORY_SEPARATOR) === false && strpos($includedFile, 'config.php') === false) { if(!in_array($includedFile, $includedFiles)) $includedFiles[] = $includedFile; } } -\TracyDebugger::$includedFiles = $includedFiles; +TracyDebugger::$includedFiles = $includedFiles; // store in session for use by console panel -$this->wire('session')->tracyIncludedFiles = \TracyDebugger::$includedFiles; \ No newline at end of file +$this->wire('session')->tracyIncludedFiles = TracyDebugger::$includedFiles; diff --git a/includes/PwApiData.php b/includes/PwApiData.php index 179a6bf5..012e914a 100644 --- a/includes/PwApiData.php +++ b/includes/PwApiData.php @@ -1,4 +1,4 @@ -wire('cache')->get($cacheName); + $storedVersion = TracyDebugger::getDataValue('apiDataVersion'); + $currentVersion = $this->wire('config')->version; + $versionChanged = $storedVersion !== null && $currentVersion != $storedVersion; + + if(!$apiData || $storedVersion === null || $versionChanged) { + // decode existing cache for change detection (only needed when version actually changed) + if($apiData && $versionChanged) { + $cachedData = json_decode(ltrim($apiData, '~'), true); + } - if(!$apiData || \TracyDebugger::getDataValue('apiDataVersion') === null || $this->wire('config')->version != \TracyDebugger::getDataValue('apiDataVersion')) { - if($apiData) $cachedData = json_decode(ltrim($apiData, '~'), true); if($type == 'variables') { $apiData = $this->getVariables(); } @@ -31,29 +38,34 @@ public function getApiData($type) { } // if PW core version has changed, populate the "TracyApiChanges" data cache - if(isset($cachedData) && \TracyDebugger::getDataValue('apiDataVersion') !== null && $this->wire('config')->version != \TracyDebugger::getDataValue('apiDataVersion')) { - \TracyDebugger::$apiChanges['cachedVersion'] = \TracyDebugger::getDataValue('apiDataVersion'); + if(isset($cachedData)) { + TracyDebugger::$apiChanges['cachedVersion'] = $storedVersion; foreach($apiData as $class => $methods) { $i=0; foreach($methods as $method => $params) { if(!isset($cachedData[$class]) || !array_key_exists($method, $cachedData[$class])) { - if($type != 'hooks') \TracyDebugger::$apiChanges[$type][$class][$i] = $method; + if($type != 'hooks') TracyDebugger::$apiChanges[$type][$class][$i] = $method; $i++; } } } - $this->wire('cache')->save('TracyApiChanges', '~'.json_encode(\TracyDebugger::$apiChanges), WireCache::expireNever); - - $configData = $this->wire('modules')->getModuleConfigData("TracyDebugger"); - $configData['apiDataVersion'] = $this->wire('config')->version; - $this->wire('modules')->saveModuleConfigData($this->wire('modules')->get("TracyDebugger"), $configData); - + $this->wire('cache')->save('TracyApiChanges', '~'.json_encode(TracyDebugger::$apiChanges), WireCache::expireNever); } // tilde hack for this: https://github.com/processwire/processwire-issues/issues/775 $apiData = '~'.json_encode($apiData); $this->wire('cache')->save($cacheName, $apiData, WireCache::expireNever); + // update stored version if needed (only save config once per request) + if($currentVersion != $storedVersion) { + static $versionSaved = false; + if(!$versionSaved) { + $configData = $this->wire('modules')->getModuleConfigData("TracyDebugger"); + $configData['apiDataVersion'] = $currentVersion; + $this->wire('modules')->saveModuleConfigData($this->wire('modules')->get("TracyDebugger"), $configData); + $versionSaved = true; + } + } } return json_decode(ltrim($apiData, '~'), true); @@ -63,7 +75,7 @@ public function getApiData($type) { private function getVariables() { $apiVars = array(); $apiVariables = array(); - $this->pwVars = $this->wire('config')->version >= 2.8 ? $this->wire('all') : $this->wire()->fuel; + $this->pwVars = $this->wire('all'); if(is_object($this->pwVars)) { foreach($this->pwVars as $key => $value) { if(!is_object($value)) continue; @@ -86,10 +98,10 @@ private function getClasses($type, $folder) { } $classesArr = array(); foreach($classes as $class) { - if(!in_array($class, \TracyDebugger::$allApiClassesArr)) { + if(!in_array($class, TracyDebugger::$allApiClassesArr)) { if($type == 'coreModules' && strpos($folder, 'modules') === false) continue; // for classes with an API object variable, provide an empty methods/properties array - if(array_key_exists(lcfirst($class), \TracyDebugger::$allApiData['variables'])) { + if(array_key_exists(lcfirst($class), TracyDebugger::$allApiData['variables'])) { $classesArr += array($class => array()); } else { @@ -106,7 +118,7 @@ private function getClasses($type, $folder) { } $classesArr += $this->buildItemsArray($r, $class, $type); } - array_push(\TracyDebugger::$allApiClassesArr, $class); + array_push(TracyDebugger::$allApiClassesArr, $class); } } ksort($classesArr); @@ -168,7 +180,7 @@ private function buildItemsArray($r, $class, $type) { } $methodStr = "$" . ($type == 'coreModules' || $type == 'siteModules' ? 'modules->get(\''.$class.'\')' : lcfirst($class)) . '->' . str_replace('___', '', $name) . '(' . (isset($methodsList[$name.'()']['params']) ? implode(', ', $methodsList[$name.'()']['params']) : '') . ')'; - if(\TracyDebugger::getDataValue('apiExplorerToggleDocComment')) { + if(TracyDebugger::getDataValue('apiExplorerToggleDocComment')) { $commentStr = "
@@ -182,7 +194,7 @@ private function buildItemsArray($r, $class, $type) { $methodsList[$name.'()']['comment'] = $methodStr; } - if(\TracyDebugger::getDataValue('apiExplorerShowDescription') || \TracyDebugger::getDataValue('codeShowDescription')) { + if(TracyDebugger::getDataValue('apiExplorerShowDescription') || TracyDebugger::getDataValue('codeShowDescription')) { // get the comment preg_match('#^/\*\*(.*)\*/#s', $docComment, $comment); if(isset($comment[0])) $comment = trim($comment[0]); @@ -244,12 +256,12 @@ private function buildItemsArray($r, $class, $type) { } $items[$class][$name]['comment'] = $methodStr; - if(\TracyDebugger::getDataValue('apiExplorerShowDescription') || \TracyDebugger::getDataValue('codeShowDescription')) { + if(TracyDebugger::getDataValue('apiExplorerShowDescription') || TracyDebugger::getDataValue('codeShowDescription')) { if(substr($info['description'], 0, 1) === '#') { $items[$class][$name]['description'] = ''; } else { - $items[$class][$name]['description'] = $info['description']; + $items[$class][$name]['description'] = htmlentities(trim($info['description'])); } } if($type == 'variables') $this->n++; @@ -294,9 +306,9 @@ private function buildItemsArray($r, $class, $type) { $items[$class][$name]['lineNumber'] = ''; $items[$class][$name]['filename'] = ''; $items[$class][$name]['comment'] = "$" . lcfirst($class) . '->' . str_replace('___', '', $name); - if(\TracyDebugger::getDataValue('apiExplorerShowDescription') || \TracyDebugger::getDataValue('codeShowDescription')) { + if(TracyDebugger::getDataValue('apiExplorerShowDescription') || TracyDebugger::getDataValue('codeShowDescription')) { $desc = preg_replace('/#([^#\s]+)/', '', $info['description']); - $items[$class][$name]['description'] = $desc; + $items[$class][$name]['description'] = htmlentities(trim($desc)); } if($type == 'variables') $this->n++; } @@ -309,16 +321,16 @@ private function buildItemsArray($r, $class, $type) { private function getPHPFilenames($root, $excludeFilenames = array()) { $fileNamePattern = "/\.(php|module)$/"; - $iter = new RecursiveIteratorIterator( - new RecursiveDirectoryIterator($root, RecursiveDirectoryIterator::SKIP_DOTS), - RecursiveIteratorIterator::SELF_FIRST, - RecursiveIteratorIterator::CATCH_GET_CHILD // Ignore "Permission denied" + $iter = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($root, \RecursiveDirectoryIterator::SKIP_DOTS), + \RecursiveIteratorIterator::SELF_FIRST, + \RecursiveIteratorIterator::CATCH_GET_CHILD // Ignore "Permission denied" ); $paths = array(); foreach($iter as $path => $dir) { // '/.' check is for site module backups - SKIP_DOTS above is not excluding these - if(!$dir->isDir() && strpos(str_replace(wire('config')->paths->root, '', $path), DIRECTORY_SEPARATOR.'.') === false && preg_match($fileNamePattern, $path) && !in_array(basename($path), $excludeFilenames) ) { + if(!$dir->isDir() && strpos(TracyDebugger::stripRootPath($path, ''), DIRECTORY_SEPARATOR.'.') === false && preg_match($fileNamePattern, $path) && !in_array(basename($path), $excludeFilenames) ) { $paths[] = $path; } } @@ -328,7 +340,7 @@ private function getPHPFilenames($root, $excludeFilenames = array()) { private function getFunctionsInFile($file, $hooks = false) { - $newTab = \TracyDebugger::getDataValue('linksNewTab') ? 'target="_blank"' : ''; + $newTab = TracyDebugger::getDataValue('linksNewTab') ? 'target="_blank"' : ''; $lines = file($file); $source = implode('', $lines); @@ -392,21 +404,17 @@ private function getFunctionsInFile($file, $hooks = false) { $files['pwFunctions'][$name]['lineNumber'] = $token[2]; if($className) { - if(class_exists("\ProcessWire\\$className", false)) { - $r = new \ReflectionMethod("\ProcessWire\\$className", '___'.$methodName); - } - elseif(class_exists($className, false)) { - $r = new \ReflectionMethod($className, '___'.$methodName); - } - if(isset($r)) { + $fqClass = class_exists("\ProcessWire\\$className", false) ? "\ProcessWire\\$className" : (class_exists($className, false) ? $className : null); + if($fqClass && method_exists($fqClass, '___'.$methodName)) { + $r = new \ReflectionMethod($fqClass, '___'.$methodName); $files['pwFunctions'][$name]['params'] = $this->phpdoc_params($r); } } $methodStr = ltrim(self::getFunctionLine($lines[($token[2]-1)]), 'function'); if( - ($hooks && \TracyDebugger::getDataValue('captainHookToggleDocComment')) || - (!$hooks && \TracyDebugger::getDataValue('apiExplorerToggleDocComment')) + ($hooks && TracyDebugger::getDataValue('captainHookToggleDocComment')) || + (!$hooks && TracyDebugger::getDataValue('apiExplorerToggleDocComment')) ) { $commentStr = "
@@ -421,8 +429,8 @@ private function getFunctionsInFile($file, $hooks = false) { } if( - ($hooks && \TracyDebugger::getDataValue('captainHookShowDescription')) || - (!$hooks && (\TracyDebugger::getDataValue('apiExplorerShowDescription') || \TracyDebugger::getDataValue('codeShowDescription'))) + ($hooks && TracyDebugger::getDataValue('captainHookShowDescription')) || + (!$hooks && (TracyDebugger::getDataValue('apiExplorerShowDescription') || TracyDebugger::getDataValue('codeShowDescription'))) ) { // get the comment preg_match('#^/\*\*(.*)\*/#s', $docComment, $comment); @@ -531,13 +539,13 @@ private static function getFunctionLine($str) { } - private function phpdoc_params(ReflectionMethod $method) { + private function phpdoc_params(\ReflectionMethod $method) { // Retrieve the full PhpDoc comment block $doc = $method->getDocComment(); // Trim each line from space and star chars $lines = array_map(function($line) { - return trim(preg_replace('/\t/', '', $line), " *"); + return trim(preg_replace('/\t/', '', $line) ?? '', " *"); }, explode("\n", $doc)); // Retain lines that start with an @ @@ -561,6 +569,7 @@ private function phpdoc_params(ReflectionMethod $method) { public static function convertNamesToUrls($str) { + if(!is_string($str) || $str === '') return ''; return trim(strtolower(preg_replace('/([A-Z])/', '-$1', $str)), '-'); } diff --git a/includes/PwVersionSwitcher.php b/includes/PwVersionSwitcher.php new file mode 100644 index 00000000..fb7b85a6 --- /dev/null +++ b/includes/PwVersionSwitcher.php @@ -0,0 +1,469 @@ +wire('config')->version to update + if(($module->wire('input')->post->tracyPwVersion && $module->wire('input')->post->tracyPwVersion != $module->wire('config')->version && $module->wire('session')->CSRF->validate()) || $module->wire('session')->tracyPwVersion) { + $module->wire('session')->tracyPwVersion = $module->wire('session')->tracyPwVersion ?: $module->wire('input')->post->tracyPwVersion; + $tracyPwVersionRetries = $module->wire('session')->tracyPwVersionRetries ?: 0; + if($module->wire('session')->tracyPwVersion != $module->wire('config')->version && $tracyPwVersionRetries < 5) { + $module->wire('session')->tracyPwVersionRetries = $tracyPwVersionRetries + 1; + sleep(1); + $module->wire('session')->redirect($module->httpReferer); + } + $module->wire('session')->remove('tracyPwVersion'); + $module->wire('session')->remove('tracyPwVersionRetries'); + } + } + + /** + * Clean up auto-revert marker at the very end of init(). + * If init() completes fully, register shutdown function to clean up the marker. + * + * @param TracyDebugger $module + */ + public static function cleanupSwitchMarker($module) { + self::cleanupPwVersionSwitchMarker($module); + } + + /** + * Show auto-revert notification in ready(). + * Deferred to ready() because PW's session system is fully stable here. + * + * @param TracyDebugger $module + */ + public static function showRevertNotification($module) { + $rootPath = $module->wire('config')->paths->root; + $revertedFile = $rootPath . '.tracy-pw-reverted'; + if(file_exists($revertedFile)) { + $revertLog = @json_decode(@file_get_contents($revertedFile), true); + $revertedFrom = isset($revertLog['revertedFrom']) ? $revertLog['revertedFrom'] : 'unknown'; + $revertedTo = isset($revertLog['revertedTo']) ? $revertLog['revertedTo'] : 'unknown'; + $reason = isset($revertLog['reason']) ? $revertLog['reason'] : ''; + $msg = "TracyDebugger: ProcessWire version switch to v{$revertedFrom} was automatically reverted to v{$revertedTo}." . + ($reason ? " Reason: {$reason}" : ''); + // Use both notice methods: session warning persists across redirects, + // module warning ensures display on the current request + $module->wire('session')->warning($msg); + $module->warning($msg); + // Only delete on a non-AJAX request where the admin theme renders notices. + // AJAX/redirect/error requests keep the file so it retries next page load. + if(!$module->wire('config')->ajax) { + @unlink($revertedFile); + } + } + } + + /** + * Perform the actual file swap in __destruct(). + * Renames wire/, index.php, and .htaccess when using PW Version Switcher. + * + * @param TracyDebugger $module + */ + public static function performFileSwap($module) { + if($module->wire('input') && $module->wire('input')->post->tracyPwVersion && $module->wire('input')->post->tracyPwVersion != $module->wire('config')->version + && (TracyDebugger::$allowedSuperuser || TracyDebugger::$validLocalUser || TracyDebugger::$validSwitchedUser)) { + + $rootPath = $module->wire('config')->paths->root; + + // Don't re-swap if a revert just occurred or is in progress + // (e.g. browser reload resends the POST data after a failed switch) + if(file_exists($rootPath . '.tracy-pw-reverted') || file_exists($rootPath . '.tracy-pw-revert')) return; + + $targetVersion = $module->wire('input')->post->tracyPwVersion; + + // validate version format (digits and dots only) + if(!preg_match('/^\d+(\.\d+)*$/', $targetVersion)) return; + $currentVersion = $module->wire('config')->version; + + // pre-validate: target wire directory must exist before we move anything + if(!file_exists($rootPath.'.wire-'.$targetVersion)) return; + + // Write the auto-revert handler file BEFORE renames so it's in place early. + // Uses @ suppression and try/catch because PW's error handler may convert + // file_put_contents warnings to exceptions during __destruct() shutdown. + self::writePwRevertHandler($rootPath); + + // track which files were swapped for auto-revert + $htaccessSwapped = false; + $indexSwapped = false; + + // rename wire — move current away, then move target into place + if(!self::renamePwVersions($rootPath.'wire', $rootPath.'.wire-'.$currentVersion, true)) return; + if(!self::renamePwVersions($rootPath.'.wire-'.$targetVersion, $rootPath.'wire')) { + // rollback: restore original wire directory + self::renamePwVersions($rootPath.'.wire-'.$currentVersion, $rootPath.'wire'); + return; + } + + // rename .htaccess if a versioned replacement exists + if(file_exists($rootPath.'.htaccess-'.$targetVersion)) { + if(!self::renamePwVersions($rootPath.'.htaccess', $rootPath.'.htaccess-'.$currentVersion, true)) return; + if(!self::renamePwVersions($rootPath.'.htaccess-'.$targetVersion, $rootPath.'.htaccess')) { + // rollback htaccess + self::renamePwVersions($rootPath.'.htaccess-'.$currentVersion, $rootPath.'.htaccess'); + return; + } + $htaccessSwapped = true; + } + // rename index.php if a versioned replacement exists + if(file_exists($rootPath.'.index-'.$targetVersion.'.php')) { + if(!self::renamePwVersions($rootPath.'index.php', $rootPath.'.index-'.$currentVersion.'.php', true)) return; + if(!self::renamePwVersions($rootPath.'.index-'.$targetVersion.'.php', $rootPath.'index.php')) { + // rollback index.php + self::renamePwVersions($rootPath.'.index-'.$currentVersion.'.php', $rootPath.'index.php'); + return; + } + $indexSwapped = true; + } + + // Clear OPcache so the next request loads fresh bytecode from the new wire/ files + if(function_exists('opcache_reset')) @opcache_reset(); + + // Inject include line into now-active index.php and write the marker to arm the mechanism + self::armPwVersionAutoRevert($rootPath, $currentVersion, $targetVersion, $htaccessSwapped, $indexSwapped); + } + } + + + /** + * Rename paths for PW version switching with collision-safe suffix. + * + * @param string $oldPath Old Path + * @param string $newPath New Path + * @param bool $addN Whether to add numeric suffix if target exists + * @return bool + */ + public static function renamePwVersions($oldPath, $newPath, $addN = false) { + if(!file_exists($oldPath)) return false; + if(file_exists($newPath) && $addN) { + $n = 0; + do { $newPath2 = $newPath . "-" . (++$n); } while(file_exists($newPath2) && $n < 100); + if($n >= 100) return false; + if(!rename($newPath, $newPath2)) return false; + } + return rename($oldPath, $newPath); + } + + + /** + * Write the standalone auto-revert handler file to the site root. + * Called BEFORE renames so it's in place early. The handler is inert until the + * marker file is written (by armPwVersionAutoRevert) after all renames succeed. + * + * All file operations use @ suppression and try/catch because PW's error handler + * may convert warnings to exceptions during __destruct() shutdown. + * + * @param string $rootPath Site root path + */ + private static function writePwRevertHandler($rootPath) { + try { + $handlerCode = <<<'HANDLER' += 100) return false; + if(!@rename($newPath, $alt)) return false; + } + return @rename($oldPath, $newPath); + }; + + // Revert function (shared by both trigger mechanisms) + $tracyDoRevert = function($tracyRevertData, $tracyRevertMarker, $tracyAttemptedFlag, $tracySafeRename, $reason) { + $rootPath = __DIR__ . '/'; + $prev = $tracyRevertData['previousVersion']; + $target = $tracyRevertData['targetVersion']; + + // revert wire/ + if(is_dir($rootPath . '.wire-' . $prev)) { + $tracySafeRename($rootPath . 'wire', $rootPath . '.wire-' . $target); + $tracySafeRename($rootPath . '.wire-' . $prev, $rootPath . 'wire'); + } + + // revert .htaccess if it was swapped + if(!empty($tracyRevertData['htaccessSwapped']) && file_exists($rootPath . '.htaccess-' . $prev)) { + $tracySafeRename($rootPath . '.htaccess', $rootPath . '.htaccess-' . $target); + $tracySafeRename($rootPath . '.htaccess-' . $prev, $rootPath . '.htaccess'); + } + + // revert index.php if it was swapped + if(!empty($tracyRevertData['indexSwapped']) && file_exists($rootPath . '.index-' . $prev . '.php')) { + $tracySafeRename($rootPath . 'index.php', $rootPath . '.index-' . $target . '.php'); + $tracySafeRename($rootPath . '.index-' . $prev . '.php', $rootPath . 'index.php'); + } + + if(function_exists('opcache_reset')) @opcache_reset(); + + // write log for admin visibility + @file_put_contents($rootPath . '.tracy-pw-reverted', json_encode(array( + 'revertedFrom' => $target, + 'revertedTo' => $prev, + 'reason' => $reason, + 'timestamp' => time() + ))); + + // remove marker and attempted flag + @unlink($tracyRevertMarker); + @unlink($tracyAttemptedFlag); + }; + + $tracyRevertData = @json_decode(@file_get_contents($tracyRevertMarker), true); + if(is_array($tracyRevertData) && !empty($tracyRevertData['previousVersion']) && !empty($tracyRevertData['targetVersion'])) { + // safety: ignore stale markers older than 1 hour + if(isset($tracyRevertData['timestamp']) && (time() - $tracyRevertData['timestamp']) > 3600) { + @unlink($tracyRevertMarker); + @unlink($tracyAttemptedFlag); + } + // REQUEST 2+: attempted flag exists — TracyDebugger never cleaned up — revert now + elseif(file_exists($tracyAttemptedFlag)) { + $tracyDoRevert($tracyRevertData, $tracyRevertMarker, $tracyAttemptedFlag, $tracySafeRename, + 'TracyDebugger failed to boot after version switch (attempted flag present on request 2+)'); + } + // REQUEST 1: first attempt — give PW a chance to boot + else { + // touch() is far more reliable than file_put_contents() in degraded + // PHP environments — it writes zero bytes and has minimal failure modes + @touch($tracyAttemptedFlag); + + // FATAL/500 ERROR TRIGGER: catch immediate crashes on this first request + register_shutdown_function(function() use ($tracyRevertData, $tracyRevertMarker, $tracyAttemptedFlag, $tracySafeRename, $tracyDoRevert) { + // only fire if marker still exists (init() cleanup deletes it on success) + if(!file_exists($tracyRevertMarker)) return; + $error = error_get_last(); + $httpCode = http_response_code(); + $isFatal = $error && in_array($error['type'], array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR)); + $isServerError = $httpCode !== false && $httpCode >= 500; + if($isFatal || $isServerError) { + if($isFatal) { + $reason = 'Fatal error: ' . (isset($error['message']) ? $error['message'] : 'unknown') . + (isset($error['file']) ? ' in ' . $error['file'] : '') . + (isset($error['line']) ? ' on line ' . $error['line'] : ''); + } else { + $reason = 'HTTP ' . $httpCode . ' error' . + ($error ? ': ' . $error['message'] : ''); + } + $tracyDoRevert($tracyRevertData, $tracyRevertMarker, $tracyAttemptedFlag, $tracySafeRename, $reason); + } + }); + } + } +} +HANDLER; + @file_put_contents($rootPath . '.tracy-pw-revert-handler.php', $handlerCode); + } catch(\Exception $e) {} catch(\Error $e) {} + } + + + /** + * Inject the include line into index.php and write the marker file to arm auto-revert. + * Called AFTER all renames succeed. The marker is written last so the handler only + * activates when everything is in place. + * + * @param string $rootPath Site root path + * @param string $previousVersion The version we switched FROM + * @param string $targetVersion The version we switched TO + * @param bool $htaccessSwapped Whether .htaccess was swapped + * @param bool $indexSwapped Whether index.php was swapped + */ + private static function armPwVersionAutoRevert($rootPath, $previousVersion, $targetVersion, $htaccessSwapped, $indexSwapped) { + try { + // 1. Inject include line into the current active index.php (post-swap) + $indexPath = $rootPath . 'index.php'; + $includeLine = "if(file_exists(__DIR__ . '/.tracy-pw-revert-handler.php')) include __DIR__ . '/.tracy-pw-revert-handler.php';"; + if(file_exists($indexPath)) { + $indexContent = @file_get_contents($indexPath); + if($indexContent !== false) { + // only inject if not already present + if(strpos($indexContent, '.tracy-pw-revert-handler.php') === false) { + // insert after the first line (always $previousVersion, + 'targetVersion' => $targetVersion, + 'htaccessSwapped' => $htaccessSwapped, + 'indexSwapped' => $indexSwapped, + 'timestamp' => time() + ); + @file_put_contents($rootPath . '.tracy-pw-revert', json_encode($markerData)); + } catch(\Exception $e) {} catch(\Error $e) {} + } + + + /** + * Handle cleanup after an auto-revert occurred. + * Called EARLY in init() (before redirect polling) so it can clear stale + * session vars that would otherwise trigger unnecessary redirect loops. + * + * NOTE: The .tracy-pw-reverted log file is intentionally NOT deleted here. + * The user notification is deferred to ready() where PW's session system is + * fully stable. If ready() doesn't run, the file persists and the notification + * shows on the next successful request. + * + * @param TracyDebugger $module + */ + private static function cleanupPwVersionAutoRevert($module) { + $rootPath = $module->wire('config')->paths->root; + $revertedFile = $rootPath . '.tracy-pw-reverted'; + + if(!file_exists($revertedFile)) return; + + // Auto-revert occurred — clean up all auto-revert infrastructure + // (.tracy-pw-reverted is kept for ready() to read and display the notification) + $markerFile = $rootPath . '.tracy-pw-revert'; + $handlerFile = $rootPath . '.tracy-pw-revert-handler.php'; + $attemptedFile = $rootPath . '.tracy-pw-revert-attempted'; + $includeLine = "if(file_exists(__DIR__ . '/.tracy-pw-revert-handler.php')) include __DIR__ . '/.tracy-pw-revert-handler.php';"; + + @unlink($handlerFile); + @unlink($markerFile); // should already be gone, but just in case + @unlink($attemptedFile); // should already be gone, but just in case + + // remove the include line from index.php if present + self::removePwRevertIncludeLine($rootPath, $includeLine); + + // clear stale session vars from the failed switch attempt + $module->wire('session')->remove('tracyPwVersion'); + $module->wire('session')->remove('tracyPwVersionRetries'); + } + + + /** + * Clean up auto-revert marker after a successful version switch. + * Called at the VERY END of init() — this is critical because if Tracy's init() + * fails partway through (e.g. DeferredContent::sendAssets() throws because + * deprecation warnings broke output buffering), this method is never reached, + * the shutdown function is never registered, and the marker survives for the + * handler to trigger a revert on the next request. + * + * Uses a shutdown function so cleanup happens AFTER ProcessWire::boot() + * (init runs before boot, and boot() can fail on version-mismatched wire/). + * + * @param TracyDebugger $module + */ + private static function cleanupPwVersionSwitchMarker($module) { + $rootPath = $module->wire('config')->paths->root; + $markerFile = $rootPath . '.tracy-pw-revert'; + + if(!file_exists($markerFile)) return; + + $handlerFile = $rootPath . '.tracy-pw-revert-handler.php'; + $attemptedFile = $rootPath . '.tracy-pw-revert-attempted'; + $includeLine = "if(file_exists(__DIR__ . '/.tracy-pw-revert-handler.php')) include __DIR__ . '/.tracy-pw-revert-handler.php';"; + + register_shutdown_function(function() use ($markerFile, $handlerFile, $attemptedFile, $includeLine, $rootPath) { + // Don't clean up if an HTTP 500+ error occurred (e.g. boot() failed + // after init() succeeded) — let the auto-revert handler fix it + $code = http_response_code(); + if($code !== false && $code >= 500) return; + // Don't clean up if marker was already removed by the handler's + // fatal error shutdown function + if(!file_exists($markerFile)) return; + @unlink($markerFile); + @unlink($handlerFile); + @unlink($attemptedFile); + // Remove the include line from index.php + $indexPath = $rootPath . 'index.php'; + if(file_exists($indexPath)) { + $indexContent = @file_get_contents($indexPath); + if($indexContent !== false && strpos($indexContent, $includeLine) !== false) { + $indexContent = str_replace([$includeLine . "\r\n", $includeLine . "\n"], '', $indexContent); + @file_put_contents($indexPath, $indexContent); + } + } + }); + } + + + /** + * Remove the auto-revert include line from index.php + * + * @param string $rootPath Site root path + * @param string $includeLine The exact include line to remove + */ + private static function removePwRevertIncludeLine($rootPath, $includeLine) { + $indexPath = $rootPath . 'index.php'; + if(file_exists($indexPath)) { + $indexContent = @file_get_contents($indexPath); + if($indexContent !== false && strpos($indexContent, $includeLine) !== false) { + $indexContent = str_replace([$includeLine . "\r\n", $includeLine . "\n"], '', $indexContent); + @file_put_contents($indexPath, $indexContent); + } + } + } + +} diff --git a/includes/ShortcutMethods.php b/includes/ShortcutMethods.php index 515c8861..337a57e9 100644 --- a/includes/ShortcutMethods.php +++ b/includes/ShortcutMethods.php @@ -1,13 +1,15 @@ data['enabledShortcutMethods'])) { function debugAll($var, $title = NULL, $options = []) { if(tracyUnavailable()) return false; - return TD::debugAll($var, $title, $options); + return \ProcessWire\TD::debugAll($var, $title, $options); } } @@ -32,8 +34,8 @@ function debugAll($var, $title = NULL, $options = []) { */ if(!function_exists('barDump') && in_array('barDump', $this->data['enabledShortcutMethods'])) { function barDump($var, $title = NULL, $options = []) { - if(tracyUnavailable() && !\TracyDebugger::getDataValue('recordGuestDumps')) return false; - return TD::barDump($var, $title, $options); + if(tracyUnavailable() && !\ProcessWire\TracyDebugger::getDataValue('recordGuestDumps')) return false; + return \ProcessWire\TD::barDump($var, $title, $options); } } @@ -43,8 +45,8 @@ function barDump($var, $title = NULL, $options = []) { */ if(!function_exists('barDumpBig') && in_array('barDumpBig', $this->data['enabledShortcutMethods'])) { function barDumpBig($var, $title = NULL) { - if(tracyUnavailable() && !\TracyDebugger::getDataValue('recordGuestDumps')) return false; - return TD::barDumpBig($var, $title); + if(tracyUnavailable() && !\ProcessWire\TracyDebugger::getDataValue('recordGuestDumps')) return false; + return \ProcessWire\TD::barDumpBig($var, $title); } } @@ -55,7 +57,7 @@ function barDumpBig($var, $title = NULL) { if(!function_exists('dump') && in_array('dump', $this->data['enabledShortcutMethods'])) { function dump($var, $title = NULL, $options = []) { if(tracyUnavailable() && PHP_SAPI !== 'cli') return false; - return TD::dump($var, $title, $options); + return \ProcessWire\TD::dump($var, $title, $options); } } @@ -66,7 +68,7 @@ function dump($var, $title = NULL, $options = []) { if(!function_exists('dumpBig') && in_array('dumpBig', $this->data['enabledShortcutMethods'])) { function dumpBig($var, $title = NULL, $options = []) { if(tracyUnavailable() && PHP_SAPI !== 'cli') return false; - return TD::dumpBig($var, $title, $options); + return \ProcessWire\TD::dumpBig($var, $title, $options); } } @@ -76,8 +78,8 @@ function dumpBig($var, $title = NULL, $options = []) { */ if(!function_exists('barEcho') && in_array('barEcho', $this->data['enabledShortcutMethods'])) { function barEcho($str, $title = NULL) { - if(tracyUnavailable() && !\TracyDebugger::getDataValue('recordGuestDumps')) return false; - return TD::barEcho($str, $title); + if(tracyUnavailable() && !\ProcessWire\TracyDebugger::getDataValue('recordGuestDumps')) return false; + return \ProcessWire\TD::barEcho($str, $title); } } @@ -88,20 +90,7 @@ function barEcho($str, $title = NULL) { if(!function_exists('timer') && in_array('timer', $this->data['enabledShortcutMethods'])) { function timer($name = NULL) { if(tracyUnavailable()) return false; - return TD::timer($name); - } -} - -/** - * TD::fireLog() shortcut. - * @tracySkipLocation - */ -if(method_exists('Tracy\Debugger', 'getFireLogger')) { - if(!function_exists('fireLog') && in_array('fireLog', $this->data['enabledShortcutMethods'])) { - function fireLog($message = NULL) { - if(tracyUnavailable()) return false; - return TD::fireLog($message); - } + return \ProcessWire\TD::timer($name); } } @@ -112,7 +101,7 @@ function fireLog($message = NULL) { if(!function_exists('addBreakpoint') && in_array('addBreakpoint', $this->data['enabledShortcutMethods'])) { function addBreakpoint($name = null, $enforceParent = null) { if(tracyUnavailable() || !class_exists('\Zarganwar\PerformancePanel\Register')) return false; - return TD::addBreakpoint($name, $enforceParent); + return \ProcessWire\TD::addBreakpoint($name, $enforceParent); } } @@ -123,7 +112,7 @@ function addBreakpoint($name = null, $enforceParent = null) { if(!function_exists('templateVars') && in_array('templateVars', $this->data['enabledShortcutMethods'])) { function templateVars($vars) { if(tracyUnavailable()) return false; - return TD::templateVars($vars); + return \ProcessWire\TD::templateVars($vars); } } @@ -138,7 +127,7 @@ function templateVars($vars) { if(!function_exists('da') && in_array('da', $this->data['enabledShortcutMethods'])) { function da($var, $title = NULL, $options = []) { if(tracyUnavailable()) return false; - return TD::debugAll($var, $title, $options); + return \ProcessWire\TD::debugAll($var, $title, $options); } } @@ -148,8 +137,8 @@ function da($var, $title = NULL, $options = []) { */ if(!function_exists('bd') && in_array('bd', $this->data['enabledShortcutMethods'])) { function bd($var, $title = NULL, $options = []) { - if(tracyUnavailable() && !\TracyDebugger::getDataValue('recordGuestDumps')) return false; - return TD::barDump($var, $title, $options); + if(tracyUnavailable() && !\ProcessWire\TracyDebugger::getDataValue('recordGuestDumps')) return false; + return \ProcessWire\TD::barDump($var, $title, $options); } } @@ -159,8 +148,8 @@ function bd($var, $title = NULL, $options = []) { */ if(!function_exists('bdb') && in_array('bdb', $this->data['enabledShortcutMethods'])) { function bdb($var, $title = NULL) { - if(tracyUnavailable() && !\TracyDebugger::getDataValue('recordGuestDumps')) return false; - return TD::barDumpBig($var, $title); + if(tracyUnavailable() && !\ProcessWire\TracyDebugger::getDataValue('recordGuestDumps')) return false; + return \ProcessWire\TD::barDumpBig($var, $title); } } @@ -171,7 +160,7 @@ function bdb($var, $title = NULL) { if(!function_exists('d') && in_array('d', $this->data['enabledShortcutMethods'])) { function d($var, $title = NULL, $options = []) { if(tracyUnavailable() && PHP_SAPI !== 'cli') return false; - return TD::dump($var, $title, $options); + return \ProcessWire\TD::dump($var, $title, $options); } } @@ -182,7 +171,7 @@ function d($var, $title = NULL, $options = []) { if(!function_exists('db') && in_array('db', $this->data['enabledShortcutMethods'])) { function db($var, $title = NULL, $options = []) { if(tracyUnavailable() && PHP_SAPI !== 'cli') return false; - return TD::dumpBig($var, $title, $options); + return \ProcessWire\TD::dumpBig($var, $title, $options); } } @@ -192,8 +181,8 @@ function db($var, $title = NULL, $options = []) { */ if(!function_exists('be') && in_array('be', $this->data['enabledShortcutMethods'])) { function be($str, $title = NULL) { - if(tracyUnavailable() && !\TracyDebugger::getDataValue('recordGuestDumps')) return false; - return TD::barEcho($str, $title); + if(tracyUnavailable() && !\ProcessWire\TracyDebugger::getDataValue('recordGuestDumps')) return false; + return \ProcessWire\TD::barEcho($str, $title); } } @@ -204,7 +193,7 @@ function be($str, $title = NULL) { if(!function_exists('l') && in_array('l', $this->data['enabledShortcutMethods'])) { function l($message, $priority = 'info') { if(tracyUnavailable()) return false; - return TD::log($message, $priority); + return \ProcessWire\TD::log($message, $priority); } } @@ -215,20 +204,7 @@ function l($message, $priority = 'info') { if(!function_exists('t') && in_array('t', $this->data['enabledShortcutMethods'])) { function t($name = NULL) { if(tracyUnavailable()) return false; - return TD::timer($name); - } -} - -/** - * TD::fireLog() shortcut. - * @tracySkipLocation - */ -if(method_exists('Tracy\Debugger', 'getFireLogger')) { - if(!function_exists('fl') && in_array('fl', $this->data['enabledShortcutMethods'])) { - function fl($message = NULL) { - if(tracyUnavailable()) return false; - return TD::fireLog($message); - } + return \ProcessWire\TD::timer($name); } } @@ -239,7 +215,7 @@ function fl($message = NULL) { if(!function_exists('bp') && in_array('bp', $this->data['enabledShortcutMethods'])) { function bp($name = null, $enforceParent = null) { if(tracyUnavailable() || !class_exists('\Zarganwar\PerformancePanel\Register')) return false; - return TD::addBreakpoint($name, $enforceParent); + return \ProcessWire\TD::addBreakpoint($name, $enforceParent); } } @@ -250,6 +226,6 @@ function bp($name = null, $enforceParent = null) { if(!function_exists('tv') && in_array('tv', $this->data['enabledShortcutMethods'])) { function tv($vars) { if(tracyUnavailable()) return false; - return TD::templateVars($vars); + return \ProcessWire\TD::templateVars($vars); } } diff --git a/includes/SlackLogger.php b/includes/SlackLogger.php index d50da810..4ee95294 100644 --- a/includes/SlackLogger.php +++ b/includes/SlackLogger.php @@ -1,8 +1,4 @@ -endpoint = 'chat.postMessage'; $this->query = [ - "token" => \TracyDebugger::getDataValue('slackAppOauthToken'), + "token" => TracyDebugger::getDataValue('slackAppOauthToken'), "text" => $message, - "channel" => \TracyDebugger::getDataValue('slackChannel'), + "channel" => TracyDebugger::getDataValue('slackChannel'), "username" => 'TracyDebugger', "icon_emoji" => ':beetle:', ]; @@ -46,7 +42,7 @@ private function send() { curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); $result = curl_exec($ch); if (PHP_MAJOR_VERSION < 8) curl_close($ch); return $result; diff --git a/includes/TD.php b/includes/TD.php index f94bb4e2..0826d1b6 100644 --- a/includes/TD.php +++ b/includes/TD.php @@ -1,4 +1,4 @@ -getPageFinder()->find(new Selectors($selector), array('returnVerbose' => true,'returnQuery' => true))->getDebugQuery(); } - public static function editLinks($pp) { return $pp->implode('{title}
'); } - public static function viewLinks($pp) { return $pp->implode('{title}
'); } - /** * These are here so that they are available even when user is not allowed or module not enabled so we * don't get a undefined function error when calling these from a template file. */ - protected static function tracyUnavailable() { - if(!\TracyDebugger::getDataValue('enabled') || \TracyDebugger::$allowedTracyUser != 'development' || !class_exists('\Tracy\Debugger')) { + if(!TracyDebugger::getDataValue('enabled') || TracyDebugger::$allowedTracyUser != 'development' || !class_exists('\Tracy\Debugger')) { return true; } else { @@ -41,9 +37,6 @@ public static function debugAll($var, $title = NULL, $options = []) { if(self::tracyUnavailable()) return false; static::barDump($var, $title, $options); static::dump($var, $title, $options); - if(method_exists('Tracy\Debugger', 'getFireLogger')) { - static::fireLog($var); - } static::log($var); } @@ -52,7 +45,7 @@ public static function debugAll($var, $title = NULL, $options = []) { * @tracySkipLocation */ public static function barEcho($str, $title = null) { - if(self::tracyUnavailable() && !\TracyDebugger::getDataValue('recordGuestDumps')) return false; + if(self::tracyUnavailable() && !TracyDebugger::getDataValue('recordGuestDumps')) return false; static::dumpToBar($str, $title, null, true); } @@ -61,7 +54,7 @@ public static function barEcho($str, $title = null) { * @tracySkipLocation */ public static function barDump($var, $title = NULL, $options = []) { - if(self::tracyUnavailable() && !\TracyDebugger::getDataValue('recordGuestDumps')) return false; + if(self::tracyUnavailable() && !TracyDebugger::getDataValue('recordGuestDumps')) return false; if(is_array($title)) { $options = $title; $title = NULL; @@ -71,10 +64,9 @@ public static function barDump($var, $title = NULL, $options = []) { if(isset($options[1])) $options['maxLength'] = $options[1]; if(isset($options[2])) $options['maxItems'] = $options[2]; } - - $options[Dumper::DEPTH] = isset($options['maxDepth']) ? $options['maxDepth'] : \TracyDebugger::getDataValue('maxDepth'); - $options[Dumper::TRUNCATE] = isset($options['maxLength']) ? $options['maxLength'] : \TracyDebugger::getDataValue('maxLength'); - if(defined('\Tracy\Dumper::ITEMS')) $options[Dumper::ITEMS] = isset($options['maxItems']) ? $options['maxItems'] : \TracyDebugger::getDataValue('maxItems'); + $options[Dumper::DEPTH] = isset($options['maxDepth']) ? $options['maxDepth'] : TracyDebugger::getDataValue('maxDepth'); + $options[Dumper::TRUNCATE] = isset($options['maxLength']) ? $options['maxLength'] : TracyDebugger::getDataValue('maxLength'); + if(defined('\Tracy\Dumper::ITEMS')) $options[Dumper::ITEMS] = isset($options['maxItems']) ? $options['maxItems'] : TracyDebugger::getDataValue('maxItems'); $options[Dumper::LOCATION] = Debugger::$showLocation; if(version_compare(PHP_VERSION, '7.2.0', '>=')) { $options[Dumper::KEYS_TO_HIDE] = Debugger::$keysToHide; @@ -83,13 +75,12 @@ public static function barDump($var, $title = NULL, $options = []) { static::dumpToBar($var, $title, $options); } - /** * Tracy\Debugger::barDumpBig() shortcut dumping with maxDepth = 6, maxLength = 9999, and maxItems = 250. * @tracySkipLocation */ public static function barDumpBig($var, $title = NULL, $options = []) { - if(self::tracyUnavailable() && !\TracyDebugger::getDataValue('recordGuestDumps')) return false; + if(self::tracyUnavailable() && !TracyDebugger::getDataValue('recordGuestDumps')) return false; if(is_array($title)) { $options = $title; $title = NULL; @@ -120,7 +111,6 @@ public static function dump($var, $title = NULL, $options = []) { $options = $title; $title = NULL; } - if(PHP_SAPI === 'cli') { echo $title . PHP_EOL; if(is_array($var) || is_object($var)) { @@ -132,18 +122,17 @@ public static function dump($var, $title = NULL, $options = []) { return false; } else { - if(isset($options) && is_array($options) && !static::has_string_keys($options)) { if(isset($options[0])) $options['maxDepth'] = $options[0]; if(isset($options[1])) $options['maxLength'] = $options[1]; if(isset($options[2])) $options['maxItems'] = $options[2]; } - $options[Dumper::DEPTH] = isset($options['maxDepth']) ? $options['maxDepth'] : \TracyDebugger::getDataValue('maxDepth'); - $options[Dumper::TRUNCATE] = isset($options['maxLength']) ? $options['maxLength'] : \TracyDebugger::getDataValue('maxLength'); + $options[Dumper::DEPTH] = isset($options['maxDepth']) ? $options['maxDepth'] : TracyDebugger::getDataValue('maxDepth'); + $options[Dumper::TRUNCATE] = isset($options['maxLength']) ? $options['maxLength'] : TracyDebugger::getDataValue('maxLength'); if(defined('\Tracy\Dumper::ITEMS')) { - $options[Dumper::ITEMS] = isset($options['maxItems']) ? $options['maxItems'] : \TracyDebugger::getDataValue('maxItems'); + $options[Dumper::ITEMS] = isset($options['maxItems']) ? $options['maxItems'] : TracyDebugger::getDataValue('maxItems'); } - $options[Dumper::LOCATION] = \TracyDebugger::$fromConsole ? false : Debugger::$showLocation; + $options[Dumper::LOCATION] = TracyDebugger::$fromConsole ? false : Debugger::$showLocation; if(version_compare(PHP_VERSION, '7.2.0', '>=')) { $options[Dumper::KEYS_TO_HIDE] = Debugger::$keysToHide; } @@ -168,7 +157,6 @@ public static function dumpBig($var, $title = NULL, $options = []) { $options = $title; $title = NULL; } - if(PHP_SAPI === 'cli') { echo $title . PHP_EOL; if(is_array($var) || is_object($var)) { @@ -180,7 +168,6 @@ public static function dumpBig($var, $title = NULL, $options = []) { return false; } else { - if(isset($options) && is_array($options) && !static::has_string_keys($options)) { if(isset($options[0])) $options['maxDepth'] = $options[0]; if(isset($options[1])) $options['maxLength'] = $options[1]; @@ -189,7 +176,7 @@ public static function dumpBig($var, $title = NULL, $options = []) { $options[Dumper::DEPTH] = 6; $options[Dumper::TRUNCATE] = 9999; if(defined('\Tracy\Dumper::ITEMS')) $options[Dumper::ITEMS] = 250; - $options[Dumper::LOCATION] = \TracyDebugger::$fromConsole ? false : Debugger::$showLocation; + $options[Dumper::LOCATION] = TracyDebugger::$fromConsole ? false : Debugger::$showLocation; if(version_compare(PHP_VERSION, '7.2.0', '>=')) { $options[Dumper::KEYS_TO_HIDE] = Debugger::$keysToHide; } @@ -212,14 +199,15 @@ private static function dumpToBar($var, $title = NULL, $options = [], $echo = fa $dumpItem = array(); $dumpItem['title'] = $title; $dumpItem['dump'] = $echo ? '
' . $var . '
' : static::generateDump($var, $options); - array_push(\TracyDebugger::$dumpItems, $dumpItem); - - if((self::tracyUnavailable() && \TracyDebugger::getDataValue('recordGuestDumps')) || (isset(\TracyDebugger::$showPanels) && in_array('dumpsRecorder', \TracyDebugger::$showPanels))) { + array_push(TracyDebugger::$dumpItems, $dumpItem); + if((self::tracyUnavailable() && TracyDebugger::getDataValue('recordGuestDumps')) || (isset(TracyDebugger::$showPanels) && in_array('dumpsRecorder', TracyDebugger::$showPanels))) { + $dumpItem['user'] = wire('user')->name; + $dumpItem['time'] = date('H:i:s'); $dumpsFile = wire('config')->paths->cache . 'TracyDebugger/dumps.json'; $dumpsRecorderItems = file_exists($dumpsFile) ? json_decode(file_get_contents($dumpsFile), true) : array(); if(!$dumpsRecorderItems) $dumpsRecorderItems = array(); array_push($dumpsRecorderItems, $dumpItem); - wire('files')->filePutContents($dumpsFile, json_encode($dumpsRecorderItems)); + wire('files')->filePutContents($dumpsFile, json_encode($dumpsRecorderItems), LOCK_EX); } } @@ -229,61 +217,58 @@ private static function dumpToBar($var, $title = NULL, $options = [], $echo = fa */ private static function generateDump($var, $options) { // standard options for all dump/barDump variations - $options[Dumper::COLLAPSE] = isset($options['collapse']) ? $options['collapse'] : \TracyDebugger::getDataValue('collapse'); - $options[Dumper::COLLAPSE_COUNT] = isset($options['collapse_count']) ? $options['collapse_count'] : \TracyDebugger::getDataValue('collapse_count'); - $options[Dumper::DEBUGINFO] = isset($options['debugInfo']) ? $options['debugInfo'] : \TracyDebugger::getDataValue('debugInfo'); + $options[Dumper::COLLAPSE] = isset($options['collapse']) ? $options['collapse'] : TracyDebugger::getDataValue('collapse'); + $options[Dumper::COLLAPSE_COUNT] = isset($options['collapse_count']) ? $options['collapse_count'] : TracyDebugger::getDataValue('collapse_count'); + $options[Dumper::DEBUGINFO] = isset($options['debugInfo']) ? $options['debugInfo'] : TracyDebugger::getDataValue('debugInfo'); $out = '
'; $editCountLink = ''; - if(count(\TracyDebugger::getDataValue('dumpPanelTabs')) > 0 && !is_string($var)) { + if(count(TracyDebugger::getDataValue('dumpPanelTabs')) > 0 && !is_string($var)) { $classExt = rand(); - if(($var instanceof Wire || $var instanceof \ProcessWire\Wire)) { - if($var instanceof User || $var instanceof \ProcessWire\User) { + if($var instanceof Wire) { + if($var instanceof User) { $type = 'users'; $section = 'access'; } - elseif($var instanceof Role || $var instanceof \ProcessWire\Role) { + elseif($var instanceof Role) { $type = 'roles'; $section = 'access'; } - elseif($var instanceof Permission || $var instanceof \ProcessWire\Permission) { + elseif($var instanceof Permission) { $type = 'permissions'; $section = 'access'; } - elseif($var instanceof Language || $var instanceof \ProcessWire\Language) { + elseif($var instanceof Language) { $type = 'languages'; $section = 'setup'; } - elseif($var instanceof Page || $var instanceof \ProcessWire\Page) { + elseif($var instanceof Page) { $type = 'page'; $section = ''; } - elseif($var instanceof Template || $var instanceof \ProcessWire\Template) { + elseif($var instanceof Template) { $type = 'template'; $section = 'setup'; } - elseif($var instanceof Field || $var instanceof \ProcessWire\Field) { + elseif($var instanceof Field) { $type = 'field'; $section = 'setup'; } - elseif($var instanceof Module || $var instanceof \ProcessWire\Module) { + elseif($var instanceof Module) { $type = 'module'; $section = ''; } - if(isset($type)) $editCountLink .= self::generateEditViewLinks($var, $type, $section); } - - if($var instanceof WireArray || $var instanceof \ProcessWire\WireArray) { + if($var instanceof WireArray) { $editCountLink .= '
  • n = ' . $var->count() . '
  • '; } - $tabs = '
      '; $tabDivs = '
      '; - $expandCollapseAll = is_string($var) || is_null($var) ? '' : '+ '; + $expandCollapseAll = is_string($var) || is_null($var) ? '' : '+ '; $numTabs = 0; - foreach(\TracyDebugger::getDataValue('dumpPanelTabs') as $i => $panel) { + foreach(TracyDebugger::getDataValue('dumpPanelTabs') as $i => $panel) { if($panel == 'debugInfo') { $options[Dumper::DEBUGINFO] = true; } @@ -291,27 +276,24 @@ private static function generateDump($var, $options) { $options[Dumper::DEBUGINFO] = false; } else { - $options[Dumper::DEBUGINFO] = isset($options['debugInfo']) ? $options['debugInfo'] : \TracyDebugger::getDataValue('debugInfo'); + $options[Dumper::DEBUGINFO] = isset($options['debugInfo']) ? $options['debugInfo'] : TracyDebugger::getDataValue('debugInfo'); } $currentDump = $expandCollapseAll . Dumper::toHtml($panel == 'iterator' && is_object($var) && method_exists($var, 'getIterator') ? self::humanize($var->getIterator()) : $var, $options); if(!isset($lastDump) || (isset($lastDump) && $currentDump !== $lastDump)) { $numTabs++; - $tabs .= '
    • '.\TracyDebugger::$dumpPanelTabs[$panel].'
    • '; + $tabs .= '
    • '.TracyDebugger::$dumpPanelTabs[$panel].'
    • '; $tabDivs .= ''; } $lastDump = $currentDump; - } $tabs .= $editCountLink . '
    '; $tabDivs .= '
    '; - if($numTabs > 1) { $out .= $tabs . $tabDivs; } else { $out .= '
    ' . $lastDump . '
    '; } - } else { $out .= '
    ' . Dumper::toHtml($var, $options) . '
    '; @@ -336,7 +318,6 @@ private static function humanize($arrayObject) { } return $arrayObject; } - /** * Generate edit link for various PW objects. * @tracySkipLocation @@ -374,16 +355,6 @@ public static function timer($name = NULL) { } } - /** - * Tracy\Debugger::fireLog() shortcut. - * @tracySkipLocation - */ - public static function fireLog($message = NULL) { - if(!method_exists('Tracy\Debugger', 'getFireLogger') || self::tracyUnavailable()) return false; - return Debugger::fireLog($message); - } - - /** * Zarganwar\PerformancePanel\Register::add() shortcut. * @tracySkipLocation @@ -399,7 +370,7 @@ public static function addBreakpoint($name = null, $enforceParent = null) { */ public static function templateVars($vars) { if(self::tracyUnavailable()) return false; - return \TracyDebugger::templateVars((array) $vars); + return TracyDebugger::templateVars((array) $vars); } /** @@ -411,5 +382,4 @@ public static function templateVars($vars) { private static function has_string_keys(array $array) { return count(array_filter(array_keys($array), 'is_string')) > 0; } - } diff --git a/includes/post_processors/AdminToolsPost.php b/includes/post_processors/AdminToolsPost.php new file mode 100644 index 00000000..4f3c35f7 --- /dev/null +++ b/includes/post_processors/AdminToolsPost.php @@ -0,0 +1,81 @@ +wire('input')->post->deleteChildren || $this->wire('input')->post->deleteLanguage || $this->wire('input')->post->deleteTemplate || $this->wire('input')->post->deleteField || $this->wire('input')->post->changeFieldType || $this->wire('input')->post->uninstallModule) && $this->wire('session')->CSRF->validate()) { + // delete children + if($this->wire('input')->post->deleteChildren) { + foreach($this->wire('pages')->get((int)$this->wire('input')->post->adminToolsId)->children("include=all") as $child) { + $child->delete(true); + } + } + // delete language + if($this->wire('input')->post->deleteLanguage) { + $lang = $this->wire('pages')->get((int)$this->wire('input')->post->adminToolsId); + $lang_parent = $lang->parent; + $lang->addStatus(Page::statusSystemOverride); + $lang->removeStatus('system'); + $lang->save(); + $lang->removeStatus(Page::statusSystem); + $lang->save(); + $this->wire('pages')->delete($lang); + $this->wire('session')->redirect($lang_parent->url); + } + // delete template + if($this->wire('input')->post->deleteTemplate) { + foreach($this->wire('pages')->find("template=".(int)$this->wire('input')->post->adminToolsId.", include=all") as $p) { + $p->delete(); + } + $template = $this->wire('templates')->get((int)$this->wire('input')->post->adminToolsId); + $this->wire('templates')->delete($template); + $templateName = $template->name; + $fieldgroup = $this->wire('fieldgroups')->get($templateName); + if($fieldgroup) $this->wire('fieldgroups')->delete($fieldgroup); + $this->wire('session')->redirect($this->wire('config')->urls->admin); + } + // delete field + if($this->wire('input')->post->deleteField) { + $field = $this->wire('fields')->get((int)$this->wire('input')->post->adminToolsId); + foreach($this->wire('templates') as $template) { + if(!$template->hasField($field)) continue; + $template->fields->remove($field); + $template->fields->save(); + } + $this->wire('fields')->delete($field); + $this->wire('session')->redirect($this->wire('config')->urls->admin.'setup/field'); + } + // change field type + if($this->wire('input')->post->changeFieldType) { + $field = $this->wire('fields')->get((int)$this->wire('input')->post->adminToolsId); + $field->type = $this->wire('input')->post->changeFieldType; + $field->save(); + } + // uninstall module + if($this->wire('input')->post->uninstallModule) { + $moduleName = $this->wire('input')->post->adminToolsName; + $reason = $this->wire('modules')->isUninstallable($moduleName, true); + $class = $this->wire('modules')->getModuleClass($moduleName); + if($reason !== true) { + if(strpos($reason, 'Fieldtype') !== false) { + foreach($this->wire('fields') as $field) { + $fieldtype = wireClassName($field->type, false); + if($fieldtype == $class) { + foreach($this->wire('templates') as $template) { + if(!$template->hasField($field)) continue; + $template->fields->remove($field); + $template->fields->save(); + } + $this->wire('fields')->delete($field); + } + } + } + elseif(strpos($reason, 'required') !== false) { + $dependents = $this->wire('modules')->getRequiresForUninstall($class); + foreach($dependents as $dependent) { + $this->wire('modules')->uninstall($dependent); + } + } + } + $this->wire('modules')->uninstall($moduleName); + $this->wire('session')->redirect($this->wire('config')->urls->admin.'module'); + } +} diff --git a/includes/post_processors/EmailSentFlagPost.php b/includes/post_processors/EmailSentFlagPost.php new file mode 100644 index 00000000..315cc19d --- /dev/null +++ b/includes/post_processors/EmailSentFlagPost.php @@ -0,0 +1,20 @@ +wire('config')->paths->logs.'tracy/email-sent'; +if($this->wire('input')->post->clearEmailSent || $this->wire('input')->get->clearEmailSent) { + if(file_exists($emailSentPath)) { + $removed = unlink($emailSentPath); + } + if (!isset($removed) || !$removed) { + $this->wire()->error( __('No file to remove')); + } + else { + $this->wire()->message(__("email-sent file deleted successfully")); + $this->wire('session')->redirect(str_replace(array('?clearEmailSent=1', '&clearEmailSent=1'), '', $this->wire('input')->url(true))); + } +} + +if(file_exists($emailSentPath)) { + $this->wire()->warning('Tracy Debugger "Email Sent" flag has been set. Clear it to continue receiving further emails', Notice::allowMarkup); +} diff --git a/includes/post_processors/FileEditorPost.php b/includes/post_processors/FileEditorPost.php new file mode 100644 index 00000000..1f451a5d --- /dev/null +++ b/includes/post_processors/FileEditorPost.php @@ -0,0 +1,74 @@ +wire('input')->post->fileEditorFilePath && $this->wire('session')->CSRF->validate()) { + $rawCode = base64_decode($this->wire('input')->post->tracyFileEditorRawCode); + if(static::$inAdmin && + $this->data['referencePageEdited'] && + $this->wire('input')->get('id') && + $this->wire('pages')->get($this->wire('input')->get('id'))->template->filename === $this->wire('config')->paths->root . $this->wire('input')->post->fileEditorFilePath + ) { + $p = $this->wire('pages')->get($this->wire('input')->get('id')); + } + else { + $p = $this->wire('page'); + } + + $templateExt = pathinfo($p->template->filename, PATHINFO_EXTENSION); + $this->tempTemplateFilename = str_replace('.'.$templateExt, '-tracytemp.'.$templateExt, $p->template->filename); + // if changes to the template of the current page are submitted + // test + if($this->wire('input')->post->tracyTestTemplateCode) { + if(!$this->wire('files')->filePutContents($this->tempTemplateFilename, $rawCode, LOCK_EX)) { + throw new WireException("Unable to write file: " . $this->tempTemplateFilename); + } + $p->template->filename = $this->tempTemplateFilename; + } + + // if changes to any other file are submitted + if($this->wire('input')->post->tracyTestFileCode || $this->wire('input')->post->tracySaveFileCode || $this->wire('input')->post->tracyChangeTemplateCode) { + $rootPath = $this->wire('config')->paths->root; + $filePath = str_replace('\\', '/', realpath($rootPath . $this->wire('input')->post->fileEditorFilePath)); + if($this->wire('input')->post->fileEditorFilePath != '' && $filePath !== false && strpos($filePath, $rootPath) === 0) { + $rawCode = base64_decode($this->wire('input')->post->tracyFileEditorRawCode); + + // backup old version to Tracy cache directory + $cachePath = $this->tracyCacheDir . $this->wire('input')->post->fileEditorFilePath; + if(!is_dir($cachePath)) if(!wireMkdir(pathinfo($cachePath, PATHINFO_DIRNAME), true)) { + throw new WireException("Unable to create cache path: $cachePath"); + } + copy($filePath, $cachePath); + + if(!$this->wire('files')->filePutContents($filePath, $rawCode, LOCK_EX)) { + throw new WireException("Unable to write file: " . $filePath); + } + if($this->wire('config')->chmodFile && PHP_OS_FAMILY !== 'Windows') chmod($filePath, octdec($this->wire('config')->chmodFile)); + + if($this->wire('input')->post->tracyTestFileCode) { + if(PHP_VERSION_ID >= 70300) { + setcookie('tracyTestFileEditor', $this->wire('input')->post->fileEditorFilePath, ['expires' => time() + (10 * 365 * 24 * 60 * 60), 'path' => '/', 'samesite' => 'Strict']); + } else { + setcookie('tracyTestFileEditor', $this->wire('input')->post->fileEditorFilePath, time() + (10 * 365 * 24 * 60 * 60), '/'); + } + } + } + $this->wire('session')->redirect($this->httpReferer); + } + } + + // if file editor restore + if($this->wire('input')->post->tracyRestoreFileEditorBackup && $this->wire('session')->CSRF->validate()) { + $rootPath = $this->wire('config')->paths->root; + $editorPath = $this->wire('input')->post->fileEditorFilePath ?: $this->wire('input')->cookie->tracyTestFileEditor; + $this->filePath = str_replace('\\', '/', realpath($rootPath . $editorPath)); + $this->cachePath = str_replace('\\', '/', realpath($this->tracyCacheDir . $editorPath)); + if($this->filePath !== false && strpos($this->filePath, $rootPath) === 0 && + $this->cachePath !== false && strpos($this->cachePath, $this->tracyCacheDir) === 0) { + copy($this->cachePath, $this->filePath); + unlink($this->cachePath); + } + $this->wire('session')->redirect($this->httpReferer); + } + } diff --git a/includes/post_processors/LanguageSwitcherPost.php b/includes/post_processors/LanguageSwitcherPost.php new file mode 100644 index 00000000..fb791a11 --- /dev/null +++ b/includes/post_processors/LanguageSwitcherPost.php @@ -0,0 +1,32 @@ +wire('input')->post->tracyLanguageSwitcher && $this->wire('session')->CSRF->validate()) || $this->wire('session')->tracyLanguageSwitcher)) { + $langId = $this->wire('input')->post->int('tracyLanguageSwitcher'); + if($langId) { + // compare language setting from session with users profile + if($this->wire('user')->language->id === $langId) { + // language is users profile language -> reset session + $this->wire('session')->remove('tracyLanguageSwitcher'); + } + else { + // language is different from profile -> save it + $this->wire('session')->set('tracyLanguageSwitcher', $langId); + } + } + + // reset cache for nav + // thx @toutouwai https://github.com/Toutouwai/CustomAdminMenus/blob/8dfdfa7d07c40ab2d93e3191d2d960e317738169/CustomAdminMenus.module#L35 + $this->wire('session')->removeFor('AdminThemeUikit', 'prnav'); + $this->wire('session')->removeFor('AdminThemeUikit', 'sidenav'); + + if($this->wire('input')->post->tracyResetLanguageSwitcher) { + $this->wire('session')->remove('tracyLanguageSwitcher'); + } + // set users language dynamically from session value + elseif($sessionLangId = $this->wire('session')->get('tracyLanguageSwitcher')) { + $this->wire('user')->language = $this->wire('languages')->get($sessionLangId); + } + } diff --git a/includes/post_processors/PageFilesPost.php b/includes/post_processors/PageFilesPost.php new file mode 100644 index 00000000..d289c41d --- /dev/null +++ b/includes/post_processors/PageFilesPost.php @@ -0,0 +1,29 @@ +wire('input')->post->deleteOrphanFiles && $this->wire('input')->post->orphanPaths && $this->wire('session')->CSRF->validate()) { + $rootPath = $this->wire('config')->paths->root; + foreach(explode('|', $this->wire('input')->post->orphanPaths) as $filePath) { + $realPath = str_replace('\\', '/', realpath($filePath)); + if($realPath !== false && strpos($realPath, $rootPath) === 0 && file_exists($realPath)) { + unlink($realPath); + } + } + $this->wire('session')->redirect($this->httpReferer); +} +// delete missing pagefiles if requested +if($this->wire('input')->post->deleteMissingFiles && $this->wire('input')->post->missingPaths && $this->wire('session')->CSRF->validate()) { + $decodedPaths = json_decode(urldecode($this->wire('input')->post->missingPaths), true); + if(is_array($decodedPaths)) { + foreach($decodedPaths as $pid => $files) { + $p = $this->wire('pages')->get($pid); + foreach($files as $file) { + $pagefile = $p->{$file['field']}->get(pathinfo($file['filename'], PATHINFO_BASENAME)); + $p->{$file['field']}->delete($pagefile); + $p->save($file['field']); + } + } + } + $this->wire('session')->redirect($this->httpReferer); +} diff --git a/includes/post_processors/PageRecorderPost.php b/includes/post_processors/PageRecorderPost.php new file mode 100644 index 00000000..ebdc1226 --- /dev/null +++ b/includes/post_processors/PageRecorderPost.php @@ -0,0 +1,15 @@ +wire('input')->post->trashRecordedPages || $this->wire('input')->post->clearRecordedPages) && $this->wire('session')->CSRF->validate()) { + if($this->wire('input')->post->trashRecordedPages) { + foreach($this->data['recordedPages'] as $pid) { + $this->wire('pages')->trash($this->wire('pages')->get($pid)); + } + } + $configData = $this->wire('modules')->getModuleConfigData("TracyDebugger"); + unset($configData['recordedPages']); + $this->wire('modules')->saveModuleConfigData($this, $configData); + $this->wire('session')->redirect($this->httpReferer); +} diff --git a/includes/post_processors/ProcesswireLogsPost.php b/includes/post_processors/ProcesswireLogsPost.php new file mode 100644 index 00000000..604f5468 --- /dev/null +++ b/includes/post_processors/ProcesswireLogsPost.php @@ -0,0 +1,12 @@ +wire('input')->post->deleteProcessWireLogs && $this->wire('session')->CSRF->validate()) { + $files = glob($this->wire('config')->paths->logs.'*'); + foreach($files as $file) { + if(is_file($file)) { + unlink($file); + } + } +} diff --git a/includes/post_processors/RequestLoggerPost.php b/includes/post_processors/RequestLoggerPost.php new file mode 100644 index 00000000..720ba2b4 --- /dev/null +++ b/includes/post_processors/RequestLoggerPost.php @@ -0,0 +1,24 @@ +wire('input')->post->tracyRequestLoggerEnableLogging || $this->wire('input')->post->tracyRequestLoggerDisableLogging) && $this->wire('session')->CSRF->validate()) { + $configData = $this->wire('modules')->getModuleConfigData("TracyDebugger"); + if($this->wire('input')->post->tracyRequestLoggerEnableLogging) { + if(!isset($configData['requestLoggerPages'])) $configData['requestLoggerPages'] = array(); + array_push($configData['requestLoggerPages'], $this->wire('input')->post->requestLoggerLogPageId); + } + else { + if(($key = array_search($this->wire('input')->post->requestLoggerLogPageId, $configData['requestLoggerPages'])) !== false) { + unset($configData['requestLoggerPages'][$key]); + } + $data = $this->wire('cache')->get("tracyRequestLogger_id_*_page_".$this->wire('input')->post->requestLoggerLogPageId); + if(count($data) > 0) { + foreach($data as $id => $datum) { + $this->wire('cache')->delete($id); + } + } + } + $this->wire('modules')->saveModuleConfigData($this, $configData); + $this->wire('session')->redirect($this->httpReferer); +} diff --git a/includes/post_processors/TracyLogsPost.php b/includes/post_processors/TracyLogsPost.php new file mode 100644 index 00000000..8e68168d --- /dev/null +++ b/includes/post_processors/TracyLogsPost.php @@ -0,0 +1,8 @@ +wire('input')->post->deleteTracyLogs && $this->wire('session')->CSRF->validate()) { + wireRmdir($logFolder, true); + wireMkdir($logFolder); +} diff --git a/includes/post_processors/UserSwitcherPost.php b/includes/post_processors/UserSwitcherPost.php new file mode 100644 index 00000000..0806eb80 --- /dev/null +++ b/includes/post_processors/UserSwitcherPost.php @@ -0,0 +1,80 @@ +wire('input')->post->userSwitcher) { + // if user is superuser and session length is set, save to config settings + if(static::$allowedSuperuser && ($this->wire('input')->post->userSwitcher || $this->wire('input')->post->logoutUserSwitcher) && $this->wire('session')->CSRF->validate()) { + // cleanup expired sessions + if(isset($this->data['userSwitchSession'])) { + foreach($this->data['userSwitchSession'] as $id => $expireTime) { + if($expireTime < time()) unset($this->data['userSwitchSession'][$id]); + } + } + // if no existing session ID, start a new session + if(!$this->wire('session')->tracyUserSwitcherId) { + $pass = new Password(); + $challenge = $pass->randomBase64String(32); + $this->wire('session')->tracyUserSwitcherId = $challenge; + + $configData = $this->wire('modules')->getModuleConfigData("TracyDebugger"); + $this->data['originalUserSwitcher'][$this->wire('session')->tracyUserSwitcherId] = $this->wire('user')->name; + $configData['originalUserSwitcher'] = $this->data['originalUserSwitcher']; + $this->wire('modules')->saveModuleConfigData($this, $configData); + + } + // save session ID and expiry time in module config settings + $configData = $this->wire('modules')->getModuleConfigData("TracyDebugger"); + $this->data['userSwitchSession'][$this->wire('session')->tracyUserSwitcherId] = time() + $this->wire('config')->sessionExpireSeconds; + $configData['userSwitchSession'] = $this->data['userSwitchSession']; + $this->wire('modules')->saveModuleConfigData($this, $configData); + } + + // if logout button clicked + if($this->wire('input')->post->logoutUserSwitcher && $this->wire('session')->CSRF->validate()) { + if($this->wire('session')->tracyUserSwitcherId) { + // if session variable exists, grab it and add to the new session after logging out + $tracyUserSwitcherId = $this->wire('session')->tracyUserSwitcherId; + $this->wire('session')->logout(); + $this->wire('session')->tracyUserSwitcherId = $tracyUserSwitcherId; + } + else { + $this->wire('session')->logout(); + } + $this->wire('session')->redirect($this->httpReferer); + } + // if end session clicked, remove session variable and config settings entry + elseif($this->wire('input')->post->endSessionUserSwitcher && $this->wire('session')->CSRF->validate()) { + $this->wire('session')->remove("tracyUserSwitcherId"); + $configData = $this->wire('modules')->getModuleConfigData("TracyDebugger"); + unset($this->data['userSwitchSession'][$this->wire('session')->tracyUserSwitcherId]); + unset($configData['userSwitchSession'][$this->wire('session')->tracyUserSwitcherId]); + $this->wire('modules')->saveModuleConfigData($this, $configData); + $this->wire('session')->redirect($this->httpReferer); + } + // if session not expired, switch to original user + elseif($this->wire('input')->post->revertOriginalUserSwitcher && $this->wire('session')->CSRF->validate()) { + if(isset($this->data['userSwitchSession'][$this->wire('session')->tracyUserSwitcherId]) && $this->data['userSwitchSession'][$this->wire('session')->tracyUserSwitcherId] > time() && $this->wire('session')->tracyUserSwitcherId) { + // if session variable exists, grab it and add to the new session after logging out + // and forceLogin the original user + $tracyUserSwitcherId = $this->wire('session')->tracyUserSwitcherId; + if($this->wire('user')->isLoggedin()) $this->wire('session')->logout(); + $this->wire('session')->forceLogin($this->data['originalUserSwitcher'][$tracyUserSwitcherId]); + $this->wire('session')->tracyUserSwitcherId = $tracyUserSwitcherId; + } + $this->wire('session')->redirect($this->httpReferer); + } + // if session not expired, switch to requested user + elseif($this->wire('input')->post->userSwitcher && $this->wire('session')->CSRF->validate()) { + if(isset($this->data['userSwitchSession'][$this->wire('session')->tracyUserSwitcherId]) && $this->data['userSwitchSession'][$this->wire('session')->tracyUserSwitcherId] > time() && $this->wire('session')->tracyUserSwitcherId) { + // if session variable exists, grab it and add to the new session after logging out + // and forceLogin the new switched user + $tracyUserSwitcherId = $this->wire('session')->tracyUserSwitcherId; + if($this->wire('user')->isLoggedin()) $this->wire('session')->logout(); + $this->wire('session')->forceLogin($this->wire('input')->post->userSwitcher); + $this->wire('session')->tracyUserSwitcherId = $tracyUserSwitcherId; + } + $this->wire('session')->redirect($this->httpReferer); + } + } diff --git a/includes/user-bar/UserBar.php b/includes/user-bar/UserBar.php index 8b2a2a8f..823516d3 100644 --- a/includes/user-bar/UserBar.php +++ b/includes/user-bar/UserBar.php @@ -1,16 +1,16 @@ - div#tracyUserBar { - '.\TracyDebugger::getDataValue("userBarTopBottom").': 0px; - '.\TracyDebugger::getDataValue("userBarLeftRight").': 0px; - z-index: '.\TracyDebugger::getDataValue("panelZindex").'; + '.TracyDebugger::getDataValue("userBarTopBottom").': 0px; + '.TracyDebugger::getDataValue("userBarLeftRight").': 0px; + z-index: '.TracyDebugger::getDataValue("panelZindex").'; position: fixed; margin: 0px !important; - background: '.(\TracyDebugger::getDataValue("userBarBackgroundColor") ? \TracyDebugger::getDataValue("userBarBackgroundColor") : 'none') .'; + background: '.(TracyDebugger::getDataValue("userBarBackgroundColor") ? TracyDebugger::getDataValue("userBarBackgroundColor") : 'none') .'; padding: 1px; - opacity: '.\TracyDebugger::getDataValue("userBarBackgroundOpacity").'; + opacity: '.TracyDebugger::getDataValue("userBarBackgroundOpacity").'; line-height: 0; } div#tracyUserBar a { @@ -40,7 +40,7 @@ $page = $this->wire('page'); $pages = $this->wire('pages'); $config = $this->wire('config'); -$iconColor = \TracyDebugger::getDataValue("userBarIconColor"); +$iconColor = TracyDebugger::getDataValue("userBarIconColor"); $userBar .= eval($this->data['userBarCustomFeatures']); $userBar .= ' diff --git a/includes/user-bar/UserBarAdmin.php b/includes/user-bar/UserBarAdmin.php index 6d59e23b..51366f5c 100644 --- a/includes/user-bar/UserBarAdmin.php +++ b/includes/user-bar/UserBarAdmin.php @@ -1,14 +1,15 @@ -hasPermission('page-edit')) { $userBar .= ' - - - + + - urls->admin.'page/edit/?id='.wire('page')->id.'" title="Edit this page"> - + '; -} \ No newline at end of file +} diff --git a/includes/user-bar/UserBarPageVersions.php b/includes/user-bar/UserBarPageVersions.php index f2805028..22a2f28a 100644 --- a/includes/user-bar/UserBarPageVersions.php +++ b/includes/user-bar/UserBarPageVersions.php @@ -1,4 +1,4 @@ -hasPermission('tracy-page-versions')) { @@ -23,7 +23,7 @@ if($countTemplateOptions > 1) { - if(\TracyDebugger::$templatePath) { + if(TracyDebugger::$templatePath) { $iconColor = '#D51616'; } else { @@ -38,7 +38,7 @@ ';*/ $userBar .= ' - - -
    + ' . $this->openPanel() . '
    '; $out .= '

    -   -   +   +  


    '; $out .= $this->entries; - $out .= \TracyDebugger::generatePanelFooter('mailInterceptor', \Tracy\Debugger::timer('mailInterceptor'), strlen($out)); - - $out .= ' -
    '; - - return parent::loadResources() . $out; + return $this->closePanel($out, 'mailInterceptor'); } protected function formatEmailAddress($addresses, $names) { @@ -191,9 +191,9 @@ protected function formatAttachments($attachments) { if(!is_array($attachments)) $attachments = array($attachments); foreach($attachments as $key => $val) { $attachment = is_numeric($key) ? $val : $key; - $styledAttachments .= ''.str_replace($this->wire('config')->paths->root, '/', $attachment) . '
    '; + $styledAttachments .= ''.$this->stripRootPath($attachment) . '
    '; } return $styledAttachments; } -} \ No newline at end of file +} diff --git a/panels/MethodsInfoPanel.php b/panels/MethodsInfoPanel.php index 832e83a1..f8af6d9e 100644 --- a/panels/MethodsInfoPanel.php +++ b/panels/MethodsInfoPanel.php @@ -1,4 +1,6 @@ -icon = ' - - + + '; - return ' - - ' . $this->icon . (\TracyDebugger::getDataValue('showPanelLabels') ? 'Methods Info' : '') . ' - - '; + return $this->buildTab('Methods Info'); } @@ -29,9 +27,9 @@ public function getPanel() { $docsUrl = 'https://adrianbj.github.io/TracyDebugger'; $debugMethodsRootUrl = $docsUrl . '/#/debug-methods?id='; - $out = '

    ' . $this->icon . ' Methods Info

    + $out = $this->buildPanelHeader('Methods Info'); -
    + $out .= $this->openPanel() . '

        


    addBreakpoint($name = NULL, $enforceParent = NULL)

    @@ -104,11 +102,7 @@ public function getPanel() { TD::timer()

    '; - $out .= \TracyDebugger::generatePanelFooter('methodsInfo', \Tracy\Debugger::timer('methodsInfo'), strlen($out), 'methodShortcuts') . ' -
    - '; - - return parent::loadResources() . $out; + return $this->closePanel($out, 'methodsInfo', 'methodShortcuts'); } } diff --git a/panels/ModuleDisablerPanel.php b/panels/ModuleDisablerPanel.php index fb23c21d..b91b68a2 100644 --- a/panels/ModuleDisablerPanel.php +++ b/panels/ModuleDisablerPanel.php @@ -1,4 +1,6 @@ -disabledModules = empty(\TracyDebugger::$disabledModules) ? array() : \TracyDebugger::$disabledModules; + $this->disabledModules = empty(TracyDebugger::$disabledModules) ? array() : TracyDebugger::$disabledModules; if(count($this->disabledModules) > 0) { - $this->iconColor = \TracyDebugger::COLOR_WARN; + $this->iconColor = TracyDebugger::COLOR_WARN; } else { - $this->iconColor = \TracyDebugger::COLOR_NORMAL; + $this->iconColor = TracyDebugger::COLOR_NORMAL; } $this->icon = ' @@ -29,18 +31,14 @@ public function getTab() { '; - return ' - - ' . $this->icon . (\TracyDebugger::getDataValue('showPanelLabels') ? ' Module Disabler' : '') . ' - - '; + return $this->buildTab('Module Disabler'); } public function getPanel() { $out = ' - '; - $out .= '

    '.$this->icon.' Module Disabler

    -
    + $out .= $this->buildPanelHeader('Module Disabler'); + $out .= $this->openPanel() . '
    '; - if(count(\TracyDebugger::$disabableModules) > 0 && $this->wire('config')->advanced && $this->wire('config')->debug) { + if(count(TracyDebugger::$disabableModules) > 0 && $this->wire('config')->advanced && $this->wire('config')->debug) { $out .= '

    Temporarily disable autoload modules. Check to disable.

    Restore Instructions
    If your site is broken after disabling modules you can restore your modules by either one of the following methods:

    • Copy "/site/assets/cache/TracyDebugger/restoremodules.php" to the root of your site and load it in your browser
      OR
    • Execute "/site/assets/cache/TracyDebugger/modulesBackup.sql" manually (via PHPMyAdmin, the command line, etc)


    '; - $out .= '
    '; - foreach(\TracyDebugger::$disabableModules as $name) { + $out .= '
    '; + foreach(TracyDebugger::$disabableModules as $name) { $flags = $this->wire('modules')->getFlags($name); $out .= '
    '; - $out .= \TracyDebugger::generatePanelFooter('moduleDisabler', \Tracy\Debugger::timer('moduleDisabler'), strlen($out)); - - $out .= ' -
    '; - - return parent::loadResources() . $out; + return $this->closePanel($out, 'moduleDisabler'); } } diff --git a/panels/OutputModePanel.php b/panels/OutputModePanel.php index c7be71be..ff534091 100644 --- a/panels/OutputModePanel.php +++ b/panels/OutputModePanel.php @@ -1,4 +1,6 @@ -outputMode = \TracyDebugger::getDataValue('outputMode'); + $this->outputMode = TracyDebugger::getDataValue('outputMode'); if($this->outputMode == 'detect') { - $this->outputMode = \TracyDebugger::$isLocal ? 'development' : 'production'; + $this->outputMode = TracyDebugger::$isLocal ? 'development' : 'production'; } $iconColor = $this->outputMode == 'development' ? $this->developmentColor : $this->productionColor; @@ -43,7 +45,7 @@ public function getTab() { '; - $label = \TracyDebugger::getDataValue('showPanelLabels') ? ucfirst($this->outputMode) : ''; + $label = TracyDebugger::getDataValue('showPanelLabels') ? ucfirst($this->outputMode) : ''; return ' @@ -56,9 +58,9 @@ public function getTab() { public function getPanel() { $out = '

    ' . str_replace('#FFFFFF', $this->{$this->outputMode.'Color'}, $this->{$this->outputMode.'Icon'}) . ' '.ucfirst($this->outputMode).' Mode

    -
    '; + ' . $this->openPanel(); - if(\TracyDebugger::getDataValue('outputMode') == 'detect') { + if(TracyDebugger::getDataValue('outputMode') == 'detect') { $out .= '

    This is automatically switched from the configured "DETECT" mode.

    '; } elseif($this->outputMode == 'development') { @@ -70,12 +72,7 @@ public function getPanel() { $out .= '

    '; - $out .= \TracyDebugger::generatePanelFooter('outputMode', \Tracy\Debugger::timer('outputMode'), strlen($out)); - - $out .= ' -
    '; - - return parent::loadResources() . $out; + return $this->closePanel($out, 'outputMode'); } } diff --git a/panels/PageFilesPanel.php b/panels/PageFilesPanel.php index 4e01dcb9..eb7d0c77 100644 --- a/panels/PageFilesPanel.php +++ b/panels/PageFilesPanel.php @@ -1,9 +1,11 @@ -name); + Debugger::timer($this->name); - if(\TracyDebugger::getDataValue('referencePageEdited') && $this->wire('input')->get('id') && ($this->wire('process') == 'ProcessPageEdit' || $this->wire('process') == 'ProcessUser' || $this->wire('process') == 'ProcessLanguage')) { + if(TracyDebugger::getDataValue('referencePageEdited') && $this->wire('input')->get('id') && ($this->wire('process') == 'ProcessPageEdit' || $this->wire('process') == 'ProcessUser') || $this->wire('process') == 'ProcessLanguage') { $this->p = $this->wire('process')->getPage(); if($this->p instanceof NullPage) { $this->p = $this->wire('pages')->get((int) $this->wire('input')->get('id')); @@ -74,14 +76,14 @@ public function getTab() { $fileField = $fileFields[$pid][$file]; } else { - $style = 'color: ' . \TracyDebugger::COLOR_WARN; + $style = 'color: ' . TracyDebugger::COLOR_WARN; $fileField = ''; $this->orphanFiles[] = $p->filesManager()->path . $file; } $this->filesListStr .= '
    - + ' . (count($this->tempFiles) > 0 ? '' : '') . ' @@ -92,7 +94,7 @@ public function getTab() { foreach($this->missingFiles[$pid] as $missingFile) { $this->filesListStr .= ' - + ' . @@ -109,13 +111,13 @@ public function getTab() { } if($this->numMissingFiles > 0) { - $iconColor = \TracyDebugger::COLOR_ALERT; + $iconColor = TracyDebugger::COLOR_ALERT; } elseif(count($this->orphanFiles) > 0) { - $iconColor = \TracyDebugger::COLOR_WARN; + $iconColor = TracyDebugger::COLOR_WARN; } else { - $iconColor = \TracyDebugger::COLOR_NORMAL; + $iconColor = TracyDebugger::COLOR_NORMAL; } // the svg icon shown in the bar and in the panel header @@ -130,36 +132,35 @@ public function getTab() { $orphanMissingCounts = $this->numMissingFiles . '/' . count($this->orphanFiles) . '/'; } - return "{$this->icon}".(\TracyDebugger::getDataValue('showPanelLabels') ? $this->label : '')." ".$orphanMissingCounts.($numDiskFiles > 0 ? $numDiskFiles : '').""; + return $this->buildTab($this->label, $this->label, ' ' . $orphanMissingCounts . ($numDiskFiles > 0 ? $numDiskFiles : '')); } /** * the panel's HTML code */ public function getPanel() { - $isAdditionalBar = \TracyDebugger::isAdditionalBar(); - $out = "

    {$this->icon} {$this->label}" . ($isAdditionalBar ? " (".$isAdditionalBar.")" : "") . "

    "; - - $out .= ''; + $out = $this->buildPanelHeader($this->label, true, true); // panel body - $out .= '
    '; + $out .= $this->openPanel(); $numOrphanFiles = count($this->orphanFiles); if($numOrphanFiles > 0) { $out .= ' -
    + + '.$this->csrfInput().' - +   '; } if($this->numMissingFiles > 0) { $out .= ' -
    + + '.$this->csrfInput().' - + '; } @@ -169,10 +170,7 @@ public function getPanel() { $out .= '
    '.$this->filesListStr.'
    '; - $out .= \TracyDebugger::generatePanelFooter($this->name, \Tracy\Debugger::timer($this->name), strlen($out)); - $out .= '
    '; - - return parent::loadResources() . $out; + return $this->closePanel($out, $this->name); } diff --git a/panels/PageRecorderPanel.php b/panels/PageRecorderPanel.php index 9fadd319..355b9dfa 100644 --- a/panels/PageRecorderPanel.php +++ b/panels/PageRecorderPanel.php @@ -1,6 +1,6 @@ -recordedPages .= '

    -
    + + '.$this->csrfInput().' -
    + + '.$this->csrfInput().'

    '; - foreach(\TracyDebugger::getDataValue('recordedPages') as $pid) { + foreach(TracyDebugger::getDataValue('recordedPages') as $pid) { $p = $this->wire('pages')->get($pid); if($p->path !=='' && !$p->is("has_parent=".$this->wire('config')->trashPageID)) { $this->recordedPages .= ''.$p->path.'
    '; $this->pageCount++; } } - $this->iconColor = \TracyDebugger::COLOR_WARN; + $this->iconColor = TracyDebugger::COLOR_WARN; } else { $this->recordedPages .= '

    There are currently no recorded pages

    '; - $this->iconColor = \TracyDebugger::COLOR_NORMAL; + $this->iconColor = TracyDebugger::COLOR_NORMAL; } $this->icon = ' @@ -43,29 +45,18 @@ public function getTab() { '; - return ' - - ' . $this->icon . (\TracyDebugger::getDataValue('showPanelLabels') ? 'Page Recorder' : '') . ' ' . ($this->pageCount > 0 ? $this->pageCount : '') . ' - - '; + return $this->buildTab('Page Recorder', 'Page Recorder', ' ' . ($this->pageCount > 0 ? $this->pageCount : '')); } public function getPanel() { - $isAdditionalBar = \TracyDebugger::isAdditionalBar(); - $out = ' -

    ' . $this->icon . ' Page Recorder' . ($isAdditionalBar ? ' ('.$isAdditionalBar.')' : '') . '

    + $out = $this->buildPanelHeader('Page Recorder', false, true); -
    '; + $out .= $this->openPanel(); $out .= $this->recordedPages; - $out .= \TracyDebugger::generatePanelFooter('pageRecorder', \Tracy\Debugger::timer('pageRecorder'), strlen($out)); - - $out .= ' -
    '; - - return parent::loadResources() . $out; + return $this->closePanel($out, 'pageRecorder'); } -} \ No newline at end of file +} diff --git a/panels/PanelSelectorPanel.php b/panels/PanelSelectorPanel.php index 36ff9867..e3e71d74 100644 --- a/panels/PanelSelectorPanel.php +++ b/panels/PanelSelectorPanel.php @@ -1,4 +1,6 @@ -icon = ' - + '; - return ' - - ' . $this->icon . (\TracyDebugger::getDataValue('showPanelLabels') ? ' Panel Selector' : '') . ' - - '; + return $this->buildTab('Panel Selector'); } protected function isOnce($name, $defaultPanels) { - $masterPanels = empty(\TracyDebugger::$stickyPanels) ? $defaultPanels : \TracyDebugger::$stickyPanels; + $masterPanels = empty(TracyDebugger::$stickyPanels) ? $defaultPanels : TracyDebugger::$stickyPanels; - if(empty(\TracyDebugger::$oncePanels)) { + if(empty(TracyDebugger::$oncePanels)) { return false; } else { - if((in_array($name, \TracyDebugger::$oncePanels) && !in_array($name, $masterPanels)) || - (!in_array($name, \TracyDebugger::$oncePanels) && in_array($name, $masterPanels))) { + if((in_array($name, TracyDebugger::$oncePanels) && !in_array($name, $masterPanels)) || + (!in_array($name, TracyDebugger::$oncePanels) && in_array($name, $masterPanels))) { return true; } else { @@ -68,8 +66,8 @@ private function renderPanelCell($panel, $defaultPanels, $showPanels, $onceIcon) $name = $panel['name']; $label = $panel['label']; - $seconds = isset(\TracyDebugger::$panelGenerationTime[$name]['time']) ? \TracyDebugger::$panelGenerationTime[$name]['time'] : ''; - $size = isset(\TracyDebugger::$panelGenerationTime[$name]['size']) ? \TracyDebugger::human_filesize(\TracyDebugger::$panelGenerationTime[$name]['size']) : ''; + $seconds = isset(TracyDebugger::$panelGenerationTime[$name]['time']) ? TracyDebugger::$panelGenerationTime[$name]['time'] : ''; + $size = isset(TracyDebugger::$panelGenerationTime[$name]['size']) ? TracyDebugger::human_filesize(TracyDebugger::$panelGenerationTime[$name]['size']) : ''; $out = '"; } - $configData .= $sectionEnd; + $configData .= $this->sectionEnd(); } // Versions Info if(in_array('versionsList', $panelSections)) { $versionsList = <<< HTML - HTML; - $out .= ' -

    ' . $this->icon . ' Request Info' . ($isAdditionalBar ? ' ('.$isAdditionalBar.')' : '') . '

    -
    - '; + $out .= $this->buildPanelHeader('Request Info', true, true); + $out .= $this->openPanel(); + // all the "non" icon links sections $i=0; - foreach(\TracyDebugger::$requestInfoSections as $name => $label) { + foreach(TracyDebugger::$requestInfoSections as $name => $label) { // get all sections excluding those that are admin "links" $counter = ''; if(strpos($name, 'Links') === false && in_array($name, $panelSections)) { @@ -929,11 +882,11 @@ public function getPanel() { $out .= ' '; - - return parent::loadResources() . $out; + return $this->closePanel($out, 'requestInfo', 'requestInfoPanel'); } @@ -955,16 +905,16 @@ private function generateOutput($p, $f, $outputFormatting) { $out = ''; $value = $outputFormatting ? $this->wire('sanitizer')->entities1($p->getFormatted($f->name)) : $p->getUnformatted($f->name); if(is_string($value) && $outputFormatting) { - $out .= substr($value, 0, \TracyDebugger::getDataValue('maxLength')) . (strlen($value) > 99 ? '... ('.strlen($value).')' : ''); + $out .= substr($value, 0, TracyDebugger::getDataValue('maxLength')) . (strlen($value) > 99 ? '... ('.strlen($value).')' : ''); } else { // trycatch is to prevent panel errors if an image is missing // log the error to the Tracy error logs instead try { - $out .= Dumper::toHtml($value, array(Dumper::LIVE => true, Dumper::DEBUGINFO => \TracyDebugger::getDataValue('debugInfo'), Dumper::DEPTH => 99, Dumper::TRUNCATE => \TracyDebugger::getDataValue('maxLength'), Dumper::COLLAPSE_COUNT => 1, Dumper::COLLAPSE => false)); + $out .= Dumper::toHtml($value, array(Dumper::LIVE => true, Dumper::DEBUGINFO => TracyDebugger::getDataValue('debugInfo'), Dumper::DEPTH => 99, Dumper::TRUNCATE => TracyDebugger::getDataValue('maxLength'), Dumper::COLLAPSE_COUNT => 1, Dumper::COLLAPSE => false)); } catch(Exception $e) { - \TD::log($e); + TD::log($e); } } return $out; @@ -976,7 +926,7 @@ private function imageDetails($p, $f) { $p->of(false); $imageStr = ''; $imagePreview = ''; - $inputfield = \TracyDebugger::getDataValue('imagesInFieldListValues') ? $f->getInputfield($p) : null; + $inputfield = TracyDebugger::getDataValue('imagesInFieldListValues') ? $f->getInputfield($p) : null; if($f->type instanceof FieldtypeRepeater) { $repeaterValue = $p->get($f->name); @@ -1027,7 +977,7 @@ private function getImages($p) { } } elseif($item && $f && $f->type instanceof FieldtypeImage) { - $inputfield = \TracyDebugger::getDataValue('imagesInFieldListValues') ? $f->getInputfield($p) : null; + $inputfield = TracyDebugger::getDataValue('imagesInFieldListValues') ? $f->getInputfield($p) : null; foreach($item as $image) { $imageStr .= $this->imageStr($inputfield, $image); } @@ -1084,7 +1034,7 @@ private function getLanguageVersion($p, $fieldName, $lang, $showDefault = false) $result = $p->$fieldName; } } - return $result == '' && $showDefault ? '' . $p->$fieldName : $result; + return $result == '' && $showDefault ? '' . $p->$fieldName : $result; } else { return $p->$fieldName; diff --git a/panels/RequestLoggerPanel.php b/panels/RequestLoggerPanel.php index 9535c8df..77ccfbd7 100644 --- a/panels/RequestLoggerPanel.php +++ b/panels/RequestLoggerPanel.php @@ -1,4 +1,4 @@ -name); + if(TracyDebugger::isAdditionalBar()) return; + Debugger::timer($this->name); - if(\TracyDebugger::getDataValue('referencePageEdited') && $this->wire('input')->get('id') && $this->wire('process') == 'ProcessPageEdit') { - $this->p = $this->wire('process')->getPage(); - if($this->p instanceof NullPage) { - $this->p = $this->wire('pages')->get((int) $this->wire('input')->get('id')); - } - } - else { - $this->p = $this->wire('page'); - } + $this->p = $this->getReferencePage(array('ProcessPageEdit')); $this->requestData = $this->p->getRequestData('all', true); // true forces array - $this->requestLoggerPages = \TracyDebugger::getDataValue('requestLoggerPages') ?: array(); + $this->requestLoggerPages = TracyDebugger::getDataValue('requestLoggerPages') ?: array(); if(isset($this->requestLoggerPages) && in_array($this->p->id, $this->requestLoggerPages)) { - $this->iconColor = \TracyDebugger::COLOR_WARN; + $this->iconColor = TracyDebugger::COLOR_WARN; } else { - $this->iconColor = \TracyDebugger::COLOR_NORMAL; + $this->iconColor = TracyDebugger::COLOR_NORMAL; } $this->icon = ' @@ -54,7 +48,7 @@ public function getTab() { '; - return "{$this->icon}" . (\TracyDebugger::getDataValue('showPanelLabels') ? $this->label : '') . (count($this->requestData) > 0 ? ' ' . count($this->requestData) : ''); + return "{$this->icon}" . (TracyDebugger::getDataValue('showPanelLabels') ? $this->label : '') . (count($this->requestData) > 0 ? ' ' . count($this->requestData) : ''); } /** @@ -62,16 +56,13 @@ public function getTab() { */ public function getPanel() { - $out = "

    {$this->icon} {$this->label}

    "; - $out .= ''; + $out = $this->buildPanelHeader($this->label, true); // panel body - $out .= '
    '; + $out .= $this->openPanel(); $out .= $this->generateRequestDumps(); - $out .= \TracyDebugger::generatePanelFooter($this->name, \Tracy\Debugger::timer($this->name), strlen($out), 'requestLoggerPanel'); - $out .= '
    '; - - return parent::loadResources() . $out; + $out .= '
    '; + return $this->closePanel($out, $this->name, 'requestLoggerPanel'); } /** @@ -92,7 +83,7 @@ private function generateRequestDumps() { foreach($this->requestData as $datum) { $time = date("Y-m-d H:i:s", $datum['time']); $out .= "

    {$datum['requestMethod']} @ $time | " . ($datum['remoteHost'] ?: $datum['remoteAddress']) . " | #{$datum['id']}

    "; - if(\TracyDebugger::getDataValue('requestLoggerReturnType') == 'object') $datum = json_decode(json_encode($datum), false); + if(TracyDebugger::getDataValue('requestLoggerReturnType') == 'object') $datum = json_decode(json_encode($datum), false); $out .= Dumper::toHtml($datum, array(Dumper::LIVE => true, Dumper::DEPTH => 9, Dumper::TRUNCATE => 99999, Dumper::COLLAPSE => true)); } } @@ -108,7 +99,8 @@ private function generateRequestDumps() { $out .= '

    '; if(!in_array($this->p->id, $this->requestLoggerPages)) { $out .= ' -
    + + '.$this->csrfInput().' @@ -116,7 +108,8 @@ private function generateRequestDumps() { } else { $out .= ' -
    + + '.$this->csrfInput().' @@ -132,7 +125,7 @@ private function generateRequestDumps() {

    diff --git a/panels/TemplatePathPanel.php b/panels/TemplatePathPanel.php index 9c1ad78a..2cf71e55 100644 --- a/panels/TemplatePathPanel.php +++ b/panels/TemplatePathPanel.php @@ -1,4 +1,6 @@ -template->filename, PATHINFO_BASENAME); @@ -23,26 +25,26 @@ public function getTab() { $fileExt = pathinfo($file, PATHINFO_EXTENSION); $baseName = pathinfo($file, PATHINFO_BASENAME); if(in_array($baseName, $templateFilenames) && $templateBasename != $baseName) continue; - if(strpos($file, '-'.\TracyDebugger::getDataValue('userDevTemplateSuffix').'.'.$fileExt)) $userDevTemplateFiles++; + if(strpos($file, '-'.TracyDebugger::getDataValue('userDevTemplateSuffix').'.'.$fileExt)) $userDevTemplateFiles++; if(strpos($fileName, $this->wire('page')->template->name) !== false && strpos($fileName, '-tracytemp') === false) { // '-tracytemp' is extension from template editor panel $this->templateOptions .= ''; } } } - $this->userDevTemplatePossible = \TracyDebugger::getDataValue('userDevTemplate') + $this->userDevTemplatePossible = TracyDebugger::getDataValue('userDevTemplate') && ($this->wire('permissions')->get("tracy-all-dev")->id || $this->wire('permissions')->get("tracy-".$this->wire('page')->template->name."-dev")->id) && $userDevTemplateFiles > 0 - && $this->wire('users')->count("roles.permissions=tracy-all-".\TracyDebugger::getDataValue('userDevTemplateSuffix')."|tracy-".$this->wire('page')->template->name."-".\TracyDebugger::getDataValue('userDevTemplateSuffix')); + && $this->wire('users')->count("roles.permissions=tracy-all-".TracyDebugger::getDataValue('userDevTemplateSuffix')."|tracy-".$this->wire('page')->template->name."-".TracyDebugger::getDataValue('userDevTemplateSuffix')); - if(\TracyDebugger::$templatePath) { - $iconColor = \TracyDebugger::COLOR_ALERT; + if(TracyDebugger::$templatePath) { + $iconColor = TracyDebugger::COLOR_ALERT; } elseif($this->wire('input')->cookie->tracyTemplatePathSticky != '' || $this->userDevTemplatePossible) { - $iconColor = \TracyDebugger::COLOR_WARN; + $iconColor = TracyDebugger::COLOR_WARN; } else { - $iconColor = \TracyDebugger::COLOR_NORMAL; + $iconColor = TracyDebugger::COLOR_NORMAL; } $this->icon = ' @@ -51,38 +53,34 @@ public function getTab() { '; - return ' - - ' . $this->icon . (\TracyDebugger::getDataValue('showPanelLabels') ? ' Template Path' : '') . ' - - '; + return $this->buildTab('Template Path'); } public function getPanel() { $templateIcon = ''; - if(\TracyDebugger::$templatePath && !\TracyDebugger::$templatePathSticky) { - if(\TracyDebugger::$templatePathOnce) { + if(TracyDebugger::$templatePath && !TracyDebugger::$templatePathSticky) { + if(TracyDebugger::$templatePathOnce) { $templateIcon = ' - + '; } - elseif(\TracyDebugger::$templatePathPermission) { + elseif(TracyDebugger::$templatePathPermission) { $templateIcon = ' - + '; } } $out = ' - '; - $out .= '

    '.$this->icon.' Template Path

    -
    + $out .= $this->buildPanelHeader('Template Path'); + $out .= $this->openPanel() . '
    Temporarily use a template file with a suffix.
    eg. '.$this->wire('page')->template->name.'-dev.php.

    Select an alternate from the list.
    Create the file in your templates directory first.

    '; $out .= ' ' . $templateIcon . '

    -   -   -   - +   +   +   +
    '; if($this->userDevTemplatePossible) { @@ -155,18 +163,14 @@ function getCookie(name) { **User Dev Template Reminder**
    • the "User Dev Template" option is enabled in module settings -
    • the "tracy-all-'.\TracyDebugger::getDataValue('userDevTemplateSuffix').'" or "tracy-'.$this->wire('page')->template->name.'-'.\TracyDebugger::getDataValue('userDevTemplateSuffix').'" permission exists
    • +
    • the "tracy-all-'.TracyDebugger::getDataValue('userDevTemplateSuffix').'" or "tracy-'.$this->wire('page')->template->name.'-'.TracyDebugger::getDataValue('userDevTemplateSuffix').'" permission exists
    • the permission has been assigned to at least one user
    • -
    • there are template files with a "-'.\TracyDebugger::getDataValue('userDevTemplateSuffix').'" suffix
    • +
    • there are template files with a "-'.TracyDebugger::getDataValue('userDevTemplateSuffix').'" suffix
    '; } - $out .= \TracyDebugger::generatePanelFooter('templatePath', \Tracy\Debugger::timer('templatePath'), strlen($out)); - - $out .= '
    '; - - return parent::loadResources() . $out; + return $this->closePanel($out, 'templatePath'); } } diff --git a/panels/TemplateResourcesPanel.php b/panels/TemplateResourcesPanel.php index d58c20aa..c5c64df7 100644 --- a/panels/TemplateResourcesPanel.php +++ b/panels/TemplateResourcesPanel.php @@ -1,10 +1,10 @@ -sectionEnd = ' -
    -
    ' . $columnName . '
    '.$file.''.\TracyDebugger::human_filesize(filesize($p->filesManager()->path . $file)).''.TracyDebugger::human_filesize(filesize($p->filesManager()->path . $file)).' '.date('Y-m-d H:i:s', filemtime($p->filesManager()->path . $file)).' '.$fileField.''.(in_array($p->filesManager()->path.$file, $this->tempFiles) ? ' ✔' : '').'
    '.pathinfo($missingFile['filename'], PATHINFO_BASENAME).''.pathinfo($missingFile['filename'], PATHINFO_BASENAME).' '.$missingFile['field'].''; @@ -83,9 +81,9 @@ private function renderPanelCell($panel, $defaultPanels, $showPanels, $onceIcon) $out .= $this->isOnce($name, $defaultPanels) ? $onceIcon : ''; // settings link or spaces - if (!empty(\TracyDebugger::$externalPanels) || !array_key_exists($name, \TracyDebugger::$externalPanels)) { + if (!empty(TracyDebugger::$externalPanels) || !array_key_exists($name, TracyDebugger::$externalPanels)) { if (array_key_exists($name, $this->panelSettingsLinks)) { - $out .= \TracyDebugger::generatePanelSettingsLink($this->panelSettingsLinks[$name]); + $out .= TracyDebugger::generatePanelSettingsLink($this->panelSettingsLinks[$name]); } else { $out .= str_repeat(' ', 6); @@ -100,7 +98,7 @@ private function renderPanelCell($panel, $defaultPanels, $showPanels, $onceIcon) // checkbox input $out .= ''; // label text + asterisk if default @@ -110,7 +108,7 @@ private function renderPanelCell($panel, $defaultPanels, $showPanels, $onceIcon) // Right side: timing and size info (only if exists) if ($seconds) { - $out .= '' . \TracyDebugger::formatTime($seconds); + $out .= '' . TracyDebugger::formatTime($seconds); if ($size) $out .= ', ' . $size; $out .= ''; } @@ -124,7 +122,7 @@ private function renderPanelCell($panel, $defaultPanels, $showPanels, $onceIcon) public function getPanel() { $out = ' - '; $onceIcon = ' - + '; - $out .= '

    '.$this->icon.' Panel Selector

    -
    + $out .= $this->buildPanelHeader('Panel Selector'); + $out .= $this->openPanel() . '
    *Panels enabled via Tracy\'s settings'; - $defaultPanels = $this->wire('page')->template == "admin" ? \TracyDebugger::getDataValue('backendPanels') : \TracyDebugger::getDataValue('frontendPanels'); - $showPanels = \TracyDebugger::$showPanels; + $defaultPanels = $this->wire('page')->template == "admin" ? TracyDebugger::getDataValue('backendPanels') : TracyDebugger::getDataValue('frontendPanels'); + $showPanels = TracyDebugger::$showPanels; - $out .= '

    '; + $out .= '

    '; $out .= ''; // split panels into two roughly equal halves $panels = []; - foreach(\TracyDebugger::$allPanels as $name => $label) { + foreach(TracyDebugger::$allPanels as $name => $label) { - if(in_array($name, \TracyDebugger::$restrictedUserDisabledPanels)) continue; - if(in_array($name, \TracyDebugger::$superUserOnlyPanels) && !\TracyDebugger::$allowedSuperuser && !\TracyDebugger::$validLocalUser && !\TracyDebugger::$validSwitchedUser) continue; - if($name == 'adminer' && !\TracyDebugger::$allowedSuperuser) continue; - if($this->wire('page')->template == 'admin' && in_array($name, \TracyDebugger::$hideInAdmin)) continue; + if(in_array($name, TracyDebugger::$restrictedUserDisabledPanels)) continue; + if(in_array($name, TracyDebugger::$superUserOnlyPanels) && !TracyDebugger::$allowedSuperuser && !TracyDebugger::$validLocalUser && !TracyDebugger::$validSwitchedUser) continue; + if($name == 'adminer' && !TracyDebugger::$allowedSuperuser) continue; + if($this->wire('page')->template == 'admin' && in_array($name, TracyDebugger::$hideInAdmin)) continue; if($name == 'userSwitcher') { - if(\TracyDebugger::getDataValue('userSwitchSession') != '') $userSwitchSession = \TracyDebugger::getDataValue('userSwitchSession'); - if(!\TracyDebugger::$allowedSuperuser && (!$this->wire('session')->tracyUserSwitcherId || (isset($userSwitchSession[$this->wire('session')->tracyUserSwitcherId]) && $userSwitchSession[$this->wire('session')->tracyUserSwitcherId] <= time()))) continue; + if(TracyDebugger::getDataValue('userSwitchSession') != '') $userSwitchSession = TracyDebugger::getDataValue('userSwitchSession'); + if(!TracyDebugger::$allowedSuperuser && (!$this->wire('session')->tracyUserSwitcherId || (isset($userSwitchSession[$this->wire('session')->tracyUserSwitcherId]) && $userSwitchSession[$this->wire('session')->tracyUserSwitcherId] <= time()))) continue; } $panels[] = ['name' => $name, 'label' => $label]; } @@ -251,29 +265,24 @@ function getCookie(name) { $out .= '
    -   -   - +   +   + '; - $out .= '  '; - if(!\TracyDebugger::getDataValue('strictMode')) { - $out .= '  '; + $out .= '  '; + if(!TracyDebugger::getDataValue('strictMode')) { + $out .= '  '; } - if(\TracyDebugger::getDataValue('panelSelectorTracyTogglerButton')) { - $out .= ''; + if(TracyDebugger::getDataValue('panelSelectorTracyTogglerButton')) { + $out .= ''; } $out .= '

    '; - $out .= \TracyDebugger::generatePanelFooter('panelSelector', \Tracy\Debugger::timer('panelSelector'), strlen($out), 'panelSelectorPanel'); - - $out .= ' - '; - - return parent::loadResources() . $out; + return $this->closePanel($out, 'panelSelector', 'panelSelectorPanel'); } } diff --git a/panels/PerformancePanel.php b/panels/PerformancePanel.php index 5ee73d71..399cc08e 100644 --- a/panels/PerformancePanel.php +++ b/panels/PerformancePanel.php @@ -1,10 +1,9 @@ -addPanel(new Zarganwar\PerformancePanel\Panel); -\TracyDebugger::$panelGenerationTime['performance']['time'] = \Tracy\Debugger::timer('performance'); +Debugger::getBar()->addPanel(new \Zarganwar\PerformancePanel\Panel); +TracyDebugger::$panelGenerationTime['performance']['time'] = Debugger::timer('performance'); diff --git a/panels/PerformancePanel/Panel.php b/panels/PerformancePanel/Panel.php index fa44ff6f..6ea276ea 100644 --- a/panels/PerformancePanel/Panel.php +++ b/panels/PerformancePanel/Panel.php @@ -2,6 +2,7 @@ namespace Zarganwar\PerformancePanel; +use ProcessWire\TracyDebugger; use Tracy\IBarPanel; /** @@ -69,9 +70,9 @@ public function getTab() . ' - - ' diff --git a/panels/PhpInfoPanel.php b/panels/PhpInfoPanel.php index 50640065..3b1cf84b 100644 --- a/panels/PhpInfoPanel.php +++ b/panels/PhpInfoPanel.php @@ -1,21 +1,23 @@ - - - - + + + - ' . (\TracyDebugger::getDataValue('showPanelLabels') ? ' PHP Info' : '') . ' + ' . (TracyDebugger::getDataValue('showPanelLabels') ? ' PHP Info' : '') . ' '; } @@ -35,7 +37,7 @@ public function getPanel() { phpinfo(); $phpInfo = ob_get_clean(); - $dom = new DOMDocument(); + $dom = new \DOMDocument(); libxml_use_internal_errors(true); $dom->loadHTML($phpInfo); libxml_use_internal_errors(false); @@ -59,8 +61,9 @@ public function getPanel() { $phpInfo .= ''; $tracyModuleUrl = $this->wire('config')->urls->TracyDebugger; + $nonceAttr = TracyDebugger::getNonceAttr(); $out = <<< HTML - HTML; } - // end for each section - $sectionEnd = ' - -
    -
    '; - $userLang = $this->wire('user')->language; /** @@ -180,19 +163,19 @@ function searchPw(form) { ksort($value); if($key == 'paths') $value = array_map(array($this, 'addRoot'), $value); } - $options = array(Dumper::DEPTH => \TracyDebugger::getDataValue('maxDepth'), Dumper::TRUNCATE => \TracyDebugger::getDataValue('maxLength'), Dumper::ITEMS => \TracyDebugger::getDataValue('maxItems'), Dumper::LIVE => true, Dumper::COLLAPSE => true); - if(defined('\Tracy\Dumper::ITEMS')) array_push($options, array(Dumper::ITEMS => \TracyDebugger::getDataValue('maxItems'))); - $value = \Tracy\Dumper::toHtml($value, $options); + $options = array(Dumper::DEPTH => TracyDebugger::getDataValue('maxDepth'), Dumper::TRUNCATE => TracyDebugger::getDataValue('maxLength'), Dumper::ITEMS => TracyDebugger::getDataValue('maxItems'), Dumper::LIVE => true, Dumper::COLLAPSE => true); + if(defined('\Tracy\Dumper::ITEMS')) array_push($options, array(Dumper::ITEMS => TracyDebugger::getDataValue('maxItems'))); + $value = Dumper::toHtml($value, $options); $configData .= "
    ".$this->wire('sanitizer')->entities($key)."" . $value . "
    -
    '; // Included Files $functions = array(); $includedFilesOut = '

    Included Files

    '; - if(count(\TracyDebugger::$includedFiles) > 0) { + if(count(TracyDebugger::$includedFiles) > 0) { $includedFilesOut .= $this->sectionHeader(array('Path')); - foreach(\TracyDebugger::$includedFiles as $key => $path) { + foreach(TracyDebugger::$includedFiles as $key => $path) { $functions[] = $this->get_defined_resources_in_file($path); - $path = \TracyDebugger::removeCompilerFromPath($path); - $path = \TracyDebugger::forwardSlashPath($path); + $path = TracyDebugger::removeCompilerFromPath($path); + $path = TracyDebugger::forwardSlashPath($path); $includedFilesOut .= "\n" . - ''.\TracyDebugger::createEditorLink($path, 1, str_replace($this->wire('config')->paths->root, '/', $path), 'Edit File').'' . + ''.TracyDebugger::createEditorLink($path, 1, $this->stripRootPath($path), 'Edit File').'' . ""; } - $includedFilesOut .= $this->sectionEnd; + $includedFilesOut .= $this->sectionEnd(); } else { $includedFilesOut .= 'There are no included files.'; } // add variables and constants to all resources - if(count(\TracyDebugger::$templateVars) > 0) foreach(\TracyDebugger::$templateVars as $key => $value) $this->allResources[] = '$'.$key; - if(count(\TracyDebugger::$templateConsts) > 0) foreach(\TracyDebugger::$templateConsts as $key => $value) $this->allResources[] = $key; + if(count(TracyDebugger::$templateVars) > 0) foreach(TracyDebugger::$templateVars as $key => $value) $this->allResources[] = '$'.$key; + if(count(TracyDebugger::$templateConsts) > 0) foreach(TracyDebugger::$templateConsts as $key => $value) $this->allResources[] = $key; $this->resourceCounts = $this->countInTemplateFiles(array_unique($this->allResources)); // Variables $this->resourceOutput .= '

    Variables

    '; - if(count(\TracyDebugger::$templateVars) > 0) { - $this->resourceOutput .= $this->formatVariables(\TracyDebugger::$templateVars, 'var'); + if(count(TracyDebugger::$templateVars) > 0) { + $this->resourceOutput .= $this->formatVariables(TracyDebugger::$templateVars, 'var'); } else { $this->resourceOutput .= 'There are no defined template file variables.'; @@ -59,8 +55,8 @@ public function getTab() { // Constants $this->resourceOutput .= '

    Constants

    '; - if(count(\TracyDebugger::$templateConsts) > 0) { - $this->resourceOutput .= $this->formatVariables(\TracyDebugger::$templateConsts, 'const'); + if(count(TracyDebugger::$templateConsts) > 0) { + $this->resourceOutput .= $this->formatVariables(TracyDebugger::$templateConsts, 'const'); } else { $this->resourceOutput .= 'There are no defined template constants.'; @@ -69,16 +65,16 @@ public function getTab() { // Functions $this->resourceOutput .= '

    Functions

    '; - if(count(\TracyDebugger::$templateFuncs) > 0) { + if(count(TracyDebugger::$templateFuncs) > 0) { $this->resourceOutput .= $this->sectionHeader(array('Name', 'File', 'Line')); $funcNames = array(); foreach($functions as $func) { foreach($func as $name => $details) { $funcNames[] = strtolower($name); - if(in_array(strtolower($name), array_map('strtolower', str_replace('processwire\\', '', array_values(\TracyDebugger::$templateFuncs))))) { + if(in_array(strtolower($name), array_map('strtolower', str_replace('processwire\\', '', array_values(TracyDebugger::$templateFuncs))))) { if(isset($details['file'])) { - $path = \TracyDebugger::removeCompilerFromPath($details['file']); - $path = \TracyDebugger::forwardSlashPath($path); + $path = TracyDebugger::removeCompilerFromPath($details['file']); + $path = TracyDebugger::forwardSlashPath($path); if(isset($this->resourceCounts[$name]) && $this->resourceCounts[$name] === 1) { $warn = true; $this->warn++; @@ -87,8 +83,8 @@ public function getTab() { $warn = false; } $this->resourceOutput .= "\n" . - ''.\TracyDebugger::createEditorLink($path, $details['line'], $name).'' . - ''.str_replace($this->wire('config')->paths->root, '/', $path).'' . + ''.TracyDebugger::createEditorLink($path, $details['line'], $name).'' . + ''.$this->stripRootPath($path).'' . ''.$details['line'].'' . ""; } @@ -96,7 +92,7 @@ public function getTab() { } } - foreach(\TracyDebugger::$templateFuncs as $key => $name) { + foreach(TracyDebugger::$templateFuncs as $key => $name) { $name = str_replace('processwire\\', '', $name); if(!in_array($name, $funcNames)) { $this->resourceOutput .= "\n" . @@ -104,7 +100,7 @@ public function getTab() { ""; } } - $this->resourceOutput .= $this->sectionEnd; + $this->resourceOutput .= $this->sectionEnd(); } else { $this->resourceOutput .= 'There are no defined template file functions.'; @@ -116,45 +112,26 @@ public function getTab() { $this->resourceOutput .= '

    Other Searched Files

    When checking for more than one occurrence of a resource

    '; $this->resourceOutput .= $this->sectionHeader(array('Path')); foreach($this->searchedFiles as $path) { - if(!in_array($path, array_map(array('\TracyDebugger', 'removeCompilerFromPath'), \TracyDebugger::$includedFiles))) { - $path = \TracyDebugger::forwardSlashPath($path); + if(!in_array($path, array_map(array(__NAMESPACE__ . '\TracyDebugger', 'removeCompilerFromPath'), TracyDebugger::$includedFiles))) { + $path = TracyDebugger::forwardSlashPath($path); $this->resourceOutput .= "\n" . - ''.\TracyDebugger::createEditorLink($path, 1, str_replace($this->wire('config')->paths->root, '/', $path), 'Edit File').'' . + ''.TracyDebugger::createEditorLink($path, 1, $this->stripRootPath($path), 'Edit File').'' . ""; } } - $this->resourceOutput .= $this->sectionEnd; + $this->resourceOutput .= $this->sectionEnd(); return ' - - ' . (\TracyDebugger::getDataValue('showPanelLabels') ? 'Template Resources' : '') . ' + + ' . (TracyDebugger::getDataValue('showPanelLabels') ? 'Template Resources' : '') . ' '; } - protected function sectionHeader($columnNames = array()) { - $out = ' -
    - - - '; - foreach($columnNames as $columnName) { - $out .= ''; - } - - $out .= ' - - - - '; - return $out; - } - - protected function formatVariables($vars, $type) { $headings = array('Name', 'Returns', 'Class', 'Value'); if($type == 'var') $headings[] = 'Files/Lines'; @@ -172,10 +149,10 @@ protected function formatVariables($vars, $type) { $value = $varArray; } - if(!\TracyDebugger::getDataValue('variablesShowPwObjects') && is_object($value)) { + if(!TracyDebugger::getDataValue('variablesShowPwObjects') && is_object($value)) { $outValue = method_exists($value,'getArray') ? $value->getArray() : $value; // run getValue() on as many levels as the Max Nesting Depth config setting - for($i=0;$i<=\TracyDebugger::getDataValue('maxDepth');$i++) { + for($i=0;$i<=TracyDebugger::getDataValue('maxDepth');$i++) { if(is_array($outValue)) { array_walk_recursive($outValue, function (&$val) { $val = is_object($val) && method_exists($val,'getArray') ? $val->getArray() : $val; @@ -188,8 +165,8 @@ protected function formatVariables($vars, $type) { } // if it's an object or multidimensional array, then collapse it - $options = array(Dumper::LIVE => true, Dumper::DEPTH => \TracyDebugger::getDataValue('maxDepth'), Dumper::TRUNCATE => \TracyDebugger::getDataValue('maxLength'), Dumper::COLLAPSE => ((is_array($outValue) && count($outValue) !== count($outValue, COUNT_RECURSIVE)) || is_object($outValue) ? true : false)); - if(defined('\Tracy\Dumper::ITEMS')) array_push($options, array(Dumper::ITEMS => \TracyDebugger::getDataValue('maxItems'))); + $options = array(Dumper::LIVE => true, Dumper::DEPTH => TracyDebugger::getDataValue('maxDepth'), Dumper::TRUNCATE => TracyDebugger::getDataValue('maxLength'), Dumper::COLLAPSE => ((is_array($outValue) && count($outValue) !== count($outValue, COUNT_RECURSIVE)) || is_object($outValue) ? true : false)); + if(defined('\Tracy\Dumper::ITEMS')) array_push($options, array(Dumper::ITEMS => TracyDebugger::getDataValue('maxItems'))); $outValue = Dumper::toHtml($outValue, $options); if(isset($this->variables['$'.$var])) { $fileLines = array(); @@ -197,9 +174,9 @@ protected function formatVariables($vars, $type) { $varOut = "" . - ''.($type == 'var' ? '$' : '').$var.'' . + ''.($type == 'var' ? '$' : '').$var.'' . "" . "" . ""; if($type == 'var') $out .= isset($varOut) ? $varOut : ''; $out .= ""; } - $out .= $this->sectionEnd; + $out .= $this->sectionEnd(); return $out; } @@ -262,7 +239,7 @@ protected function get_defined_resources_in_file($file) { break; case T_VARIABLE: - if(in_array(str_replace('$', '', $token[1]), array_keys(\TracyDebugger::$templateVars))) { + if(in_array(str_replace('$', '', $token[1]), array_keys(TracyDebugger::$templateVars))) { if($lastTokenName == $token[1]) $i++; if($lastTokenName != $token[1] || $lastTokenName == $token[1] && $lastTokenLine != $token[2]) { $this->variables[$token[1]][$i]['file'] = $file; @@ -297,8 +274,8 @@ protected function get_defined_resources_in_file($file) { protected function countInTemplateFiles($resources) { $resourceCounts = array(); - $dir = new RecursiveDirectoryIterator($this->wire('config')->paths->templates); - foreach(new RecursiveIteratorIterator($dir) as $file) { + $dir = new \RecursiveDirectoryIterator($this->wire('config')->paths->templates); + foreach(new \RecursiveIteratorIterator($dir) as $file) { if($file->isFile() && ($file->getExtension() == 'php' || $file->getExtension() == 'inc') && $file->getFilename() != 'admin.php') { $this->searchedFiles[] = $file->getPathname(); $content = file_get_contents($file->getPathname()); @@ -318,23 +295,18 @@ public function getPanel() { $out = '

    - + Template Resources -

    + -
    + ' . $this->openPanel() . '

    These are all the non-PW resources that are available in the template for this page. If you are looking for the fields and their values for this page, look in the ProcessWire Info panel under "Fields List & Values".
    An orange warning background indicates that the variable/constant/function only occurs once in the files included for this page as well as all other files in the /site/templates directory.

    '; $out .= $this->resourceOutput; - $out .= \TracyDebugger::generatePanelFooter('templateResources', \Tracy\Debugger::timer('templateResources'), strlen($out), 'templateResourcesPanel'); - - $out .= ' -
    '; - - return parent::loadResources() . $out; + return $this->closePanel($out, 'templateResources', 'templateResourcesPanel'); } } diff --git a/panels/TerminalPanel.php b/panels/TerminalPanel.php index 04251a1a..7f903941 100644 --- a/panels/TerminalPanel.php +++ b/panels/TerminalPanel.php @@ -1,4 +1,6 @@ -icon = ' - - '; - return ' - ' . - $this->icon . (\TracyDebugger::getDataValue('showPanelLabels') ? ' Terminal' : '') . ' - '; + return $this->buildTab('Terminal'); } @@ -33,25 +32,22 @@ public function getPanel() { $terminalModuleId = $this->wire('modules')->getModuleID("ProcessTerminal"); $terminalUrl = $this->wire('pages')->get("process=$terminalModuleId")->url; - $out = ' -

    ' . $this->icon . ' Terminal

    '; + $out = $this->buildPanelHeader('Terminal', true); if($this->wire('modules')->isInstalled("ProcessTerminal")) { - $out .= ' -
    + $out .= $this->openPanel('', 'padding: 0 !important') . ' '; } else { - $out .= ' -
    + $out .= $this->openPanel() . '

    This panel is not available because the ProcessTerminal module has not been installed.

    '; } - $out .= '
    '.\TracyDebugger::generatePanelFooter('terminal', \Tracy\Debugger::timer('terminal'), strlen($out)).'
    '; + $out .= '
    '.TracyDebugger::generatePanelFooter('terminal', Debugger::timer('terminal'), strlen($out)).'
    '; $out .= '
    '; - return parent::loadResources() . $out; + return $out; } } diff --git a/panels/TodoPanel.php b/panels/TodoPanel.php index d1fe4cd4..2b5ee0ac 100644 --- a/panels/TodoPanel.php +++ b/panels/TodoPanel.php @@ -1,4 +1,6 @@ -entries .= " \n
    "; if($currentFile !== $item['file']) { - $this->entries .= "" . str_replace($this->wire('config')->paths->root, '', $item['file']) . ""; + $this->entries .= "" . $this->stripRootPath($item['file'], '') . ""; } // if "todo" or other matched tag is at start of comment then remove it // otherwise, underline it wherever it is in the comment @@ -44,25 +46,23 @@ public function getTab() { $this->entries .= "" . "" . - ""; } $currentFile = $item['file']; $i++; } } - $this->entries .= ' -
    '.$columnName.'
    "; $i=1; foreach($this->variables['$'.$var] as $item) { - $path = \TracyDebugger::removeCompilerFromPath($item['file']); - $path = \TracyDebugger::forwardSlashPath($path); - $fileLines[$var][str_replace($this->wire('config')->paths->root, '/', $path)]['lines'][] = \TracyDebugger::createEditorLink($path, $item['line'], $item['line']); + $path = TracyDebugger::removeCompilerFromPath($item['file']); + $path = TracyDebugger::forwardSlashPath($path); + $fileLines[$var][$this->stripRootPath($path)]['lines'][] = TracyDebugger::createEditorLink($path, $item['line'], $item['line']); $i++; } $currentFile = null; @@ -220,14 +197,14 @@ protected function formatVariables($vars, $type) { $warn = false; } $out .= "\n
    ".gettype($value)."".(gettype($value) == "object" ? get_class($value) : "")."".$outValue."
    ".$item['line']."".strtoupper($item['type'])."".\TracyDebugger::createEditorLink($item['file'], $item['line'], preg_replace('/(?" . + "".TracyDebugger::createEditorLink($item['file'], $item['line'], preg_replace('/(?" . "
    -
    '; + $this->entries .= $this->sectionEnd(); if($thisPageNumEntries > 0) { - $this->iconColor = \TracyDebugger::COLOR_ALERT; + $this->iconColor = TracyDebugger::COLOR_ALERT; } elseif($numEntries > 0) { - $this->iconColor = \TracyDebugger::COLOR_WARN; + $this->iconColor = TracyDebugger::COLOR_WARN; } else { - $this->iconColor = \TracyDebugger::COLOR_NORMAL; + $this->iconColor = TracyDebugger::COLOR_NORMAL; } $this->icon = ' @@ -73,28 +73,17 @@ public function getTab() { '; - return ' - - ' . $this->icon . (\TracyDebugger::getDataValue('showPanelLabels') ? 'Todo' : '') . ' ' . $thisPageNumEntries . '/' . $numEntries . ' - - '; + return $this->buildTab('ToDo', 'Todo', ' ' . $thisPageNumEntries . '/' . $numEntries); } public function getPanel() { - $out = ' -

    ' . $this->icon . ' ToDo

    - -
    '; + $out = $this->buildPanelHeader('ToDo'); + $out .= $this->openPanel(); $out .= $this->entries; - $out .= \TracyDebugger::generatePanelFooter('todo', \Tracy\Debugger::timer('todo'), strlen($out), 'todoPanel'); - - $out .= ' -
    '; - - return parent::loadResources() . $out; + return $this->closePanel($out, 'todo', 'todoPanel'); } @@ -127,48 +116,35 @@ protected function containsTodoType($str) { } - protected function sectionHeader($columnNames = array()) { - $out = ' -
    - - - '; - foreach($columnNames as $columnName) { - $out .= ''; - } - - $out .= ' - - - - '; - return $out; - } - - private function getTodos() { - $items = $this->scanDirectories($this->wire('config')->paths->templates); - if(\TracyDebugger::getDataValue('todoScanModules') == 1) $moduleItems = $this->scanDirectories($this->wire('config')->paths->siteModules); + $todoLinesData = $this->wire('cache')->get('TracyToDoData'); + $changed = false; + + $items = $this->scanDirectories($this->wire('config')->paths->templates, $todoLinesData, $changed); + if(TracyDebugger::getDataValue('todoScanModules') == 1) $moduleItems = $this->scanDirectories($this->wire('config')->paths->siteModules, $todoLinesData, $changed); if(isset($moduleItems)) $items = array_merge($items, $moduleItems); - if(\TracyDebugger::getDataValue('todoScanAssets') == 1) $assetsItems = $this->scanDirectories($this->wire('config')->paths->assets); + if(TracyDebugger::getDataValue('todoScanAssets') == 1) $assetsItems = $this->scanDirectories($this->wire('config')->paths->assets, $todoLinesData, $changed); if(isset($assetsItems)) $items = array_merge($items, $assetsItems); - if(\TracyDebugger::getDataValue('todoSpecifiedDirectories') != '') { - foreach(array_map('trim', explode("\n", \TracyDebugger::getDataValue('todoSpecifiedDirectories'))) as $customSpecifiedDir) { - $customSpecifiedItems = $this->scanDirectories($this->wire('config')->paths->root . $customSpecifiedDir); + if(TracyDebugger::getDataValue('todoSpecifiedDirectories') != '') { + foreach(array_map('trim', explode("\n", TracyDebugger::getDataValue('todoSpecifiedDirectories'))) as $customSpecifiedDir) { + $customSpecifiedItems = $this->scanDirectories($this->wire('config')->paths->root . $customSpecifiedDir, $todoLinesData, $changed); $items = array_merge($items, $customSpecifiedItems); } } + if($changed) { + $this->wire('cache')->save('TracyToDoData', $todoLinesData, WireCache::expireNever); + } + return $items; } - private function scanDirectories($dir) { - $todoLinesData = $this->wire('cache')->get('TracyToDoData'); + private function scanDirectories($dir, &$todoLinesData, &$changed) { $items = array(); - $ignoreDirs = array_map('trim', explode(',', \TracyDebugger::getDataValue('todoIgnoreDirs'))); + $ignoreDirs = array_map('trim', explode(',', TracyDebugger::getDataValue('todoIgnoreDirs'))); array_push($ignoreDirs, 'TracyDebugger'); - $allowedExtensions = array_map('trim', explode(',', \TracyDebugger::getDataValue('todoAllowedExtensions'))); + $allowedExtensions = array_map('trim', explode(',', TracyDebugger::getDataValue('todoAllowedExtensions'))); foreach($iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::SELF_FIRST) as $fileinfo) { $filePath = $fileinfo->getPathname(); $fileSize = filesize($filePath); @@ -176,7 +152,7 @@ private function scanDirectories($dir) { if(!$todoLinesData || !isset($todoLinesData[$filePath]) || filemtime($filePath) > $todoLinesData[$filePath]['time']) { $todoLinesData[$filePath]['time'] = time(); $todoLinesData[$filePath]['items'] = $this->parseFile($filePath, $fileSize); - $this->wire('cache')->save('TracyToDoData', $todoLinesData, WireCache::expireNever); + $changed = true; } $items[] = $todoLinesData[$filePath]['items']; } diff --git a/panels/TracyExceptionsPanel.php b/panels/TracyExceptionsPanel.php index 564c908b..3e8681e9 100644 --- a/panels/TracyExceptionsPanel.php +++ b/panels/TracyExceptionsPanel.php @@ -1,69 +1,72 @@ -needsUtf8 === null) { + $this->needsUtf8 = PHP_VERSION_ID < 70100 + && extension_loaded('mbstring') + && function_exists('iconv'); + } + return $this->needsUtf8; + } public function getTab() { - \Tracy\Debugger::timer('tracyExceptions'); + Debugger::timer('tracyExceptions'); $this->tracyExceptionFile = $this->wire('input')->cookie->tracyExceptionFile; - $this->filesArray = $this->getFilesArray($this->wire('config')->paths->logs . 'tracy/', array('html')); - $tracyExeceptionsData = $this->wire('cache')->get('TracyExceptionsData'); + $this->filesArray = $this->getFilesArray($this->wire('config')->paths->logs . 'tracy/'); + $tracyExceptionsData = $this->wire('cache')->get('TracyExceptionsData'); - $this->newFiles = array(); - if($tracyExeceptionsData) { - foreach ($this->filesArray as $file) { - if(!in_array($file, $tracyExeceptionsData)) { - $this->newFiles[] = $file; - } - } + if($tracyExceptionsData) { + $this->newFiles = array_diff($this->filesArray, $tracyExceptionsData); } - if(count($this->newFiles) > 0) { - $this->iconColor = \TracyDebugger::COLOR_ALERT; - } - else { - $this->iconColor = \TracyDebugger::COLOR_NORMAL; - } + $this->wire('cache')->save('TracyExceptionsData', $this->filesArray, WireCache::expireNever); + + $this->newFilesMap = array_flip($this->newFiles); + $this->iconColor = count($this->newFiles) > 0 ? TracyDebugger::COLOR_ALERT : TracyDebugger::COLOR_NORMAL; $this->icon = ' '; - return ' - - ' . $this->icon . (\TracyDebugger::getDataValue('showPanelLabels') ? ' Tracy Exceptions' : '') . ' - - '; + return $this->buildTab('Tracy Exceptions'); } public function getPanel() { $tracyModuleUrl = $this->wire('config')->urls->TracyDebugger; - $currentUrl = $_SERVER['REQUEST_URI']; + $currentUrl = htmlspecialchars($_SERVER['REQUEST_URI'] ?? '', ENT_QUOTES, 'UTF-8'); - $filePath = $this->wire('config')->paths->root . $this->tracyExceptionFile; - - $maximizeSvg = ''; + // generate CSRF token for AJAX calls (shared with File Editor) + if(!$this->wire('session')->tracyFileEditorToken) { + $this->wire('session')->tracyFileEditorToken = bin2hex(random_bytes(32)); + } + $csrfToken = $this->wire('session')->tracyFileEditorToken; + $nonceAttr = TracyDebugger::getNonceAttr(); $out = <<< HTML - HTML; - $out .= '

    '.$this->icon.' Tracy Exceptions

    -
    -
    - -
    -
    '; + $out .= '

    '.$this->icon.' Tracy Exceptions

    + ' . $this->openPanel('', 'display: flex; flex-direction: column; min-height: 0;') . ' +
    +
    +
    '; $out .= "
    "; - $out .= $this->php_file_tree($this->wire('config')->paths->logs . 'tracy/', array('html')); + $out .= $this->php_file_tree($this->wire('config')->paths->logs . 'tracy/'); $out .= "
    "; $out .= ' - +
    -
    - +
    + +
    + +
    '; - $out .= \TracyDebugger::generatePanelFooter('tracyExceptions', \Tracy\Debugger::timer('tracyExceptions'), strlen($out), 'tracyExceptionsPanel'); - - $out .= ' -
    '; - - return parent::loadResources() . $out; + return $this->closePanel($out, 'tracyExceptions', 'tracyExceptionsPanel'); } /** - * Generates a valid HTML list of all directories, sub-directories and files + * Generates a valid HTML list of all files * * @param string $directory starting point, valid path, with or without trailing slash - * @param array $extensions array of strings with extension types (without dot), default: empty array, show all files - * @param bool $extFilter to include (false) or exclude (true) files with that extension, default: false * @return string html markup * */ - public function php_file_tree($directory, $extensions = array(), $extFilter = false) { + public function php_file_tree($directory) { - if(!function_exists("scandir")) { - $msg = $this->_('Error: scandir function does not exist.'); - $this->error($msg); - return; - } - - $directory = rtrim($directory, '/\\'); // strip both slash and backslash at the end + $directory = rtrim($directory, '/\\'); $tree = "
    "; - $tree .= $this->php_file_tree_dir($directory, $extensions, (bool) $extFilter); + $tree .= $this->php_file_tree_dir($directory); $tree .= "
    "; return $tree; } /** - * Recursive function to generate the list of directories/files. + * Generate the list of exception files. * * @param string $directory starting point, full valid path, without trailing slash - * @param array $extensions array of strings with extension types (without dot), default: empty array - * @param bool $extFilter to include (false) or exclude (true) files with that extension, default: false (include) - * @param string $parent relative directory path, for internal use only * @return string html markup * */ - private function php_file_tree_dir($directory, $extensions = array(), $extFilter = false, $parent = "") { - - $tree = ""; + private function php_file_tree_dir($directory) { - if(count($this->filesArray) > 0) { - // Sort directories/files - natcasesort($this->filesArray); - $this->filesArray = array_reverse($this->filesArray, false); - - // Make directories first, then files - $fls = $dirs = array(); - foreach($this->filesArray as $f) { - if(@is_dir("$directory/$f")) $dirs[] = $f; else $fls[] = $f; - } - $this->filesArray = array_merge($dirs, $fls); - $this->wire('cache')->save('TracyExceptionsData', $this->filesArray, WireCache::expireNever); + if(count($this->filesArray) === 0) { + return ""; + } - $tree .= "
      "; + $rootPath = $this->wire('config')->paths->root; + $parentDir = "/" . str_replace($rootPath, "", $directory . "/"); + $parentDir = str_replace("//", "/", $parentDir); + $needsUtf8 = $this->needsUtf8Convert(); - foreach($this->filesArray as $file) { - $fileName = $this->toUTF8($file, $this->encoding); + $parts = array('
        '); - $parentDir = "/" . str_replace($this->wire('config')->paths->root, "", $directory . "/"); // directory is without trailing slash - $dirPath = $this->toUTF8("$parentDir/$file/", $this->encoding); - $dirPath = str_replace("//", "/", $dirPath); + foreach($this->filesArray as $file) { + $fileName = $needsUtf8 ? $this->toUTF8($file) : $file; + $isNew = isset($this->newFilesMap[$fileName]); - $ext = strtolower(substr($file, strrpos($file, ".") + 1)); - $link = str_replace("%2F", "/", rawurlencode("$dirPath")); // to overcome bug/feature on apache - $link = trim($link, '/'); - $link = str_replace('//', '/', $link); - $link = str_replace($this->wire('config')->paths->root, '', $link); - $tree .= "
      • newFiles) ? ' style="color: #FFFFFF !important"' : '')." href='tracyexception://?f=$link&l=1'>$fileName
      • "; + $link = str_replace('%2F', '/', rawurlencode($parentDir . $file)); + $link = trim($link, '/'); + $link = str_replace($rootPath, '', $link); - } + $liStyle = 'padding: 3px 5px' . ($isNew ? '; background: ' . TracyDebugger::COLOR_ALERT : ''); + $aStyle = $isNew ? ' style="color: #FFFFFF !important"' : ''; - $tree .= "
      "; + $escapedFileName = htmlspecialchars($fileName ?? '', ENT_QUOTES, 'UTF-8'); + $parts[] = "
    • {$escapedFileName}
    • "; } - return $tree; - } - - private function getFilesArray($directory, $extensions = array(), $extFilter = false) { - $filesArray = array(); - // Get directories/files - $filesArray = array_diff(@scandir($directory, SCANDIR_SORT_DESCENDING), array('.', '..')); // array_diff removes . and .. - // Filter unwanted extensions - // currently empty extensions array returns all files in folders - // comment if statement if you want empty extensions array to return no files at all - if(!empty($extensions)) { - foreach(array_keys($filesArray) as $key) { - - // exclude dotfiles, but leave files in extensions filter - // substr($filesArray[$key], 1) removes first char - if($filesArray[$key][0] == '.' && !in_array(substr($filesArray[$key], 1), $extensions) ) unset($filesArray[$key]); + $parts[] = '
    '; + return implode("\n", $parts); + } - if(!@is_dir("$directory/$filesArray[$key]")) { - $ext = substr($filesArray[$key], strrpos($filesArray[$key], ".") + 1); - if($extFilter == in_array($ext, $extensions)) unset($filesArray[$key]); - } - } - } - return $filesArray; + /** + * Get the latest exception files sorted newest first. + * + * @param string $directory path to tracy log directory + * @return array filenames (basename only) + * + */ + private function getFilesArray($directory) { + $files = glob($directory . '*.html'); + if(!$files) return array(); + rsort($files); + return array_map('basename', array_slice($files, 0, TracyDebugger::getDataValue("numExceptions"))); } /** @@ -227,76 +202,22 @@ private function getFilesArray($directory, $extensions = array(), $extFilter = f */ private function toUTF8($str, $encoding = 'auto', $c = false) { - if(PHP_VERSION_ID >= 70100) return $str; - - // http://stackoverflow.com/questions/7979567/php-convert-any-string-to-utf-8-without-knowing-the-original-character-set-or - if(extension_loaded('mbstring') && function_exists('iconv')) { - if($encoding == 'auto') { - if(DIRECTORY_SEPARATOR != '/') { - // windows - $str = @iconv(mb_detect_encoding($str, mb_detect_order(), true), 'UTF-8', $str); - } else { - // linux - $str = @iconv('Windows-1250', 'UTF-8', $str); // wild guess!!! could be ISO-8859-2, UTF-8, ... - } + if(!$this->needsUtf8Convert()) return $str; + + if($encoding == 'auto') { + if(DIRECTORY_SEPARATOR != '/') { + $str = @iconv(mb_detect_encoding($str, mb_detect_order(), true), 'UTF-8', $str); } else { - if($encoding == 'urldecode') $str = @urldecode($str); - else if($encoding == 'none') $str = $str; - else if($encoding != 'UTF-8') $str = @iconv($encoding, 'UTF-8', $str); + $str = @iconv('Windows-1250', 'UTF-8', $str); } + } else { + if($encoding == 'urldecode') $str = @urldecode($str); + else if($encoding == 'none') $str = $str; + else if($encoding != 'UTF-8') $str = @iconv($encoding, 'UTF-8', $str); } - // replacement of % must be first!!! + if($c) $str = str_replace(array("%", "#", " ", "{", "}", "^", "+"), array("%25", "%23", "%20", "%7B", "%7D", "%5E", "%2B"), $str); return $str; } - /** - * Convert $config->paths->key to $config->urls->key - * - * @param string $path eg. $config->paths->templates - * @param array $pathTypes eg. array('site','templates'), if not specified, array is constructed from $config->paths - * @return string path converted to url, empty string if path not found - * - */ - private function convertPathToUrl($path, $pathTypes = array()) { - $path = rtrim($path, '/\\') . '/'; // strip both slash and backslash at the end and then re-add separator - $url = ''; - - if(!$pathTypes) { - $pathTypes = array('root'); // root is missing - foreach($this->wire('config')->paths as $pathType => $dummy) $pathTypes[] = $pathType; - } - - foreach($pathTypes as $pathType) { - if($this->wire('config')->paths->{$pathType} == $path) { - $url = $this->wire('config')->urls->{$pathType}; - break; - } - } - return $url; - } - - /** - * Convert string delimited by delimiter into an array. Removes empty array keys. - * - * @param string $extensions string with delimiters - * @param string $delimiter, default is comma - * @return array - * - */ - private function toArray($extensions, $delimiter = ',') { - $ext = preg_replace('# +#', '', $extensions); // remove all spaces - $ext = array_filter(explode($delimiter, $ext), 'strlen'); // convert to array splitting by delimiter - return $ext; - } - - - private function strposa($haystack, $needle, $offset=0) { - if(!is_array($needle)) $needle = array($needle); - foreach($needle as $query) { - if(strpos($haystack, $query, $offset) !== false) return true; // stop on first true result - } - return false; - } - } diff --git a/panels/TracyLogsPanel.php b/panels/TracyLogsPanel.php index 44686910..f9af1911 100644 --- a/panels/TracyLogsPanel.php +++ b/panels/TracyLogsPanel.php @@ -1,4 +1,6 @@ - -
    '.$columnName.'
    -
    '; + Debugger::timer('tracyLogs'); /** * Tracy log panel sections @@ -43,14 +39,14 @@ public function getTab() { $errorLogs = array('error', 'exception', 'critical'); foreach($logs as $log) { - if(in_array($log['name'], \TracyDebugger::getDataValue("excludedTracyLogFiles"))) { + if(in_array($log['name'], TracyDebugger::getDataValue("excludedTracyLogFiles"))) { continue; } $x=99; if(!isset($logLinesData[$log['name']]) || filemtime($this->getFilename($log['name'])) > $logLinesData[$log['name']]['time']) { - $lines = \TracyDebugger::tailCustom($this->wire('config')->paths->logs.'tracy/'.$log['name'].'.log', \TracyDebugger::getDataValue("numLogEntries")); + $lines = TracyDebugger::tailCustom($this->wire('config')->paths->logs.'tracy/'.$log['name'].'.log', TracyDebugger::getDataValue("numLogEntries")); $lines = mb_convert_encoding($lines, 'UTF-8'); $lines = explode("\n", $lines); @@ -60,7 +56,7 @@ public function getTab() { if(in_array($log['name'], $errorLogs)) { $isNewErrors++; } - $this->wire('cache')->save('TracyLogData.Tracy', $logLinesData, WireCache::expireNever); + $cacheChanged = true; } $logLines = $logLinesData[$log['name']]['lines']; @@ -74,7 +70,7 @@ public function getTab() { $itemKey = $log['name'] . '_' . $x; $entryUrlAndText = explode('@', substr($entry, 22)); // get the rest of the line after the date; if(isset($entryUrlAndText[1])) { - $entriesArr[$itemKey]['url'] = "".trim($entryUrlAndText[1]).""; + $entriesArr[$itemKey]['url'] = "".htmlspecialchars(trim($entryUrlAndText[1] ?? ''), ENT_QUOTES, 'UTF-8').""; } else { continue; //bit of a hack - some entries getting duplicated but with empty URL, so ignore @@ -91,6 +87,10 @@ public function getTab() { } } + if(isset($cacheChanged)) { + $this->wire('cache')->save('TracyLogData.Tracy', $logLinesData, WireCache::expireNever); + } + if(count($entriesArr)) { $timestamp = array(); $order = array(); @@ -103,40 +103,40 @@ public function getTab() { array_multisort($timestamp, SORT_DESC, $order, SORT_ASC, SORT_NATURAL, $entriesArr); //display most recent entries from all log files - foreach(array_slice($entriesArr, 0, \TracyDebugger::getDataValue("numLogEntries")) as $item) { + foreach(array_slice($entriesArr, 0, TracyDebugger::getDataValue("numLogEntries")) as $item) { if(in_array($item['log'], $errorLogs)) { $isError = true; - $color = \TracyDebugger::COLOR_ALERT; + $color = TracyDebugger::COLOR_ALERT; } else { $isError = false; - $color = \TracyDebugger::COLOR_WARN; + $color = TracyDebugger::COLOR_WARN; } - $trimmedText = trim(htmlspecialchars($item['text'], ENT_QUOTES, 'UTF-8')); + $trimmedText = trim(htmlspecialchars($item['text'] ?? '', ENT_QUOTES, 'UTF-8')); $lineIsNew = !isset($cachedLogLinesData[$item['log']]) || (isset($cachedLogLinesData[$item['log']]) && strtotime($item['date']) > $cachedLogLinesData[$item['log']]['time']); $this->logEntries .= " \n" . "".$item['log']."" . "".str_replace('-','‑',str_replace(' ',' ', $item['date']))."" . "".(isset($item['url']) ? $item['url'] : '')."" . - "".\TracyDebugger::createEditorLink($this->wire('config')->paths->logs . 'tracy/' . $item['log'] . '.log', 1, (strlen($trimmedText) > 350 ? substr($trimmedText,0, 350)." ... (".strlen($trimmedText).")" : $trimmedText), 'View in your code editor').(\TracyDebugger::isJson($item['text']) ? "\n".\Tracy\Dumper::toHtml(json_decode($item['text'], true)) : '')."" . + "".TracyDebugger::createEditorLink($this->wire('config')->paths->logs . 'tracy/' . $item['log'] . '.log', 1, (strlen($trimmedText) > 350 ? substr($trimmedText,0, 350)." ... (".strlen($trimmedText).")" : $trimmedText), 'View in your code editor').(TracyDebugger::isJson($item['text']) ? "\n".\Tracy\Dumper::toHtml(json_decode($item['text'], true)) : '')."" . ""; } - $this->logEntries .= $sectionEnd; + $this->logEntries .= $this->sectionEnd(); } } // color icon based on errors/other log entries if($isNewErrors > 0) { - $this->iconColor = \TracyDebugger::COLOR_ALERT; + $this->iconColor = TracyDebugger::COLOR_ALERT; } elseif($isNew > 0) { - $this->iconColor = \TracyDebugger::COLOR_WARN; + $this->iconColor = TracyDebugger::COLOR_WARN; } else { - $this->iconColor = \TracyDebugger::COLOR_NORMAL; + $this->iconColor = TracyDebugger::COLOR_NORMAL; } $this->icon = ' @@ -148,32 +148,9 @@ public function getTab() { '; - return ' - ' . - $this->icon . (\TracyDebugger::getDataValue('showPanelLabels') ? 'Tracy Logs' : '') . ' - - '; - } - - protected function sectionHeader($columnNames = array()) { - $out = ' -
    - - - '; - foreach($columnNames as $columnName) { - $out .= ''; - } - - $out .= ' - - - - '; - return $out; + return $this->buildTab('Tracy Logs'); } - /** * Returns instance of FileLog for given log name * @@ -222,7 +199,7 @@ public function getFilename($name) { public function getLogs() { $logs = array(); - $dir = new DirectoryIterator($this->wire('config')->paths->logs.'tracy/'); + $dir = new \DirectoryIterator($this->wire('config')->paths->logs.'tracy/'); if(!@file_exists($this->wire('config')->paths->logs.'tracy/.')) { return null; } @@ -246,26 +223,20 @@ public function getLogs() { public function getPanel() { // Load all the panel sections - $isAdditionalBar = \TracyDebugger::isAdditionalBar(); - $out = '

    ' . $this->icon . ' Tracy Logs' . ($isAdditionalBar ? ' ('.$isAdditionalBar.')' : '') . '

    - -
    '; + $out = $this->buildPanelHeader('Tracy Logs', false, true); + $out .= $this->openPanel(); $out .= $this->logEntries; if($this->numLogEntries > 0) { $out .= '

    -
    + + '.$this->csrfInput().'

    '; } - $out .= \TracyDebugger::generatePanelFooter('tracyLogs', \Tracy\Debugger::timer('tracyLogs'), strlen($out), 'processwireAndTracyLogsPanels'); - - $out .= ' -
    '; - - return parent::loadResources() . $out; + return $this->closePanel($out, 'tracyLogs', 'processwireAndTracyLogsPanels'); } } diff --git a/panels/TracyTogglerPanel.php b/panels/TracyTogglerPanel.php index 4c33758b..0b9af146 100644 --- a/panels/TracyTogglerPanel.php +++ b/panels/TracyTogglerPanel.php @@ -1,4 +1,6 @@ -icon = ' - - + + '; $out = ' - - - ' . $this->icon . (\TracyDebugger::getDataValue('showPanelLabels') ? ' Disable Tracy' : '') . ' + + ' . $this->icon . (TracyDebugger::getDataValue('showPanelLabels') ? ' Disable Tracy' : '') . ' '; - \TracyDebugger::$panelGenerationTime['tracyToggler']['time'] = \Tracy\Debugger::timer('tracyToggler'); - \TracyDebugger::$panelGenerationTime['tracyToggler']['size'] = strlen($out); + TracyDebugger::$panelGenerationTime['tracyToggler']['time'] = Debugger::timer('tracyToggler'); + TracyDebugger::$panelGenerationTime['tracyToggler']['size'] = strlen($out); return $out; diff --git a/panels/UserSwitcherPanel.php b/panels/UserSwitcherPanel.php index c9442fe7..8f086467 100644 --- a/panels/UserSwitcherPanel.php +++ b/panels/UserSwitcherPanel.php @@ -1,4 +1,6 @@ -wire('user')->isLoggedin()) { - $iconColor = \TracyDebugger::COLOR_WARN; + $iconColor = TracyDebugger::COLOR_WARN; $this->switchedUser = true; } else { - $iconColor = \TracyDebugger::COLOR_ALERT; + $iconColor = TracyDebugger::COLOR_ALERT; } $this->icon = ' @@ -30,11 +32,7 @@ public function getTab() { '; - return ' - - ' . $this->icon . (\TracyDebugger::getDataValue('showPanelLabels') ? $this->wire('user')->name : '') . ' - - '; + return $this->buildTab('User Switcher', $this->wire('user')->name); } @@ -46,26 +44,26 @@ public function getPanel() { $userRoles[] = ''.$r->name.''; } - $out = ' -

    ' . $this->icon . ' User Switcher

    + $out = $this->buildPanelHeader('User Switcher'); + $out .= '

    ' . $this->wire('user')->name . '

    - - - + + +  '. implode(', ', $userRoles) .'

    '; $remainingSessionLength = $this->wire('config')->sessionExpireSeconds; - if(\TracyDebugger::getDataValue('userSwitchSession') != '') { - $userSwitchSession = \TracyDebugger::getDataValue('userSwitchSession'); + if(TracyDebugger::getDataValue('userSwitchSession') != '') { + $userSwitchSession = TracyDebugger::getDataValue('userSwitchSession'); $sessionSwitcherId = $this->wire('session')->tracyUserSwitcherId; - if(isset($userSwitchSession[$sessionSwitcherId])) { + if($sessionSwitcherId && isset($userSwitchSession[$sessionSwitcherId])) { $remainingSessionLength = ($userSwitchSession[$sessionSwitcherId] - time()) / 60; } if($remainingSessionLength <= 0) $remainingSessionLength = 0; @@ -76,31 +74,51 @@ public function getPanel() { '; } else { - $out .= '
    '; + $out .= ''; } - if(\TracyDebugger::$allowedSuperuser || $remainingSessionLength > 0) { + if(TracyDebugger::$allowedSuperuser || $remainingSessionLength > 0) { $out .= ' - '; if(!$this->wire('user')->isLoggedin()) $out .= ''; - if(\TracyDebugger::getDataValue('userSwitcherSelector')) { - $selectableUsers = $this->wire('users')->find(\TracyDebugger::getDataValue('userSwitcherSelector')); - } - elseif(\TracyDebugger::getDataValue('userSwitcherRestricted') && count(\TracyDebugger::getDataValue('userSwitcherRestricted')) > 0) { - $selectableUsers = $this->wire('users')->find('roles!='.implode(', roles!=', \TracyDebugger::getDataValue('userSwitcherRestricted'))); - } - elseif(\TracyDebugger::getDataValue('userSwitcherIncluded') && count(\TracyDebugger::getDataValue('userSwitcherIncluded')) > 0) { - $selectableUsers = $this->wire('users')->find('roles='.implode('|', \TracyDebugger::getDataValue('userSwitcherIncluded'))); + if(method_exists($this->wire('pages'), 'findRaw')) { + $findRawFields = ['id', 'name', 'email', 'roles']; + $findRawSelector = 'templates_id=' . implode('|', $this->wire('config')->userTemplateIDs) . ', has_parent=' . implode('|', $this->wire('config')->usersPageIDs) . ', objects=1, nulls=1, sort=name, status<' . Page::statusUnpublished; + if(TracyDebugger::getDataValue('userSwitcherSelector')) { + $selectableUsers = $this->wire('pages')->findRaw($findRawSelector . ', ' . TracyDebugger::getDataValue('userSwitcherSelector'), $findRawFields); + } + elseif(TracyDebugger::getDataValue('userSwitcherRestricted') && count(TracyDebugger::getDataValue('userSwitcherRestricted')) > 0) { + $selectableUsers = $this->wire('pages')->findRaw($findRawSelector . ', roles!='.implode(', roles!=', TracyDebugger::getDataValue('userSwitcherRestricted')), $findRawFields); + } + elseif(TracyDebugger::getDataValue('userSwitcherIncluded') && count(TracyDebugger::getDataValue('userSwitcherIncluded')) > 0) { + $selectableUsers = $this->wire('pages')->findRaw($findRawSelector . ', roles='.implode('|', TracyDebugger::getDataValue('userSwitcherIncluded')), $findRawFields); + } + else { + $selectableUsers = $this->wire('pages')->findRaw($findRawSelector, $findRawFields); + } } else { - $selectableUsers = $this->wire('users')->find(''); + if(TracyDebugger::getDataValue('userSwitcherSelector')) { + $selectableUsers = $this->wire('users')->find(TracyDebugger::getDataValue('userSwitcherSelector')); + } + elseif(TracyDebugger::getDataValue('userSwitcherRestricted') && count(TracyDebugger::getDataValue('userSwitcherRestricted')) > 0) { + $selectableUsers = $this->wire('users')->find('roles!='.implode(', roles!=', TracyDebugger::getDataValue('userSwitcherRestricted'))); + } + elseif(TracyDebugger::getDataValue('userSwitcherIncluded') && count(TracyDebugger::getDataValue('userSwitcherIncluded')) > 0) { + $selectableUsers = $this->wire('users')->find('roles='.implode('|', TracyDebugger::getDataValue('userSwitcherIncluded'))); + } + else { + $selectableUsers = $this->wire('users')->find(''); + } + $selectableUsers = $selectableUsers->sort('name'); } if(count($selectableUsers) > 10) { $tracyModuleUrl = $this->wire('config')->urls->TracyDebugger; + $nonceAttr = TracyDebugger::getNonceAttr(); $out .= <<< HTML - '; } if($this->wire('user')->isLoggedin()) $out .= ' '; - if($this->switchedUser && \TracyDebugger::getDataValue('originalUserSwitcher')) $out .= ' '; + if($this->switchedUser && TracyDebugger::getDataValue('originalUserSwitcher')) $out .= ' '; if($this->switchedUser) $out .= ''; $out .= ' '; - if(\TracyDebugger::$allowedSuperuser || $remainingSessionLength > 0) { + if(TracyDebugger::$allowedSuperuser || $remainingSessionLength > 0) { $out .= ' - '; } - $out .= \TracyDebugger::generatePanelFooter('userSwitcher', \Tracy\Debugger::timer('userSwitcher'), strlen($out), 'userSwitcherPanel'); - - $out .= ' -

    '; - - return parent::loadResources() . $out; + return $this->closePanel($out, 'userSwitcher', 'userSwitcherPanel'); } } diff --git a/panels/UsersPanel.php b/panels/UsersPanel.php index 5ae4d6ac..4057d427 100644 --- a/panels/UsersPanel.php +++ b/panels/UsersPanel.php @@ -1,4 +1,6 @@ -getUsers(); - $this->iconColor = $this->numNonSuperusers ? \TracyDebugger::COLOR_WARN : \TracyDebugger::COLOR_NORMAL; + $this->iconColor = $this->numNonSuperusers ? TracyDebugger::COLOR_WARN : TracyDebugger::COLOR_NORMAL; $this->icon = ' '; - return ' - - ' . $this->icon . (\TracyDebugger::getDataValue('showPanelLabels') ? ' Users' : '') . ' - - '; + return $this->buildTab('Users'); } @@ -77,24 +75,23 @@ public function getPanel() { $userRoles[] = ''.$r->name.''; } - $out = ' -

    ' . $this->icon . ' Users

    -
    + $out = $this->buildPanelHeader('Users'); + $out .= $this->openPanel() . '

    Current User

    - - + + '.$this->wire('user')->name.'

    - - - + + + '. implode(', ', $userRoles) .'

    @@ -106,7 +103,7 @@ public function getPanel() { $out .= '

    - + WARNING
    There are users with the "tracy-debugger" permission that are not superusers.

    This is only an issue if a live site is in DEVELOPMENT mode. Non-superusers will have no access in PRODUCTION mode.

    '; @@ -114,15 +111,15 @@ public function getPanel() { $out .= '

    - + Users
    '.$this->listTracyUsers.'

    - - - + + + Roles
    '.$this->listTracyRoles.'

    @@ -131,7 +128,7 @@ public function getPanel() {

    IP Address Restriction

    '; - $checkIpAddress = \TracyDebugger::checkIpAddress(); + $checkIpAddress = TracyDebugger::checkIpAddress(); if($checkIpAddress['ipAddress'] != '') { $out .= '

    Access for non-superusers is currently restricted to: ' . $checkIpAddress['ipAddress'] . '

    '; @@ -139,7 +136,7 @@ public function getPanel() { $out .= '

    - + This does not match your current IP address

    '; @@ -148,7 +145,7 @@ public function getPanel() { $out .= '

    - + This matches your current IP address

    '; @@ -157,7 +154,7 @@ public function getPanel() { else { $out .= '

    - + There is no defined IP Address restriction, so the non-superusers listed above will have full access from any location.

    '; @@ -167,12 +164,7 @@ public function getPanel() { $out .= '

    All allowed users are superusers

    If you want to allow other users, give them the "tracy-debugger" permission, and preferably also restrict via IP Address in the module config settings.

    '; } - $out .= \TracyDebugger::generatePanelFooter('users', \Tracy\Debugger::timer('users'), strlen($out)); - - $out .= ' -
    '; - - return parent::loadResources() . $out; + return $this->closePanel($out, 'users'); } } diff --git a/panels/ValidatorPanel.php b/panels/ValidatorPanel.php index 8f9cb004..bdfd8471 100644 --- a/panels/ValidatorPanel.php +++ b/panels/ValidatorPanel.php @@ -1,4 +1,6 @@ -validatorUrl = 'https://validator.w3.org/nu/'; $this->validationUrl = $this->validatorUrl . "?doc=".$this->wire('page')->httpUrl."&out=html&showimagereport=yes&showsource=yes"; @@ -25,19 +27,19 @@ public function getTab() { $http->setHeader('Content-Type', 'text/html; charset=utf-8'); $http->setHeader('User-Agent', 'ProcessWireTracyDebugger'); - $this->rawResult = $http->post($this->validatorUrl, \TracyDebugger::$pageHtml); + $this->rawResult = $http->post($this->validatorUrl, TracyDebugger::$pageHtml); //$this->rawResult = preg_replace('/[^(\x20-\x7F)]*/','', $this->rawResult); $this->rawResult = mb_encode_numericentity($this->rawResult, [0x80, 0x10FFFF, 0, ~0], 'UTF-8'); - $showPanelLabels = \TracyDebugger::getDataValue('showPanelLabels'); + $showPanelLabels = TracyDebugger::getDataValue('showPanelLabels'); if($this->rawResult) { - $doc = new DOMDocument(); + $doc = new \DOMDocument(); libxml_use_internal_errors(true); $doc->loadHTML($this->rawResult, LIBXML_HTML_NODEFDTD | LIBXML_HTML_NOIMPLIED); libxml_use_internal_errors(false); - $xpath = new DOMXPath($doc); + $xpath = new \DOMXPath($doc); $success = $xpath->query("//*[@class='success']"); $failure = $xpath->query("//*[@class='failure']"); @@ -61,7 +63,7 @@ public function getTab() { strpos($this->rawResult, 'Connection refused') !== false) ? true : false; if($errors) { - $this->color = \TracyDebugger::COLOR_ALERT; + $this->color = TracyDebugger::COLOR_ALERT; $this->icon = ' '; @@ -69,7 +71,7 @@ public function getTab() { $this->label = $showPanelLabels ? $this->resultLabel : ''; } elseif(strpos($this->rawResult, 'class="warning"') !== false || strpos($this->rawResult, 'class="info"') !== false || strpos($this->rawResult, 'class="info warning"') !== false) { - $this->color = \TracyDebugger::COLOR_WARN; + $this->color = TracyDebugger::COLOR_WARN; $this->icon = ' '; @@ -77,7 +79,7 @@ public function getTab() { $this->label = $showPanelLabels ? $this->resultLabel : ''; } else { - $this->color = \TracyDebugger::COLOR_NORMAL; + $this->color = TracyDebugger::COLOR_NORMAL; $this->icon = ' '; @@ -100,8 +102,8 @@ public function getTab() { public function getPanel() { $out = ' -

    '.str_replace('#FFFFFF', $this->color, $this->icon).' '.$this->resultLabel.'

    -
    +

    '.str_replace('#FFFFFF', $this->color, $this->icon).' '.$this->resultLabel.'

    + ' . $this->openPanel() . ' ' + + '' + + '' + + '
    ' + + '
    ' + + '

    ' + + '
    ' + + '
    ' + + '
    ' + escapedContents + '
    ' + + '
    ' + + '
    • Exception log file
    ' + + '
    ' + + '
    '; + + Tracy.BlueScreen.loadAjax(bsContent); } - document.getElementById("tracyExceptionsViewerCode").innerHTML = fileData.contents; - document.getElementById("tracy-bs").style.zIndex = "100"; + + var tracyBs = document.getElementById("tracy-bs"); + if(tracyBs) tracyBs.style.zIndex = "100"; + } xmlhttp.getAllResponseHeaders(); } }; - xmlhttp.open("POST", tracyExceptionsViewer.currentUrl, true); + xmlhttp.open("POST", tracyExceptionsViewer.currentURL, true); xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); xmlhttp.setRequestHeader("X-Requested-With", "XMLHttpRequest"); - xmlhttp.send("filePath=" + filePath); + xmlhttp.send("filePath=" + encodeURIComponent(filePath) + "&csrfToken=" + encodeURIComponent(tracyExceptionsViewer.csrfToken)); } }, loadExceptionFile: function(filePath) { - if(document.getElementById("tracy-debug-panel-TracyExceptionsPanel").classList.contains("tracy-mode-window")) { + if(document.getElementById("tracy-debug-panel-ProcessWire-TracyExceptionsPanel").classList.contains("tracy-mode-window")) { this.populateExceptionViewer(filePath); } else { - if(!window.Tracy.Debug.panels || !document.getElementById("tracy-debug-panel-TracyExceptionsPanel")) { - window.requestAnimationFrame(tracyExceptionLoader.loadExceptionFile(filePath)); + if(!window.Tracy.Debug.panels || !document.getElementById("tracy-debug-panel-ProcessWire-TracyExceptionsPanel")) { + window.requestAnimationFrame(function() { tracyExceptionLoader.loadExceptionFile(filePath); }); } else { - var panel = window.Tracy.Debug.panels["tracy-debug-panel-TracyExceptionsPanel"]; + var panel = window.Tracy.Debug.panels["tracy-debug-panel-ProcessWire-TracyExceptionsPanel"]; if(panel.elem.dataset.tracyContent) { panel.init(); } @@ -89,6 +162,7 @@ if(!tracyExceptionLoader) { const queryStr = curEl.href.split('?')[1]; const fullFilePath = tracyExceptionLoader.getFileLineVars(queryStr, "f"); tracyExceptionLoader.loadExceptionFile(fullFilePath); + if (typeof showUnloadButton === 'function') showUnloadButton(); } const tracyExceptionFiles = document.getElementById("tracyExceptionFiles"); @@ -99,7 +173,7 @@ if(!tracyExceptionLoader) { const queryStr = tracyExceptions[i].getElementsByTagName("a")[0].href.split('?')[1]; const currentFilePath = decodeURI(queryStr.replace('f=', '').replace('&l=1', '')).replace('site/assets/logs/tracy/', ''); tracyExceptions[i].getElementsByTagName("a")[0].className = - document.getElementById('panelTitleFilePath').innerHTML === currentFilePath ? "active" : ""; + document.getElementById('panelTitleFilePath').textContent === currentFilePath ? "active" : ""; } } } diff --git a/scripts/file-editor-search.js b/scripts/file-editor-search.js index a05214d4..e2654e5b 100644 --- a/scripts/file-editor-search.js +++ b/scripts/file-editor-search.js @@ -16,7 +16,7 @@ addFilterBox({ } }, addTo: { - selector: "#tracy-debug-panel-FileEditorPanel .tracy-inner", + selector: "#tracy-debug-panel-ProcessWire-FileEditorPanel .tracy-inner", position: "append" }, inputDelay: 500, @@ -87,4 +87,4 @@ addFilterBox({ } catch (e) {} } } -}); \ No newline at end of file +}); diff --git a/scripts/file-editor.js b/scripts/file-editor.js index 0a723a60..b91b1b13 100644 --- a/scripts/file-editor.js +++ b/scripts/file-editor.js @@ -25,7 +25,7 @@ if(!tracyFileEditorLoader) { recentlyOpenSelect.options[recentlyOpenSelect.options.length] = new Option(storedFiles[i], storedFiles[i]); } } - var initialFile = document.getElementById('panelTitleFilePath').innerHTML; + var initialFile = document.getElementById('panelTitleFilePath').textContent; tracyFileEditorLoader.addRecentlyOpenedFile(initialFile); } }, @@ -60,10 +60,10 @@ if(!tracyFileEditorLoader) { if (parts.length == 2) return parts.pop().split(";").shift(); }, - createButton: function(name, value, onlick) { + createButton: function(name, value, needsRawCode) { var button = ' '; + if(needsRawCode) button += ' data-raw-file-editor-code="1"'; + button += ' value="'+value+'" /> '; return button; }, @@ -100,11 +100,15 @@ if(!tracyFileEditorLoader) { populateFileEditor: function(filePath, line) { document.getElementById("fileEditorFilePath").value = filePath; - document.cookie = "tracyFileEditorFilePath=" + filePath + "; path=/"; - document.getElementById("panelTitleFilePath").innerHTML = "/" + filePath; + document.cookie = "tracyFileEditorFilePath=" + filePath + "; path=/; SameSite=Strict"; + var fePanel = document.getElementById("tracy-debug-panel-ProcessWire-FileEditorPanel"); + if(fePanel) { + var titleEl = fePanel.querySelector("#panelTitleFilePath"); + if(titleEl) titleEl.textContent = "/" + filePath; + } if(typeof tracyFileEditor === "undefined" || !tracyFileEditor.tfe) { - window.requestAnimationFrame(tracyFileEditorLoader.populateFileEditor(filePath, line)); + window.requestAnimationFrame(function() { tracyFileEditorLoader.populateFileEditor(filePath, line); }); } else { var xmlhttp; @@ -116,6 +120,11 @@ if(!tracyFileEditorLoader) { tracyFileEditorLoader.generateButtons(fileData); tracyFileEditor.tfe.setValue(fileData["contents"]); tracyFileEditor.tfe.gotoLine(line, 0); + var fePanel = document.getElementById("tracy-debug-panel-ProcessWire-FileEditorPanel"); + if(fePanel) { + var titleEl = fePanel.querySelector("#panelTitleFilePath"); + if(titleEl) titleEl.textContent = "/" + filePath; + } // set mode appropriately var mode = tracyFileEditor.modelist.getModeForPath(filePath).mode; @@ -128,22 +137,23 @@ if(!tracyFileEditorLoader) { xmlhttp.open("POST", tracyFileEditor.currentUrl, true); xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); xmlhttp.setRequestHeader("X-Requested-With", "XMLHttpRequest"); - xmlhttp.send("filePath=" + filePath); + xmlhttp.send("filePath=" + encodeURIComponent(filePath) + "&csrfToken=" + encodeURIComponent(tracyFileEditor.csrfToken)); init_php_file_tree(filePath); } }, loadFileEditor: function(filePath, line) { + var panelElem = document.getElementById("tracy-debug-panel-ProcessWire-FileEditorPanel"); - if(document.getElementById("tracy-debug-panel-FileEditorPanel").classList.contains("tracy-mode-window")) { + if(panelElem && panelElem.classList.contains("tracy-mode-window")) { this.populateFileEditor(filePath, line); } else { - if(!window.Tracy.Debug.panels || !document.getElementById("tracy-debug-panel-FileEditorPanel")) { - window.requestAnimationFrame(tracyFileEditorLoader.loadFileEditor(filePath, line)); + if(!window.Tracy.Debug.panels || !panelElem) { + window.requestAnimationFrame(function() { tracyFileEditorLoader.loadFileEditor(filePath, line); }); } else { - var panel = window.Tracy.Debug.panels["tracy-debug-panel-FileEditorPanel"]; + var panel = window.Tracy.Debug.panels["tracy-debug-panel-ProcessWire-FileEditorPanel"]; if(panel.elem.dataset.tracyContent) { panel.init(); } diff --git a/scripts/main.js b/scripts/main.js index 1177690d..de6ba058 100644 --- a/scripts/main.js +++ b/scripts/main.js @@ -1,5 +1,5 @@ function tracyResizePanel(panel) { - panel = document.getElementById("tracy-debug-panel-" + panel); + panel = document.getElementById("tracy-debug-panel-ProcessWire-" + panel); var tracyPanel = window.Tracy.Debug.panels[panel.id]; tracyPanel.elem.classList.add('tracy-panel-resized'); tracyPanel.elem.dataset.tracyContent = true; // hack to satisy condition in Tracy's restorePosition() method @@ -23,9 +23,57 @@ function tracyResizePanel(panel) { } function tracyClosePanel(panel) { - localStorage.setItem("remove-tracy-debug-panel-" + panel + "Panel", 1); + localStorage.setItem("remove-tracy-debug-panel-ProcessWire-" + panel + "Panel", 1); } +// Delegated listeners for data-attribute-driven actions (replaces inline handlers) +document.addEventListener('click', function(e) { + var el = e.target.closest('[data-tracy-resize]'); + if(el) { + e.preventDefault(); + tracyResizePanel(el.getAttribute('data-tracy-resize')); + return; + } + el = e.target.closest('[data-tracy-close]'); + if(el) { + tracyClosePanel(el.getAttribute('data-tracy-close')); + return; + } + el = e.target.closest('[data-tracy-filterbox-clear]'); + if(el) { + var input = el.parentElement.querySelector('input'); + input.getFilterBox().clearFilterBox(); + input.focus(); + return; + } + el = e.target.closest('[data-dumps-toggle]'); + if(el) { + tracyDumpsToggler(el, el.getAttribute('data-dumps-toggle') === 'expand'); + return; + } + el = e.target.closest('[data-dump-type]'); + if(el) { + e.preventDefault(); + toggleDumpType(el, el.getAttribute('data-dump-type'), el.getAttribute('data-dump-class-ext')); + return; + } + el = e.target.closest('[data-raw-file-editor-code]'); + if(el && typeof tracyFileEditor !== 'undefined') { + tracyFileEditor.getRawFileEditorCode(); + return; + } +}, true); + +document.addEventListener('submit', function(e) { + if(!e.target.closest || !e.target.closest('.tracy-panel')) return; + var confirm_msg = e.target.getAttribute('data-confirm'); + if(confirm_msg) { + if(!confirm(confirm_msg)) { + e.preventDefault(); + } + } +}); + // panel rollup thanks to @tpr / @rolandtoth // jQuery .on() equivalent var filterEventHandler = function (selector, callback) { @@ -152,7 +200,7 @@ function setupTracyPanelFilterBoxDisplays (fbx) { }, attrs: { class: "tracy-filterbox-clear", - onclick: "var input = this.parentElement.querySelector('input'); input.getFilterBox().clearFilterBox(); input.focus();" + "data-tracy-filterbox-clear": "1" }, text: function () { return fbx.getFilter() ? "×" : ""; diff --git a/scripts/php-file-tree/php_file_tree.js b/scripts/php-file-tree/php_file_tree.js index fbdd38c5..552a1028 100644 --- a/scripts/php-file-tree/php_file_tree.js +++ b/scripts/php-file-tree/php_file_tree.js @@ -35,6 +35,11 @@ function init_php_file_tree(linkedFilePath) { if (!document.getElementsByTagName || !document.getElementById("tracyFoldersFiles")) return; + var treeEl = document.getElementById("tracyFoldersFiles"); + var els = treeEl.querySelectorAll(".active"); + [].forEach.call(els, function (el) { + el.classList.remove("active"); + }); var path = ''; var aMenus = document.getElementById("tracyFoldersFiles").getElementsByTagName("LI"); for (var i = 0; i < aMenus.length; i++) { @@ -95,9 +100,10 @@ function init_php_file_tree(linkedFilePath) { var parser = document.createElement('a'); var queryStr = items[j].href.split('?')[1]; var currentFilePath = decodeURI(queryStr.replace('f=','').replace('&l=1','')); - if(document.getElementById('panelTitleFilePath').innerHTML == currentFilePath || linkedFilePath == currentFilePath) { - var els = document.getElementsByClassName("active"); - [].forEach.call(els, function (el) { + var fePanelEl = document.getElementById("tracy-debug-panel-ProcessWire-FileEditorPanel"); + var titleContent = fePanelEl ? (fePanelEl.querySelector("#panelTitleFilePath") || {}).innerHTML : ""; + if(titleContent == currentFilePath || linkedFilePath == currentFilePath) { + treeEl.querySelectorAll(".active").forEach(function (el) { el.classList.remove("active"); }); items[j].classList.add("active"); diff --git a/scripts/php-info-search.js b/scripts/php-info-search.js index 79243c41..b8feeea0 100644 --- a/scripts/php-info-search.js +++ b/scripts/php-info-search.js @@ -16,7 +16,7 @@ addFilterBox({ } }, addTo: { - selector: "#tracy-debug-panel-PhpInfoPanel .tracy-icons", + selector: "#tracy-debug-panel-ProcessWire-PhpInfoPanel .tracy-icons", position: "before" }, inputDelay: 500, @@ -116,7 +116,7 @@ addFilterBox({ } } - window.Tracy.Debug.panels['tracy-debug-panel-PhpInfoPanel'].reposition(); + window.Tracy.Debug.panels['tracy-debug-panel-ProcessWire-PhpInfoPanel'].reposition(); } } -}); \ No newline at end of file +}); diff --git a/scripts/user-switcher-search.js b/scripts/user-switcher-search.js index 2766f85b..1612b015 100644 --- a/scripts/user-switcher-search.js +++ b/scripts/user-switcher-search.js @@ -1,7 +1,7 @@ addFilterBox({ suffix: "-userswitcher-panel", target: { - selector: "#tracy-debug-panel-UserSwitcherPanel select[name='userSwitcher']", + selector: "#tracy-debug-panel-ProcessWire-UserSwitcherPanel select[name='userSwitcher']", items: "option" }, extraFilterAttrs: [ @@ -19,7 +19,7 @@ addFilterBox({ } }, addTo: { - selector: "#tracy-debug-panel-UserSwitcherPanel .tracy-icons", + selector: "#tracy-debug-panel-ProcessWire-UserSwitcherPanel .tracy-icons", position: "before" }, displays: setupTracyPanelFilterBoxDisplays diff --git a/styles/styles.css b/styles/styles.css index 0ab38998..8f0c8022 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -560,11 +560,11 @@ td pre.tracy-dump { pointer-events: none; } -#tracy-debug-panel-PanelSelectorPanel label { +#tracy-debug-panel-ProcessWire-PanelSelectorPanel label { line-height: 1.75 !important; } -#tracy-debug-panel-PanelSelectorPanel fieldset div label:hover { +#tracy-debug-panel-ProcessWire-PanelSelectorPanel fieldset div label:hover { background: #E2E9EF !important; } @@ -746,7 +746,7 @@ ul.pw-info-links { align-items: center; width: 100%; min-height: 30px; - background: #373b41; + background: #40464C; margin: 0; padding: 0; } @@ -756,7 +756,7 @@ ul.pw-info-links { overflow-x: auto; overflow-y: hidden; white-space: nowrap; - background: #373b41; + background: #40464C; margin: 0; padding: 0; } @@ -789,7 +789,7 @@ ul.pw-info-links { margin: 0 1px 0 0; padding: 5px 3px 2px 8px; height: 35px; - background-color: #2D2D2D; + background-color: #40464C; color: #D0D0D0; border: none; cursor: pointer; @@ -845,7 +845,7 @@ ul.pw-info-links { padding: 8px 12px; font-size: 14px; line-height: 19px; - background-color: #3E4451; + background-color: #596069; color: #DCDCDC; border: none; cursor: pointer; @@ -1041,45 +1041,45 @@ ul.pw-info-links { cursor: default !important; } -#tracy-debug-panel-AdminerPanel { +#tracy-debug-panel-ProcessWire-AdminerPanel { height: 75%; min-height: 300px !important; - width: 1400px; + width: 1400px !important; } -#tracy-debug-panel-FileEditorPanel { +#tracy-debug-panel-ProcessWire-FileEditorPanel { height: 75%; min-height: 300px !important; - width: 1400px; + width: 1400px !important; } -#tracy-debug-panel-ConsolePanel, -#tracy-debug-panel-ValidatorPanel, -#tracy-debug-panel-TerminalPanel { +#tracy-debug-panel-ProcessWire-ConsolePanel, +#tracy-debug-panel-ProcessWire-ValidatorPanel, +#tracy-debug-panel-ProcessWire-TerminalPanel { height: 75%; min-height: 300px !important; - width: 1400px; + width: 1400px !important; min-width: 825px !important; padding: 0px !important; margin: 0px !important; } -#tracy-debug-panel-ModuleDisablerPanel { - width: 900px; +#tracy-debug-panel-ProcessWire-ModuleDisablerPanel { + width: 900px !important; min-width: 700px !important; } -#tracy-debug-panel-TemplateResourcesPanel, -#tracy-debug-panel-ToDoPanel { - width: 1400px; +#tracy-debug-panel-ProcessWire-TemplateResourcesPanel, +#tracy-debug-panel-ProcessWire-ToDoPanel { + width: 1400px !important; height: 75%; } -#tracy-debug-panel-ConsolePanel fieldset { +#tracy-debug-panel-ProcessWire-ConsolePanel fieldset { width: 100% !important; } -#tracy-debug-panel-ConsolePanel.tracy-mode-window { +#tracy-debug-panel-ProcessWire-ConsolePanel.tracy-mode-window { width: 100% !important; } @@ -1214,22 +1214,22 @@ ul.pw-info-links { vertical-align: middle !important; } -#tracy-debug-panel-CaptainHookPanel .tracy-inner .tracy-toggle:not(.tracy-collapsed), -#tracy-debug-panel-ApiExplorerPanel .tracy-inner .tracy-toggle:not(.tracy-collapsed) { +#tracy-debug-panel-ProcessWire-CaptainHookPanel .tracy-inner .tracy-toggle:not(.tracy-collapsed), +#tracy-debug-panel-ProcessWire-ApiExplorerPanel .tracy-inner .tracy-toggle:not(.tracy-collapsed) { color: #e83561 !important; } -#tracy-debug-panel-CaptainHookPanel .tracy-inner .tracy-toggle, -#tracy-debug-panel-ApiExplorerPanel .tracy-inner .tracy-toggle { +#tracy-debug-panel-ProcessWire-CaptainHookPanel .tracy-inner .tracy-toggle, +#tracy-debug-panel-ProcessWire-ApiExplorerPanel .tracy-inner .tracy-toggle { display: block; } -#tracy-debug-panel-CaptainHookPanel .tracy-inner .tracy-toggle + div > p:first-child { +#tracy-debug-panel-ProcessWire-CaptainHookPanel .tracy-inner .tracy-toggle + div > p:first-child { margin: 0 0 5px !important; } -#tracy-debug-panel-CaptainHookPanel .tracy-inner .tracy-toggle + div, -#tracy-debug-panel-ApiExplorerPanel .tracy-inner .tracy-toggle + div { +#tracy-debug-panel-ProcessWire-CaptainHookPanel .tracy-inner .tracy-toggle + div, +#tracy-debug-panel-ProcessWire-ApiExplorerPanel .tracy-inner .tracy-toggle + div { margin-bottom: 12px; padding-left: 12px; } @@ -1272,7 +1272,7 @@ ul.pw-info-links { border-radius: 0 !important; } -#tracy-debug-panel-FileEditorPanel.tracy-mode-window { +#tracy-debug-panel-ProcessWire-FileEditorPanel.tracy-mode-window { width: 100% !important; } @@ -1841,7 +1841,11 @@ ul.pw-info-links { list-style: none; margin: 0 0 0 5px; padding: 0; - width: 400px; + width: 300px; +} + +#tracy-debug-panel-ProcessWire-TracyExceptionsPanel[class*="tracy-mode-"] { + min-width: 575px; } .tracy-file-tree ul ul { diff --git a/tracy-2.10.x/src/Tracy/Bar/assets/bar.js b/tracy-2.10.x/src/Tracy/Bar/assets/bar.js index 40375735..901c2993 100644 --- a/tracy-2.10.x/src/Tracy/Bar/assets/bar.js +++ b/tracy-2.10.x/src/Tracy/Bar/assets/bar.js @@ -150,10 +150,17 @@ class Panel { } let doc = win.document; - doc.write('' - + '' - + '', - ); + doc.open(); + doc.close(); + let charset = doc.createElement('meta'); + charset.setAttribute('charset', 'utf-8'); + doc.head.appendChild(charset); + doc.body.id = 'tracy-debug'; + let s = doc.createElement('script'); + s.src = baseUrl + '_tracy_bar=js&XDEBUG_SESSION_STOP=1'; + s.async = true; + s.addEventListener('load', function() { Tracy.Dumper.init(); }); + doc.head.appendChild(s); let meta = this.elem.parentElement.lastElementChild; doc.body.innerHTML = '' diff --git a/tracy-2.10.x/src/Tracy/Helpers.php b/tracy-2.10.x/src/Tracy/Helpers.php index 8c0e3587..bc7d3efe 100644 --- a/tracy-2.10.x/src/Tracy/Helpers.php +++ b/tracy-2.10.x/src/Tracy/Helpers.php @@ -330,7 +330,7 @@ public static function isCli(): bool /** @internal */ public static function getNonceAttr(): string { - return preg_match('#^Content-Security-Policy(?:-Report-Only)?:.*\sscript-src\s+(?:[^;]+\s)?\'nonce-([\w+/]+=*)\'#mi', implode("\n", headers_list()), $m) + return preg_match('#^Content-Security-Policy(?:-Report-Only)?:.*\s(?:script-src|script-src-elem)\s+(?:[^;]+\s)?\'nonce-([\w+/]+=*)\'#mi', implode("\n", headers_list()), $m) ? ' nonce="' . self::escapeHtml($m[1]) . '"' : ''; } diff --git a/tracy-2.11.x/src/Tracy/Bar/assets/bar.js b/tracy-2.11.x/src/Tracy/Bar/assets/bar.js index 56f23795..7167d707 100644 --- a/tracy-2.11.x/src/Tracy/Bar/assets/bar.js +++ b/tracy-2.11.x/src/Tracy/Bar/assets/bar.js @@ -150,10 +150,17 @@ class Panel { } let doc = win.document; - doc.write('' - + '' - + '', - ); + doc.open(); + doc.close(); + let charset = doc.createElement('meta'); + charset.setAttribute('charset', 'utf-8'); + doc.head.appendChild(charset); + doc.body.id = 'tracy-debug'; + let s = doc.createElement('script'); + s.src = baseUrl + '_tracy_bar=js&XDEBUG_SESSION_STOP=1'; + s.async = true; + s.addEventListener('load', function() { Tracy.Dumper.init(); }); + doc.head.appendChild(s); let meta = this.elem.parentElement.lastElementChild; doc.body.innerHTML = '' diff --git a/tracy-2.11.x/src/Tracy/Helpers.php b/tracy-2.11.x/src/Tracy/Helpers.php index be6bf27d..90ee8e2f 100755 --- a/tracy-2.11.x/src/Tracy/Helpers.php +++ b/tracy-2.11.x/src/Tracy/Helpers.php @@ -334,7 +334,7 @@ public static function isCli(): bool /** @internal */ public static function getNonce(): ?string { - return preg_match('#^Content-Security-Policy(?:-Report-Only)?:.*\sscript-src\s+(?:[^;]+\s)?\'nonce-([\w+/]+=*)\'#mi', implode("\n", headers_list()), $m) + return preg_match('#^Content-Security-Policy(?:-Report-Only)?:.*\s(?:script-src|script-src-elem)\s+(?:[^;]+\s)?\'nonce-([\w+/]+=*)\'#mi', implode("\n", headers_list()), $m) ? $m[1] : null; } diff --git a/tracy-2.5.x/.eslintrc.js b/tracy-2.5.x/.eslintrc.js deleted file mode 100644 index 246bc1be..00000000 --- a/tracy-2.5.x/.eslintrc.js +++ /dev/null @@ -1,21 +0,0 @@ -module.exports = { - 'env': { - 'browser': true - }, - 'parserOptions': { - 'ecmaVersion': 6 - }, - 'globals': { - 'Tracy': true - }, - 'extends': 'eslint:recommended', - 'rules': { - 'indent': ['error', 'tab'], - 'quotes': ['error', 'single'], - 'semi': ['error', 'always'], - 'prefer-arrow-callback': ['error'], - 'arrow-parens': ['error'], - 'arrow-spacing': ['error'], - 'no-var': ['error'] - } -}; diff --git a/tracy-2.5.x/appveyor.yml b/tracy-2.5.x/appveyor.yml deleted file mode 100644 index f198beaf..00000000 --- a/tracy-2.5.x/appveyor.yml +++ /dev/null @@ -1,33 +0,0 @@ -build: off -cache: - - c:\php -> appveyor.yml - - '%LOCALAPPDATA%\Composer\files -> appveyor.yml' - -clone_folder: c:\projects\tracy - -init: - - SET PATH=c:\php;%PATH% - - SET PHP=1 - - SET ANSICON=121x90 (121x90) - -install: - # Install PHP - - IF EXIST c:\php (SET PHP=0) ELSE (mkdir c:\php) - - IF %PHP%==1 cd c:\php - - IF %PHP%==1 curl https://windows.php.net/downloads/releases/archives/php-5.6.14-Win32-VC11-x86.zip --output php.zip - - IF %PHP%==1 7z x php.zip >nul - - IF %PHP%==1 echo extension_dir=ext >> php.ini - - IF %PHP%==1 echo extension=php_openssl.dll >> php.ini - - IF %PHP%==1 del /Q *.zip - - cd c:\projects\tracy - - # Install Nette Tester - - appveyor DownloadFile https://getcomposer.org/composer.phar - - php composer.phar install --prefer-dist --no-interaction --no-progress - -test_script: - - vendor\bin\tester tests -s - -on_failure: - # Print *.actual content - - for /r %%x in (*.actual) do ( type "%%x" ) diff --git a/tracy-2.5.x/composer.json b/tracy-2.5.x/composer.json deleted file mode 100644 index e6f4c1f8..00000000 --- a/tracy-2.5.x/composer.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "tracy/tracy", - "description": "😎 Tracy: the addictive tool to ease debugging PHP code for cool developers. Friendly design, logging, profiler, advanced features like debugging AJAX calls or CLI support. You will love it.", - "keywords": ["debug", "debugger", "nette", "profiler", "xdebug"], - "homepage": "https://tracy.nette.org", - "license": ["BSD-3-Clause"], - "authors": [ - { - "name": "David Grudl", - "homepage": "https://davidgrudl.com" - }, - { - "name": "Nette Community", - "homepage": "https://nette.org/contributors" - } - ], - "require": { - "php": ">=5.4.4", - "ext-session": "*", - "ext-json": "*" - }, - "require-dev": { - "nette/utils": "~2.3", - "nette/di": "~2.3 || ~3.0.0", - "nette/tester": "~1.7 || ~2.0" - }, - "suggest": { - "https://nette.org/donate": "Please support Tracy via a donation" - }, - "autoload": { - "classmap": ["src"], - "files": ["src/shortcuts.php"] - }, - "minimum-stability": "dev", - "extra": { - "branch-alias": { - "dev-master": "2.5-dev" - } - } -} diff --git a/tracy-2.5.x/contributing.md b/tracy-2.5.x/contributing.md deleted file mode 100644 index 184152c0..00000000 --- a/tracy-2.5.x/contributing.md +++ /dev/null @@ -1,33 +0,0 @@ -How to contribute & use the issue tracker -========================================= - -Nette welcomes your contributions. There are several ways to help out: - -* Create an issue on GitHub, if you have found a bug -* Write test cases for open bug issues -* Write fixes for open bug/feature issues, preferably with test cases included -* Contribute to the [documentation](https://nette.org/en/writing) - -Issues ------- - -Please **do not use the issue tracker to ask questions**. We will be happy to help you -on [Nette forum](https://forum.nette.org) or chat with us on [Gitter](https://gitter.im/nette/nette). - -A good bug report shouldn't leave others needing to chase you up for more -information. Please try to be as detailed as possible in your report. - -**Feature requests** are welcome. But take a moment to find out whether your idea -fits with the scope and aims of the project. It's up to *you* to make a strong -case to convince the project's developers of the merits of this feature. - -Contributing ------------- - -If you'd like to contribute, please take a moment to read [the contributing guide](https://nette.org/en/contributing). - -The best way to propose a feature is to discuss your ideas on [Nette forum](https://forum.nette.org) before implementing them. - -Please do not fix whitespace, format code, or make a purely cosmetic patch. - -Thanks! :heart: diff --git a/tracy-2.5.x/examples/ajax.php b/tracy-2.5.x/examples/ajax.php deleted file mode 100644 index 6fb2f087..00000000 --- a/tracy-2.5.x/examples/ajax.php +++ /dev/null @@ -1,76 +0,0 @@ - - - -

    Tracy: AJAX demo

    - -

    - see Debug Bar in the bottom right corner -

    - -

    - use ESC to toggle BlueScreen -

    - - - - - - -For security reasons, Tracy is visible only on localhost. Look into the source code to see how to enable Tracy.

    '; -} diff --git a/tracy-2.5.x/examples/assets/E_COMPILE_ERROR.php b/tracy-2.5.x/examples/assets/E_COMPILE_ERROR.php deleted file mode 100644 index 9f4a9230..00000000 --- a/tracy-2.5.x/examples/assets/E_COMPILE_ERROR.php +++ /dev/null @@ -1,3 +0,0 @@ - - - -

    Tracy: bar dump demo

    - -

    You can dump variables to bar in rightmost bottom egde.

    - -test', 'String'); - - -if (Debugger::$productionMode) { - echo '

    For security reasons, Tracy is visible only on localhost. Look into the source code to see how to enable Tracy.

    '; -} diff --git a/tracy-2.5.x/examples/dump.php b/tracy-2.5.x/examples/dump.php deleted file mode 100644 index 92635f2e..00000000 --- a/tracy-2.5.x/examples/dump.php +++ /dev/null @@ -1,48 +0,0 @@ - - - -

    Tracy: Dumper demo

    - -test'); - -dump($arr); - -dump($obj); - - -echo "

    With location

    \n"; - -Debugger::$showLocation = true; - -dump($arr); - - -if (Debugger::$productionMode) { - echo '

    For security reasons, Tracy is visible only on localhost. Look into the source code to see how to enable Tracy.

    '; -} diff --git a/tracy-2.5.x/examples/exception.php b/tracy-2.5.x/examples/exception.php deleted file mode 100644 index 35d44aba..00000000 --- a/tracy-2.5.x/examples/exception.php +++ /dev/null @@ -1,50 +0,0 @@ - - - -

    Tracy: exception demo

    - -second(true, false); - } - - - public function second($arg1, $arg2) - { - self::third([1, 2, 3]); - } - - - public static function third($arg1) - { - throw new Exception('The my exception', 123); - } -} - - -function demo($a, $b) -{ - $demo = new DemoClass; - $demo->first($a, $b); -} - - -if (Debugger::$productionMode) { - echo '

    For security reasons, Tracy is visible only on localhost. Look into the source code to see how to enable Tracy.

    '; -} - -demo(10, 'any string'); diff --git a/tracy-2.5.x/examples/fatal-error.php b/tracy-2.5.x/examples/fatal-error.php deleted file mode 100644 index 9e5bcfc0..00000000 --- a/tracy-2.5.x/examples/fatal-error.php +++ /dev/null @@ -1,22 +0,0 @@ - - - -

    Tracy: fatal error demo

    - -For security reasons, Tracy is visible only on localhost. Look into the source code to see how to enable Tracy.

    '; -} - -require __DIR__ . '/assets/E_COMPILE_ERROR.php'; diff --git a/tracy-2.5.x/examples/firelogger.php b/tracy-2.5.x/examples/firelogger.php deleted file mode 100644 index 8d07195e..00000000 --- a/tracy-2.5.x/examples/firelogger.php +++ /dev/null @@ -1,43 +0,0 @@ - 'val1', 'key2' => true]]; - -// will show in FireLogger -Debugger::fireLog('Hello World'); -Debugger::fireLog($arr); - - -function first($arg1, $arg2) -{ - second(true, false); -} - - -function second($arg1, $arg2) -{ - third([1, 2, 3]); -} - - -function third($arg1) -{ - throw new Exception('The my exception', 123); -} - - -try { - first(10, 'any string'); -} catch (Exception $e) { - Debugger::fireLog($e); -} - -?> - - -

    Tracy: FireLogger demo

    - -

    How to enable FireLogger?

    diff --git a/tracy-2.5.x/examples/notice.php b/tracy-2.5.x/examples/notice.php deleted file mode 100644 index 461f18e5..00000000 --- a/tracy-2.5.x/examples/notice.php +++ /dev/null @@ -1,31 +0,0 @@ - - - -

    Tracy Notice and StrictMode demo

    - -For security reasons, Tracy is visible only on localhost. Look into the source code to see how to enable Tracy.

    '; -} diff --git a/tracy-2.5.x/examples/output-debugger.php b/tracy-2.5.x/examples/output-debugger.php deleted file mode 100644 index 9bf7a767..00000000 --- a/tracy-2.5.x/examples/output-debugger.php +++ /dev/null @@ -1,15 +0,0 @@ -'; -} - - -head(); -echo '

    Output Debugger demo

    '; diff --git a/tracy-2.5.x/examples/preloading.php b/tracy-2.5.x/examples/preloading.php deleted file mode 100644 index 58bc9db8..00000000 --- a/tracy-2.5.x/examples/preloading.php +++ /dev/null @@ -1,35 +0,0 @@ - - - -

    Tracy: Preloading

    - -renderLoader() ?> - - - - -For security reasons, Tracy is visible only on localhost. Look into the source code to see how to enable Tracy.

    '; -} diff --git a/tracy-2.5.x/examples/redirect.php b/tracy-2.5.x/examples/redirect.php deleted file mode 100644 index 9ffc7a70..00000000 --- a/tracy-2.5.x/examples/redirect.php +++ /dev/null @@ -1,32 +0,0 @@ - - - -

    Tracy: redirect demo

    - - -For security reasons, Tracy is visible only on localhost. Look into the source code to see how to enable Tracy.

    '; -} diff --git a/tracy-2.5.x/examples/warning.php b/tracy-2.5.x/examples/warning.php deleted file mode 100644 index 64e33ba9..00000000 --- a/tracy-2.5.x/examples/warning.php +++ /dev/null @@ -1,24 +0,0 @@ - - - -

    Tracy Warning and StrictMode demo

    - -For security reasons, Tracy is visible only on localhost. Look into the source code to see how to enable Tracy.

    '; -} diff --git a/tracy-2.5.x/license.md b/tracy-2.5.x/license.md deleted file mode 100644 index 5a566176..00000000 --- a/tracy-2.5.x/license.md +++ /dev/null @@ -1,55 +0,0 @@ -Licenses -======== - -Good news! You may use Tracy under the terms of either the New BSD License -or the GNU General Public License (GPL) version 2 or 3. - -The BSD License is recommended for most projects. It is easy to understand and it -places almost no restrictions on what you can do with the framework. If the GPL -fits better to your project, you can use the framework under this license. - -You don't have to notify anyone which license you are using. You can freely -use Tracy in commercial projects as long as the copyright header -remains intact. - - -New BSD License ---------------- - -Copyright (c) 2004, 2014 David Grudl (https://davidgrudl.com) -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - * Neither the name of "Tracy" nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -This software is provided by the copyright holders and contributors "as is" and -any express or implied warranties, including, but not limited to, the implied -warranties of merchantability and fitness for a particular purpose are -disclaimed. In no event shall the copyright owner or contributors be liable for -any direct, indirect, incidental, special, exemplary, or consequential damages -(including, but not limited to, procurement of substitute goods or services; -loss of use, data, or profits; or business interruption) however caused and on -any theory of liability, whether in contract, strict liability, or tort -(including negligence or otherwise) arising in any way out of the use of this -software, even if advised of the possibility of such damage. - - -GNU General Public License --------------------------- - -GPL licenses are very very long, so instead of including them here we offer -you URLs with full text: - -- [GPL version 2](http://www.gnu.org/licenses/gpl-2.0.html) -- [GPL version 3](http://www.gnu.org/licenses/gpl-3.0.html) diff --git a/tracy-2.5.x/readme.md b/tracy-2.5.x/readme.md deleted file mode 100644 index 6dbf7c2f..00000000 --- a/tracy-2.5.x/readme.md +++ /dev/null @@ -1,354 +0,0 @@ -[Tracy](https://tracy.nette.org) - PHP debugger -============================================== - -[![Downloads this Month](https://img.shields.io/packagist/dm/tracy/tracy.svg)](https://packagist.org/packages/tracy/tracy) -[![Build Status](https://travis-ci.org/nette/tracy.svg?branch=master)](https://travis-ci.org/nette/tracy) -[![Build Status Windows](https://ci.appveyor.com/api/projects/status/github/nette/tracy?branch=master&svg=true)](https://ci.appveyor.com/project/dg/tracy/branch/master) -[![Latest Stable Version](https://poser.pugx.org/tracy/tracy/v/stable)](https://github.com/nette/tracy/releases) -[![License](https://img.shields.io/badge/license-New%20BSD-blue.svg)](https://github.com/nette/tracy/blob/master/license.md) -[![Join the chat at https://gitter.im/nette/tracy](https://badges.gitter.im/nette/tracy.svg)](https://gitter.im/nette/tracy) - - -Introduction ------------- - -Tracy library is a useful helper for everyday PHP programmers. It helps you to: - -- quickly detect and correct errors -- log errors -- dump variables -- measure execution time of scripts/queries -- see memory consumption - - -PHP is a perfect language for making hardly detectable errors because it gives a great flexibility to programmers. Tracy\Debugger is more valuable because of that. It is a ultimate tool among the diagnostic ones. -If you are meeting Tracy the first time, believe me, your life starts to be divided one before the Tracy and the one with her. -Welcome to the good part! - -Documentation can be found on the [website](https://tracy.nette.org). - -If you like Tracy, **[please make a donation now](https://nette.org/make-donation?to=tracy)**. Thank you! - - -Installation ------------- - -The recommended way to is via Composer: - -``` -composer require tracy/tracy -``` - -Alternatively, you can download the whole package or [tracy.phar](https://github.com/nette/tester/releases) file. - -Tracy 2.5 requires PHP version 5.4.4 or newer (supports PHP up to 7.4) and is compatible with Chrome 49+, Firefox 45+, MS Edge 12+, Safari 10+ and iOS Safari 10.2+. - -Tracy 2.4 requires PHP version 5.4.4 or newer (supports PHP up to 7.2) and is compatible with Chrome 29+, Firefox 28+, IE 11+, MS Edge 12+, Safari 9+ and iOS Safari 9.2+. - - -Usage ------ - -Activating Tracy is easy. Simply add these two lines of code, preferably just after library loading (like `require 'vendor/autoload.php'`) and before any output is sent to browser: - -```php -use Tracy\Debugger; - -Debugger::enable(); -``` - -The first thing you will notice on the website is a Debugger Bar. - -(If you do not see anything, it means that Tracy is running in production mode. For security reasons, Tracy is visible only on localhost. -You may force Tracy to run in development mode by passing the `Debugger::DEVELOPMENT` as the first parameter of `enable()` method.) - - -Debugger Bar ------------- - -The Debugger Bar is a floating panel. It is displayed in the bottom right corner of a page. You can move it using the mouse. It will remember its position after the page reloading. - -[![Debugger-Bar](https://nette.github.io/tracy/images/tracy-bar.png)](https://nette.github.io/tracy/tracy-debug-bar.html) - -You can add other useful panels into the Debugger Bar. You can find interesing ones in [Addons](https://addons.nette.org) or you can create your own. - -Implementation of custom panel is easy, just implement interface `Tracy\IBarPanel` with two methods `getTab` and `getPanel`, both returning HTML content to be displayed. -Afterward, registering via `Debugger::getBar()->addPanel(new CustomPanel());` is everything you will need to do. - - -Visualization of errors and exceptions --------------------------------------- - -Surely, you know how PHP reports errors: there is something like this in the page source code: - -```pre -Parse error: syntax error, unexpected '}' in HomepagePresenter.php on line 15 -``` - -or uncaught exception: - -```pre -Fatal error: Uncaught Nette\MemberAccessException: Call to undefined method Nette\Application\UI\Form::addTest()? in /sandbox/vendor/nette/utils/src/Utils/ObjectMixin.php:100 -Stack trace: -#0 /sandbox/vendor/nette/utils/src/Utils/Object.php(75): Nette\Utils\ObjectMixin::call(Object(Nette\Application\UI\Form), 'addTest', Array) -#1 /sandbox/app/forms/SignFormFactory.php(32): Nette\Object->__call('addTest', Array) -#2 /sandbox/app/presenters/SignPresenter.php(21): App\Forms\SignFormFactory->create() -#3 /sandbox/vendor/nette/component-model/src/ComponentModel/Container.php(181): App\Presenters\SignPresenter->createComponentSignInForm('signInForm') -#4 /sandbox/vendor/nette/component-model/src/ComponentModel/Container.php(139): Nette\ComponentModel\Container->createComponent('signInForm') -#5 /sandbox/temp/cache/latte/15206b353f351f6bfca2c36cc.php(17): Nette\ComponentModel\Co in /sandbox/vendor/nette/utils/src/Utils/ObjectMixin.php on line 100
    -``` - -It is not so easy to navigate through this output. If you enable Tracy, both errors and exceptions are displayed in a completely different form: - -[![Uncaught exception rendered by Tracy](https://nette.github.io/tracy/images/tracy-exception.png)](https://nette.github.io/tracy/tracy-exception.html) - -The error message literally screams. You can see a part of the source code with the highlighted line where the error occurred. A message clearly explains an error. The entire site is [interactive, try it](https://nette.github.io/tracy/tracy-exception.html). - -And you know what? Fatal errors are captured and displayed in the same way. No need to install any extension (click for live example): - -[![Fatal error rendered by Tracy](https://nette.github.io/tracy/images/tracy-error.png)](https://nette.github.io/tracy/tracy-error.html) - -Errors like a typo in a variable name or an attempt to open a nonexistent file generate reports of E_NOTICE or E_WARNING level. These can be easily overlooked and/or can be completely hidden in a web page graphic layout. Let Tracy manage them: - -[![Notice rendered by Tracy](https://nette.github.io/tracy/images/tracy-notice2.png)](https://nette.github.io/tracy/tracy-debug-bar.html) - -Or they may be displayed like errors: - -```php -Debugger::$strictMode = true; -``` - -[![Notice rendered by Tracy](https://nette.github.io/tracy/images/tracy-notice.png)](https://nette.github.io/tracy/tracy-notice.html) - - -Content Security Policy ------------------------ - -If your site uses Content Security Policy, you'll need to add `'nonce-'` to `script-src` and eventually the same nonce to `style-src` for Tracy to work properly. Some 3rd plugins may require additional directives. - -Configuration example for [Nette Framework](https://nette.org): - -```neon -http: - csp: - script-src: nonce - style-src: nonce -``` - - -Faster loading --------------- - -The basic integration is straightforward, however if you have slow blocking scripts in web page, they can slow the Tracy loading. -The solution is to place `` into your template before -any scripts: - -```html - - - - ...<title> - <?php Tracy\Debugger::renderLoader() ?> - <link rel="stylesheet" href="assets/style.css"> - <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script> -</head> -``` - - -AJAX and redirected requests ----------------------------- - -Tracy is able to show Debug bar and Bluescreens for AJAX and redirected requests. You just have to start session before Tracy: - -```php -session_start(); -Debugger::enable(); -``` - -In case you use non-standard session handler, you can start Tracy immediately (in order to handle any errors), then initialize your session handler -and then inform Tracy that session is ready to use via `dispatch()`: - -```php -Debugger::enable(); - -// initialize session handler -session_start(); - -Debugger::dispatch(); -``` - - -Production mode and error logging ---------------------------------- - -As you can see, Tracy is quite eloquent. It is appreciated in a development environment, but on a production server it would cause a disaster. Any debugging information cannot be listed there. Therefore Tracy has an environment autodetection and logging functionality. Instead of showing herself, Tracy stores information into a log file and shows the visitor a user-comprehensible server error message: - -[![Server Error 500](https://nette.github.io/tracy/images/tracy-error2.png)](https://nette.github.io/tracy/tracy-production.html) - -Production output mode suppresses all debugging information which is sent out via `dump()` or `Debugger::fireLog()`, and of course all error messages generated by PHP. So, even if you forget `dump($obj)` in the source code, you do not have to worry about it on your production server. Nothing will be seen. - -The output mode is set by the first parameter of `Debugger::enable()`. You can specify either a constant `Debugger::PRODUCTION` or `Debugger::DEVELOPMENT`. - -If it is not specified, the default value `Debugger::DETECT` is used. In this case the system detects a server by IP address. The production mode is chosen if an application is accessed via public IP address. A local IP address leads to development mode. It is not necessary to set the mode in most cases. The mode is correctly recognized when you are launching the application on your local server or in production. - -In the production mode, Tracy automatically captures all errors and exceptions into a text log. Unless you specify otherwise, it will be stored in log/error.log. This error logging is extremely useful. Imagine, that all users of your application are actually betatesters. They are doing cutting-edge work for free when hunting bugs and you would be silly if you threw away their valuable reports to a recycle bin unnoticed. - -If you need to log your own messages or caught exceptions, use the method `log()`: - -```php -Debugger::log('Unexpected error'); // text message - -try { - criticalOperation(); -} catch (Exception $e) { - Debugger::log($e); // log exception - // or - Debugger::log($e, Debugger::ERROR) // also sends an email notification -} -``` - -A directory for errors logging can be set by the second parameter of the enable() method: - -```php -Debugger::enable(Debugger::DETECT, __DIR__ . '/mylog'); -``` - -If you want Tracy to log PHP errors like `E_NOTICE` or `E_WARNING` with detailed information (HTML report), set `Debugger::$logSeverity`: - -```php -Debugger::$logSeverity = E_NOTICE | E_WARNING; -``` - -For a real professional the error log is a crucial source of information and he or she wants to be notified about any new error immediately. Tracy helps him. She is capable of sending an email for every new error record. The variable $email identifies where to send these e-mails: - -```php -Debugger::$email = 'admin@example.com'; -``` - -To protect your e-mail box from flood, Tracy sends **only one message** and creates a file `email-sent`. When a developer receives the e-mail notification, he checks the log, corrects his application and deletes the `email-sent` monitoring file. This activates the e-mail sending again. - - -Variable dumping ------------------ - -Every debugging developer is a good friend with the function `var_dump`, which lists all contents of any variable in detail. Unfortunately, its output is without HTML formatting and outputs the dump into a single line of HTML code, not to mention context escaping. It is necessary to replace the `var_dump` by a handier function. That is just what `dump()` is. - -```php -$arr = array(10, 20.2, true, null, 'hello'); - -dump($arr); -// or Tracy\Debugger::dump($arr); -``` - -generates the output: - -![dump](https://nette.github.io/tracy/images/tracy-dump.png) - -You can also change the nesting depth by `Debugger::$maxDepth` and displayed strings length by `Debugger::$maxLength`. Naturally, lower values accelerate Tracy rendering. - -```php -Debugger::$maxDepth = 2; // default: 3 -Debugger::$maxLength = 50; // default: 150 -``` - -The `dump()` function can display other useful information. `Tracy\Dumper::LOCATION_SOURCE` adds tooltip with path to the file, where the function was called. `Tracy\Dumper::LOCATION_LINK` adds a link to the file. `Tracy\Dumper::LOCATION_CLASS` adds a tooltip to every dumped object containing path to the file, in which the object's class is defined. All these constants can be set in `Debugger::$showLocation` variable before calling the `dump()`. You can set multiple values at once using the `|` operator. - -```php -Debugger::$showLocation = Tracy\Dumper::LOCATION_SOURCE; // Shows path to where the dump() was called -Debugger::$showLocation = Tracy\Dumper::LOCATION_CLASS | Tracy\Dumper::LOCATION_LINK; // Shows both paths to the classes and link to where the dump() was called -Debugger::$showLocation = false; // Hides additional location information -Debugger::$showLocation = true; // Shows all additional location information -``` - -Very handy alternative to `dump()` is `dumpe()` (ie. dump and exit) and `bdump()`. This allows us to dump variables in Debugger Bar. This is useful, because dumps don't mess up the output and we can also add a title to the dump. - -```php -bdump([2, 4, 6, 8], 'even numbers up to ten'); -bdump([1, 3, 5, 7, 9], 'odd numbers up to ten'); -``` - -![bar dump](https://nette.github.io/tracy/images/tracy-bardump.png) - - -Timing ------- - -Another useful tool is the debugger stopwatch with a precision of microseconds: - -```php -Debugger::timer(); - -// sweet dreams my cherrie -sleep(2); - -$elapsed = Debugger::timer(); -// $elapsed = 2 -``` - -Multiple measurements at once can be achieved by an optional parameter. - -```php -Debugger::timer('page-generating'); -// some code - -Debugger::timer('rss-generating'); -// some code - -$rssElapsed = Debugger::timer('rss-generating'); -$pageElapsed = Debugger::timer('page-generating'); -``` - -```php -Debugger::timer(); // runs the timer - -... // some time consuming operation - -echo Debugger::timer(); // elapsed time in seconds -``` - - -FireLogger ----------- - -You cannot always send debugging information to the browser window. This applies to AJAX requests, or generating XML files to output. In such cases, you can send the messages by a separate channel into FireLogger. Error, Notice and Warning levels are sent to FireLogger window automatically. It is also possible to log suppressed exceptions in running application when attention to them is important. - -How to do it? - -Firefox: -- install extension [Firebug](http://getfirebug.com/) and [FireLogger](https://addons.mozilla.org/cs/firefox/addon/firelogger/) -- turn on Firebug (using F12 key), enable tabs Net and Logger (stay on Logger) - -Chrome: -- install extension [FireLogger for Chrome](https://chrome.google.com/webstore/detail/firelogger-for-chrome/hmagilfopmdjkeomnjpchokglfdfjfeh) -- turn on Chrome DevTools (using Ctrl-Shift-I key) and open Console - -Navigate to [demo page](https://examples.nette.org/tracy/) and you will see messages sent from PHP. - -Because Tracy\Debugger communicates with FireLogger via HTTP headers, you must call the logging function before the PHP script sends anything to output. It is also possible to enable output buffering and delay the output. - -```php -use Tracy\Debugger; - -Debugger::fireLog('Hello World'); // send string into FireLogger console - -Debugger::fireLog($_SERVER); // or even arrays and objects - -Debugger::fireLog(new Exception('Test Exception')); // or exceptions -``` - -The result looks like this: - -![FireLogger](https://nette.github.io/tracy/images/tracy-firelogger.png) - -Ports ------------------------------ -This is list of unofficial ports to another frameworks and CMS than Nette: -- [Drupal 7](http://drupal.org/project/traced) -- Laravel framework: [recca0120/laravel-tracy](https://github.com/recca0120/laravel-tracy), [whipsterCZ/laravel-tracy](https://github.com/whipsterCZ/laravel-tracy) -- [OpenCart](https://github.com/BurdaPraha/oc_tracy) -- [ProcessWire CMS/CMF](https://github.com/adrianbj/TracyDebugger) -- [Slim Framework](https://github.com/runcmf/runtracy) -- Symfony framework: [kutny/tracy-bundle](https://github.com/kutny/tracy-bundle), [VasekPurchart/Tracy-Blue-Screen-Bundle](https://github.com/VasekPurchart/Tracy-Blue-Screen-Bundle) -- [Wordpress](https://github.com/ktstudio/WP-Tracy) - -... feel free to be famous, create a port for your favourite platform! diff --git a/tracy-2.5.x/src/Bridges/Nette/Bridge.php b/tracy-2.5.x/src/Bridges/Nette/Bridge.php deleted file mode 100644 index 78faae81..00000000 --- a/tracy-2.5.x/src/Bridges/Nette/Bridge.php +++ /dev/null @@ -1,120 +0,0 @@ -<?php - -/** - * This file is part of the Tracy (https://tracy.nette.org) - * Copyright (c) 2004 David Grudl (https://davidgrudl.com) - */ - -namespace Tracy\Bridges\Nette; - -use Latte; -use Nette; -use Tracy; -use Tracy\BlueScreen; -use Tracy\Helpers; - - -/** - * Bridge for NEON & Latte. - */ -class Bridge -{ - public static function initialize() - { - $blueScreen = Tracy\Debugger::getBlueScreen(); - $blueScreen->addPanel([__CLASS__, 'renderLatteError']); - $blueScreen->addAction([__CLASS__, 'renderLatteUnknownMacro']); - $blueScreen->addAction([__CLASS__, 'renderMemberAccessException']); - $blueScreen->addPanel([__CLASS__, 'renderNeonError']); - } - - - public static function renderLatteError($e) - { - if (!$e instanceof Latte\CompileException) { - return null; - } - return [ - 'tab' => 'Template', - 'panel' => (preg_match('#\n|\?#', $e->sourceName) - ? '' - : '<p>' - . (@is_file($e->sourceName) // @ - may trigger error - ? '<b>File:</b> ' . Helpers::editorLink($e->sourceName, $e->sourceLine) - : '<b>' . htmlspecialchars($e->sourceName . ($e->sourceLine ? ':' . $e->sourceLine : '')) . '</b>') - . '</p>') - . '<pre class=code><div>' - . BlueScreen::highlightLine(htmlspecialchars($e->sourceCode, ENT_IGNORE, 'UTF-8'), $e->sourceLine) - . '</div></pre>', - ]; - } - - - public static function renderLatteUnknownMacro($e) - { - if ( - $e instanceof Latte\CompileException - && @is_file($e->sourceName) // @ - may trigger error - && (preg_match('#Unknown macro (\{\w+)\}, did you mean (\{\w+)\}\?#A', $e->getMessage(), $m) - || preg_match('#Unknown attribute (n:\w+), did you mean (n:\w+)\?#A', $e->getMessage(), $m)) - ) { - return [ - 'link' => Helpers::editorUri($e->sourceName, $e->sourceLine, 'fix', $m[1], $m[2]), - 'label' => 'fix it', - ]; - } - return null; - } - - - public static function renderMemberAccessException($e) - { - if (!$e instanceof Nette\MemberAccessException && !$e instanceof \LogicException) { - return null; - } - $loc = $e instanceof Nette\MemberAccessException ? $e->getTrace()[1] : $e->getTrace()[0]; - if (preg_match('#Cannot (?:read|write to) an undeclared property .+::\$(\w+), did you mean \$(\w+)\?#A', $e->getMessage(), $m)) { - return [ - 'link' => Helpers::editorUri($loc['file'], $loc['line'], 'fix', '->' . $m[1], '->' . $m[2]), - 'label' => 'fix it', - ]; - } elseif (preg_match('#Call to undefined (static )?method .+::(\w+)\(\), did you mean (\w+)\(\)?#A', $e->getMessage(), $m)) { - $operator = $m[1] ? '::' : '->'; - return [ - 'link' => Helpers::editorUri($loc['file'], $loc['line'], 'fix', $operator . $m[2] . '(', $operator . $m[3] . '('), - 'label' => 'fix it', - ]; - } - return null; - } - - - public static function renderNeonError($e) - { - if ( - $e instanceof Nette\Neon\Exception - && preg_match('#line (\d+)#', $e->getMessage(), $m) - && ($trace = Helpers::findTrace($e->getTrace(), 'Nette\Neon\Decoder::decode')) - ) { - return [ - 'tab' => 'NEON', - 'panel' => ($trace2 = Helpers::findTrace($e->getTrace(), 'Nette\DI\Config\Adapters\NeonAdapter::load')) - ? '<p><b>File:</b> ' . Helpers::editorLink($trace2['args'][0], $m[1]) . '</p>' - . self::highlightNeon(file_get_contents($trace2['args'][0]), $m[1]) - : self::highlightNeon($trace['args'][0], (int) $m[1]), - ]; - } - return null; - } - - - private static function highlightNeon($code, $line) - { - $code = htmlspecialchars($code, ENT_IGNORE, 'UTF-8'); - $code = str_replace(' ', "<span class='tracy-dump-whitespace'>·</span>", $code); - $code = str_replace("\t", "<span class='tracy-dump-whitespace'>→ </span>", $code); - return '<pre class=code><div>' - . BlueScreen::highlightLine($code, $line) - . '</div></pre>'; - } -} diff --git a/tracy-2.5.x/src/Bridges/Nette/MailSender.php b/tracy-2.5.x/src/Bridges/Nette/MailSender.php deleted file mode 100644 index 4658d584..00000000 --- a/tracy-2.5.x/src/Bridges/Nette/MailSender.php +++ /dev/null @@ -1,57 +0,0 @@ -<?php - -/** - * This file is part of the Tracy (https://tracy.nette.org) - * Copyright (c) 2004 David Grudl (https://davidgrudl.com) - */ - -namespace Tracy\Bridges\Nette; - -use Nette; -use Tracy; - - -/** - * Tracy logger bridge for Nette Mail. - */ -class MailSender -{ - use Nette\SmartObject; - - /** @var Nette\Mail\IMailer */ - private $mailer; - - /** @var string|null sender of email notifications */ - private $fromEmail; - - - public function __construct(Nette\Mail\IMailer $mailer, $fromEmail = null) - { - $this->mailer = $mailer; - $this->fromEmail = $fromEmail; - } - - - /** - * @param mixed $message - * @param string $email - * @return void - */ - public function send($message, $email) - { - $host = preg_replace('#[^\w.-]+#', '', isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : php_uname('n')); - - $mail = new Nette\Mail\Message; - $mail->setHeader('X-Mailer', 'Tracy'); - if ($this->fromEmail || Nette\Utils\Validators::isEmail("noreply@$host")) { - $mail->setFrom($this->fromEmail ?: "noreply@$host"); - } - foreach (explode(',', $email) as $item) { - $mail->addTo(trim($item)); - } - $mail->setSubject('PHP: An error occurred on the server ' . $host); - $mail->setBody(Tracy\Logger::formatMessage($message) . "\n\nsource: " . Tracy\Helpers::getSource()); - - $this->mailer->send($mail); - } -} diff --git a/tracy-2.5.x/src/Bridges/Nette/TracyExtension.php b/tracy-2.5.x/src/Bridges/Nette/TracyExtension.php deleted file mode 100644 index 35d2ff16..00000000 --- a/tracy-2.5.x/src/Bridges/Nette/TracyExtension.php +++ /dev/null @@ -1,135 +0,0 @@ -<?php - -/** - * This file is part of the Tracy (https://tracy.nette.org) - * Copyright (c) 2004 David Grudl (https://davidgrudl.com) - */ - -namespace Tracy\Bridges\Nette; - -use Nette; -use Tracy; - - -/** - * Tracy extension for Nette DI. - */ -class TracyExtension extends Nette\DI\CompilerExtension -{ - public $defaults = [ - 'email' => null, - 'fromEmail' => null, - 'logSeverity' => null, - 'editor' => null, - 'browser' => null, - 'errorTemplate' => null, - 'strictMode' => null, - 'showBar' => null, - 'maxLen' => null, - 'maxLength' => null, - 'maxDepth' => null, - 'showLocation' => null, - 'scream' => null, - 'bar' => [], // of class name - 'blueScreen' => [], // of callback - 'editorMapping' => [], - 'netteMailer' => true, - ]; - - /** @var bool */ - private $debugMode; - - /** @var bool */ - private $cliMode; - - - public function __construct($debugMode = false, $cliMode = false) - { - $this->debugMode = $debugMode; - $this->cliMode = $cliMode; - } - - - public function loadConfiguration() - { - $this->validateConfig($this->defaults); - $builder = $this->getContainerBuilder(); - - $builder->addDefinition($this->prefix('logger')) - ->setClass('Tracy\ILogger') - ->setFactory('Tracy\Debugger::getLogger'); - - $builder->addDefinition($this->prefix('blueScreen')) - ->setFactory('Tracy\Debugger::getBlueScreen'); - - $builder->addDefinition($this->prefix('bar')) - ->setFactory('Tracy\Debugger::getBar'); - } - - - public function afterCompile(Nette\PhpGenerator\ClassType $class) - { - $initialize = $class->getMethod('initialize'); - $builder = $this->getContainerBuilder(); - $class = method_exists('Nette\DI\Helpers', 'filterArguments') ? 'Nette\DI\Helpers' : 'Nette\DI\Compiler'; - - $options = $this->config; - unset($options['bar'], $options['blueScreen'], $options['netteMailer']); - if (isset($options['logSeverity'])) { - $res = 0; - foreach ((array) $options['logSeverity'] as $level) { - $res |= is_int($level) ? $level : constant($level); - } - $options['logSeverity'] = $res; - } - foreach ($options as $key => $value) { - if ($value !== null) { - $key = ($key === 'fromEmail' ? 'getLogger()->' : '$') . $key; - $initialize->addBody($builder->formatPhp( - 'Tracy\Debugger::' . $key . ' = ?;', - $class::filterArguments([$value]) - )); - } - } - - $logger = $builder->getDefinition($this->prefix('logger')); - if ($logger->getFactory()->getEntity() !== ['Tracy\Debugger', 'getLogger']) { - $initialize->addBody($builder->formatPhp('Tracy\Debugger::setLogger(?);', [$logger])); - } - if ($this->config['netteMailer'] && $builder->getByType('Nette\Mail\IMailer')) { - $initialize->addBody($builder->formatPhp('Tracy\Debugger::getLogger()->mailer = ?;', [ - [new Nette\DI\Statement('Tracy\Bridges\Nette\MailSender', ['fromEmail' => $this->config['fromEmail']]), 'send'], - ])); - } - - if ($this->debugMode) { - foreach ((array) $this->config['bar'] as $item) { - if (is_string($item) && substr($item, 0, 1) === '@') { - $item = new Nette\DI\Statement(['@' . $builder::THIS_CONTAINER, 'getService'], [substr($item, 1)]); - } elseif (is_string($item)) { - $item = new Nette\DI\Statement($item); - } - $initialize->addBody($builder->formatPhp( - '$this->getService(?)->addPanel(?);', - $class::filterArguments([$this->prefix('bar'), $item]) - )); - } - - if (!$this->cliMode && ($name = $builder->getByType('Nette\Http\Session'))) { - $initialize->addBody('$this->getService(?)->start();', [$name]); - $initialize->addBody('Tracy\Debugger::dispatch();'); - } - } - - foreach ((array) $this->config['blueScreen'] as $item) { - $initialize->addBody($builder->formatPhp( - '$this->getService(?)->addPanel(?);', - $class::filterArguments([$this->prefix('blueScreen'), $item]) - )); - } - - if (($dir = Tracy\Debugger::$logDirectory) && !is_writable($dir)) { - throw new Nette\InvalidStateException("Make directory '$dir' writable."); - } - } -} diff --git a/tracy-2.5.x/src/Tracy/Bar.php b/tracy-2.5.x/src/Tracy/Bar.php deleted file mode 100644 index 958fefdd..00000000 --- a/tracy-2.5.x/src/Tracy/Bar.php +++ /dev/null @@ -1,264 +0,0 @@ -<?php - -/** - * This file is part of the Tracy (https://tracy.nette.org) - * Copyright (c) 2004 David Grudl (https://davidgrudl.com) - */ - -namespace Tracy; - - -/** - * Debug Bar. - */ -class Bar -{ - /** @var IBarPanel[] */ - private $panels = []; - - /** @var bool initialized by dispatchAssets() */ - private $useSession = false; - - /** @var string|NULL generated by renderLoader() */ - private $contentId; - - - /** - * Add custom panel. - * @param IBarPanel $panel - * @param string $id - * @return static - */ - public function addPanel(IBarPanel $panel, $id = null) - { - if ($id === null) { - $c = 0; - do { - $id = get_class($panel) . ($c++ ? "-$c" : ''); - } while (isset($this->panels[$id])); - } - $this->panels[$id] = $panel; - return $this; - } - - - /** - * Returns panel with given id - * @param string $id - * @return IBarPanel|null - */ - public function getPanel($id) - { - return isset($this->panels[$id]) ? $this->panels[$id] : null; - } - - - /** - * Renders loading <script> - * @return void - */ - public function renderLoader() - { - if (!$this->useSession) { - throw new \LogicException('Start session before Tracy is enabled.'); - } - $contentId = $this->contentId = $this->contentId ?: substr(md5(uniqid('', true)), 0, 10); - $nonce = Helpers::getNonce(); - $async = true; - require __DIR__ . '/assets/Bar/loader.phtml'; - } - - - /** - * Renders debug bar. - * @return void - */ - public function render() - { - $useSession = $this->useSession && session_status() === PHP_SESSION_ACTIVE; - $redirectQueue = &$_SESSION['_tracy']['redirect']; - - foreach (['bar', 'redirect', 'bluescreen'] as $key) { - $queue = &$_SESSION['_tracy'][$key]; - $queue = array_slice((array) $queue, -10, null, true); - $queue = array_filter($queue, function ($item) { - return isset($item['time']) && $item['time'] > time() - 60; - }); - } - - $rows = []; - - if (Helpers::isAjax()) { - if ($useSession) { - $rows[] = (object) ['type' => 'ajax', 'panels' => $this->renderPanels('-ajax')]; - $contentId = $_SERVER['HTTP_X_TRACY_AJAX'] . '-ajax'; - $_SESSION['_tracy']['bar'][$contentId] = ['content' => self::renderHtmlRows($rows), 'dumps' => Dumper::fetchLiveData(), 'time' => time()]; - } - - } elseif (preg_match('#^Location:#im', implode("\n", headers_list()))) { // redirect - if ($useSession) { - Dumper::fetchLiveData(); - Dumper::$livePrefix = count($redirectQueue) . 'p'; - $redirectQueue[] = [ - 'panels' => $this->renderPanels('-r' . count($redirectQueue)), - 'dumps' => Dumper::fetchLiveData(), - 'time' => time(), - ]; - } - - } elseif (Helpers::isHtmlMode()) { - $rows[] = (object) ['type' => 'main', 'panels' => $this->renderPanels()]; - $dumps = Dumper::fetchLiveData(); - foreach (array_reverse((array) $redirectQueue) as $info) { - $rows[] = (object) ['type' => 'redirect', 'panels' => $info['panels']]; - $dumps += $info['dumps']; - } - $redirectQueue = null; - $content = self::renderHtmlRows($rows); - - if ($this->contentId) { - $_SESSION['_tracy']['bar'][$this->contentId] = ['content' => $content, 'dumps' => $dumps, 'time' => time()]; - } else { - $contentId = substr(md5(uniqid('', true)), 0, 10); - $nonce = Helpers::getNonce(); - $async = false; - require __DIR__ . '/assets/Bar/loader.phtml'; - } - } - } - - - /** - * @return string - */ - private static function renderHtmlRows(array $rows) - { - ob_start(function () {}); - require __DIR__ . '/assets/Bar/panels.phtml'; - require __DIR__ . '/assets/Bar/bar.phtml'; - return Helpers::fixEncoding(ob_get_clean()); - } - - - /** - * @return array - */ - private function renderPanels($suffix = null) - { - set_error_handler(function ($severity, $message, $file, $line) { - if (error_reporting() & $severity) { - throw new \ErrorException($message, 0, $severity, $file, $line); - } - }); - - $obLevel = ob_get_level(); - $panels = []; - - foreach ($this->panels as $id => $panel) { - $idHtml = preg_replace('#[^a-z0-9]+#i', '-', $id) . $suffix; - try { - $tab = (string) $panel->getTab(); - $panelHtml = $tab ? (string) $panel->getPanel() : null; - if ($tab && $panel instanceof \Nette\Diagnostics\IBarPanel) { - $e = new \Exception('Support for Nette\Diagnostics\IBarPanel is deprecated'); - } - - } catch (\Exception $e) { - } catch (\Throwable $e) { - } - if (isset($e)) { - while (ob_get_level() > $obLevel) { // restore ob-level if broken - ob_end_clean(); - } - $idHtml = "error-$idHtml"; - $tab = "Error in $id"; - $panelHtml = "<h1>Error: $id</h1><div class='tracy-inner'>" . nl2br(Helpers::escapeHtml($e)) . '</div>'; - unset($e); - } - $panels[] = (object) ['id' => $idHtml, 'tab' => $tab, 'panel' => $panelHtml]; - } - - restore_error_handler(); - return $panels; - } - - - /** - * Renders debug bar assets. - * @return bool - */ - public function dispatchAssets() - { - $asset = isset($_GET['_tracy_bar']) ? $_GET['_tracy_bar'] : null; - if ($asset === 'js') { - header('Content-Type: application/javascript'); - header('Cache-Control: max-age=864000'); - header_remove('Pragma'); - header_remove('Set-Cookie'); - $this->renderAssets(); - return true; - } - - $this->useSession = session_status() === PHP_SESSION_ACTIVE; - - if ($this->useSession && Helpers::isAjax()) { - header('X-Tracy-Ajax: 1'); // session must be already locked - } - - if ($this->useSession && $asset && preg_match('#^content(-ajax)?\.(\w+)$#', $asset, $m)) { - $session = &$_SESSION['_tracy']['bar'][$m[2] . $m[1]]; - header('Content-Type: application/javascript'); - header('Cache-Control: max-age=60'); - header_remove('Set-Cookie'); - if (!$m[1]) { - $this->renderAssets(); - } - if ($session) { - $method = $m[1] ? 'loadAjax' : 'init'; - echo "Tracy.Debug.$method(", json_encode($session['content']), ', ', json_encode($session['dumps']), ');'; - $session = null; - } - $session = &$_SESSION['_tracy']['bluescreen'][$m[2]]; - if ($session) { - echo 'Tracy.BlueScreen.loadAjax(', json_encode($session['content']), ', ', json_encode($session['dumps']), ');'; - $session = null; - } - return true; - } - - return false; - } - - - private function renderAssets() - { - $css = array_map('file_get_contents', array_merge([ - __DIR__ . '/assets/Bar/bar.css', - __DIR__ . '/assets/Toggle/toggle.css', - __DIR__ . '/assets/Dumper/dumper.css', - __DIR__ . '/assets/BlueScreen/bluescreen.css', - ], Debugger::$customCssFiles)); - - echo -"(function(){ - var el = document.createElement('style'); - el.setAttribute('nonce', document.currentScript.getAttribute('nonce') || document.currentScript.nonce); - el.className='tracy-debug'; - el.textContent=" . json_encode(preg_replace('#\s+#u', ' ', implode($css))) . "; - document.head.appendChild(el);}) -();\n"; - - if(Debugger::$customCssStr) echo "(function(){var el = document.createElement('div'); el.className='tracy-debug'; el.innerHTML='".preg_replace('#\s+#u', ' ', Debugger::$customCssStr)."'; document.head.appendChild(el);})();\n"; - - array_map('readfile', array_merge([ - __DIR__ . '/assets/Bar/bar.js', - __DIR__ . '/assets/Toggle/toggle.js', - __DIR__ . '/assets/Dumper/dumper.js', - __DIR__ . '/assets/BlueScreen/bluescreen.js', - ], Debugger::$customJsFiles)); - - if(Debugger::$customJsStr) echo Debugger::$customJsStr; - - if(Debugger::$customBodyStr) echo "(function(){var el = document.createElement('div'); el.className='tracy-debug'; el.innerHTML='".preg_replace('#\s+#u', ' ', Debugger::$customBodyStr)."'; document.addEventListener('DOMContentLoaded', function() { document.body.appendChild(el);});})();\n"; - } -} diff --git a/tracy-2.5.x/src/Tracy/BlueScreen.php b/tracy-2.5.x/src/Tracy/BlueScreen.php deleted file mode 100644 index 25748046..00000000 --- a/tracy-2.5.x/src/Tracy/BlueScreen.php +++ /dev/null @@ -1,359 +0,0 @@ -<?php - -/** - * This file is part of the Tracy (https://tracy.nette.org) - * Copyright (c) 2004 David Grudl (https://davidgrudl.com) - */ - -namespace Tracy; - - -/** - * Red BlueScreen. - */ -class BlueScreen -{ - /** @var string[] */ - public $info = []; - - /** @var string[] paths to be collapsed in stack trace (e.g. core libraries) */ - public $collapsePaths = []; - - /** @var int */ - public $maxDepth = 3; - - /** @var int */ - public $maxLength = 150; - - /** @var string[] */ - public $keysToHide = ['password', 'passwd', 'pass', 'pwd', 'creditcard', 'credit card', 'cc', 'pin']; - - /** @var callable[] */ - private $panels = []; - - /** @var callable[] functions that returns action for exceptions */ - private $actions = []; - - - public function __construct() - { - $this->collapsePaths[] = preg_match('#(.+/vendor)/tracy/tracy/src/Tracy$#', strtr(__DIR__, '\\', '/'), $m) - ? $m[1] - : __DIR__; - } - - - /** - * Add custom panel. - * @param callable $panel - * @return static - */ - public function addPanel($panel) - { - if (!in_array($panel, $this->panels, true)) { - $this->panels[] = $panel; - } - return $this; - } - - - /** - * Add action. - * @param callable $action - * @return static - */ - public function addAction($action) - { - $this->actions[] = $action; - return $this; - } - - - /** - * Renders blue screen. - * @param \Exception|\Throwable $exception - * @return void - */ - public function render($exception) - { - if (Helpers::isAjax() && session_status() === PHP_SESSION_ACTIVE) { - ob_start(function () {}); - $this->renderTemplate($exception, __DIR__ . '/assets/BlueScreen/content.phtml'); - $contentId = $_SERVER['HTTP_X_TRACY_AJAX']; - $_SESSION['_tracy']['bluescreen'][$contentId] = ['content' => ob_get_clean(), 'dumps' => Dumper::fetchLiveData(), 'time' => time()]; - - } else { - $this->renderTemplate($exception, __DIR__ . '/assets/BlueScreen/page.phtml'); - } - } - - - /** - * Renders blue screen to file (if file exists, it will not be overwritten). - * @param \Exception|\Throwable $exception - * @param string $file file path - * @return void - */ - public function renderToFile($exception, $file) - { - if ($handle = @fopen($file, 'x')) { - ob_start(); // double buffer prevents sending HTTP headers in some PHP - ob_start(function ($buffer) use ($handle) { fwrite($handle, $buffer); }, 4096); - $this->renderTemplate($exception, __DIR__ . '/assets/BlueScreen/page.phtml', false); - ob_end_flush(); - ob_end_clean(); - fclose($handle); - } - } - - - private function renderTemplate($exception, $template, $toScreen = true) - { - $messageHtml = preg_replace( - '#\'\S[^\']*\S\'|"\S[^"]*\S"#U', - '<i>$0</i>', - htmlspecialchars((string) $exception->getMessage(), ENT_SUBSTITUTE, 'UTF-8') - ); - $info = array_filter($this->info); - $source = Helpers::getSource(); - $sourceIsUrl = preg_match('#^https?://#', $source); - $title = $exception instanceof \ErrorException - ? Helpers::errorTypeToString($exception->getSeverity()) - : Helpers::getClass($exception); - $lastError = $exception instanceof \ErrorException || $exception instanceof \Error ? null : error_get_last(); - - $keysToHide = array_flip(array_map('strtolower', $this->keysToHide)); - $dump = function ($v, $k = null) use ($keysToHide) { - if (is_string($k) && isset($keysToHide[strtolower($k)])) { - $v = Dumper::HIDDEN_VALUE; - } - return Dumper::toHtml($v, [ - Dumper::DEPTH => $this->maxDepth, - Dumper::TRUNCATE => $this->maxLength, - Dumper::LIVE => true, - Dumper::LOCATION => Dumper::LOCATION_CLASS, - Dumper::KEYS_TO_HIDE => $this->keysToHide, - ]); - }; - $css = array_map('file_get_contents', array_merge([ - __DIR__ . '/assets/BlueScreen/bluescreen.css', - ], Debugger::$customCssFiles)); - $css = preg_replace('#\s+#u', ' ', implode($css)); - - $nonce = $toScreen ? Helpers::getNonce() : null; - $actions = $toScreen ? $this->renderActions($exception) : []; - - require $template; - } - - - /** - * @return \stdClass[] - */ - private function renderPanels($ex) - { - $obLevel = ob_get_level(); - $res = []; - foreach ($this->panels as $callback) { - try { - $panel = call_user_func($callback, $ex); - if (empty($panel['tab']) || empty($panel['panel'])) { - continue; - } - $res[] = (object) $panel; - continue; - } catch (\Exception $e) { - } catch (\Throwable $e) { - } - while (ob_get_level() > $obLevel) { // restore ob-level if broken - ob_end_clean(); - } - is_callable($callback, true, $name); - $res[] = (object) [ - 'tab' => "Error in panel $name", - 'panel' => nl2br(Helpers::escapeHtml($e)), - ]; - } - return $res; - } - - - /** - * @return array[] - */ - private function renderActions($ex) - { - $actions = []; - foreach ($this->actions as $callback) { - $action = call_user_func($callback, $ex); - if (!empty($action['link']) && !empty($action['label'])) { - $actions[] = $action; - } - } - - if (property_exists($ex, 'tracyAction') && !empty($ex->tracyAction['link']) && !empty($ex->tracyAction['label'])) { - $actions[] = $ex->tracyAction; - } - - if (preg_match('# ([\'"])(\w{3,}(?:\\\\\w{3,})+)\\1#i', $ex->getMessage(), $m)) { - $class = $m[2]; - if ( - !class_exists($class) && !interface_exists($class) && !trait_exists($class) - && ($file = Helpers::guessClassFile($class)) && !is_file($file) - ) { - $actions[] = [ - 'link' => Helpers::editorUri($file, 1, 'create'), - 'label' => 'create class', - ]; - } - } - - if (preg_match('# ([\'"])((?:/|[a-z]:[/\\\\])\w[^\'"]+\.\w{2,5})\\1#i', $ex->getMessage(), $m)) { - $file = $m[2]; - $actions[] = [ - 'link' => Helpers::editorUri($file, 1, $label = is_file($file) ? 'open' : 'create'), - 'label' => $label . ' file', - ]; - } - - $query = ($ex instanceof \ErrorException ? '' : Helpers::getClass($ex) . ' ') - . preg_replace('#\'.*\'|".*"#Us', '', $ex->getMessage()); - $actions[] = [ - 'link' => 'https://www.google.com/search?sourceid=tracy&q=' . urlencode($query), - 'label' => 'search', - 'external' => true, - ]; - - if ( - $ex instanceof \ErrorException - && !empty($ex->skippable) - && preg_match('#^https?://#', $source = Helpers::getSource()) - ) { - $actions[] = [ - 'link' => $source . (strpos($source, '?') ? '&' : '?') . '_tracy_skip_error', - 'label' => 'skip error', - ]; - } - return $actions; - } - - - /** - * Returns syntax highlighted source code. - * @param string $file - * @param int $line - * @param int $lines - * @return string|null - */ - public static function highlightFile($file, $line, $lines = 15, array $vars = null) - { - $source = @file_get_contents($file); // @ file may not exist - if ($source) { - $source = static::highlightPhp($source, $line, $lines, $vars); - if ($editor = Helpers::editorUri($file, $line)) { - $source = substr_replace($source, ' data-tracy-href="' . Helpers::escapeHtml($editor) . '"', 4, 0); - } - return $source; - } - } - - - /** - * Returns syntax highlighted source code. - * @param string $source - * @param int $line - * @param int $lines - * @return string - */ - public static function highlightPhp($source, $line, $lines = 15, array $vars = null) - { - if (function_exists('ini_set')) { - ini_set('highlight.comment', '#998; font-style: italic'); - ini_set('highlight.default', '#000'); - ini_set('highlight.html', '#06B'); - ini_set('highlight.keyword', '#D24; font-weight: bold'); - ini_set('highlight.string', '#080'); - } - - $source = str_replace(["\r\n", "\r"], "\n", $source); - $source = explode("\n", highlight_string($source, true)); - $out = $source[0]; // <code><span color=highlight.html> - $source = str_replace('<br />', "\n", $source[1]); - $out .= static::highlightLine($source, $line, $lines); - - if ($vars) { - $out = preg_replace_callback('#">\$(\w+)( )?</span>#', function ($m) use ($vars) { - return array_key_exists($m[1], $vars) - ? '" title="' - . str_replace('"', '"', trim(strip_tags(Dumper::toHtml($vars[$m[1]], [Dumper::DEPTH => 1])))) - . $m[0] - : $m[0]; - }, $out); - } - - $out = str_replace(' ', ' ', $out); - return "<pre class='code'><div>$out</div></pre>"; - } - - - /** - * Returns highlighted line in HTML code. - * @return string - */ - public static function highlightLine($html, $line, $lines = 15) - { - $source = explode("\n", "\n" . str_replace("\r\n", "\n", $html)); - $out = ''; - $spans = 1; - $start = $i = max(1, min($line, count($source) - 1) - (int) floor($lines * 2 / 3)); - while (--$i >= 1) { // find last highlighted block - if (preg_match('#.*(</?span[^>]*>)#', $source[$i], $m)) { - if ($m[1] !== '</span>') { - $spans++; - $out .= $m[1]; - } - break; - } - } - - $source = array_slice($source, $start, $lines, true); - end($source); - $numWidth = strlen((string) key($source)); - - foreach ($source as $n => $s) { - $spans += substr_count($s, '<span') - substr_count($s, '</span'); - $s = str_replace(["\r", "\n"], ['', ''], $s); - preg_match_all('#<[^>]+>#', $s, $tags); - if ($n == $line) { - $out .= sprintf( - "<span class='highlight'>%{$numWidth}s: %s\n</span>%s", - $n, - strip_tags($s), - implode('', $tags[0]) - ); - } else { - $out .= sprintf("<span class='line'>%{$numWidth}s:</span> %s\n", $n, $s); - } - } - $out .= str_repeat('</span>', $spans) . '</code>'; - return $out; - } - - - /** - * Should a file be collapsed in stack trace? - * @param string $file - * @return bool - */ - public function isCollapsed($file) - { - $file = strtr($file, '\\', '/') . '/'; - foreach ($this->collapsePaths as $path) { - $path = strtr($path, '\\', '/') . '/'; - if (strncmp($file, $path, strlen($path)) === 0) { - return true; - } - } - return false; - } -} diff --git a/tracy-2.5.x/src/Tracy/Debugger.php b/tracy-2.5.x/src/Tracy/Debugger.php deleted file mode 100644 index 9efb3809..00000000 --- a/tracy-2.5.x/src/Tracy/Debugger.php +++ /dev/null @@ -1,659 +0,0 @@ -<?php - -/** - * This file is part of the Tracy (https://tracy.nette.org) - * Copyright (c) 2004 David Grudl (https://davidgrudl.com) - */ - -namespace Tracy; - -use ErrorException; - - -/** - * Debugger: displays and logs errors. - */ -class Debugger -{ - const VERSION = '2.5.9'; - - /** server modes for Debugger::enable() */ - const - DEVELOPMENT = false, - PRODUCTION = true, - DETECT = null; - - const COOKIE_SECRET = 'tracy-debug'; - - /** @var bool in production mode is suppressed any debugging output */ - public static $productionMode = self::DETECT; - - /** @var bool whether to display debug bar in development mode */ - public static $showBar = true; - - /** @var bool whether to send data to FireLogger in development mode */ - public static $showFireLogger = true; - - /** @var bool whether to disable the shutdown handler in development mode */ - public static $disableShutdownHandler = false; - - /** @var bool */ - private static $enabled = false; - - /** @var string|null reserved memory; also prevents double rendering */ - private static $reserved; - - /** @var int initial output buffer level */ - private static $obLevel; - - /********************* errors and exceptions reporting ****************d*g**/ - - /** @var bool|int determines whether any error will cause immediate death in development mode; if integer that it's matched against error severity */ - public static $strictMode = false; - - /** @var bool disables the @ (shut-up) operator so that notices and warnings are no longer hidden */ - public static $scream = false; - - /** @var callable[] functions that are automatically called after fatal error */ - public static $onFatalError = []; - - /********************* Debugger::dump() ****************d*g**/ - - /** @var int how many nested levels of array/object properties display by dump() */ - public static $maxDepth = 3; - - /** @var int how long strings display by dump() */ - public static $maxLength = 150; - - /** @var bool display location by dump()? */ - public static $showLocation = false; - - /** @deprecated */ - public static $maxLen = 150; - - /********************* logging ****************d*g**/ - - /** @var string|null name of the directory where errors should be logged */ - public static $logDirectory; - - /** @var int log bluescreen in production mode for this error severity */ - public static $logSeverity = 0; - - /** @var string|array email(s) to which send error notifications */ - public static $email; - - /** for Debugger::log() and Debugger::fireLog() */ - const - DEBUG = ILogger::DEBUG, - INFO = ILogger::INFO, - WARNING = ILogger::WARNING, - ERROR = ILogger::ERROR, - EXCEPTION = ILogger::EXCEPTION, - CRITICAL = ILogger::CRITICAL; - - /********************* misc ****************d*g**/ - - /** @var int timestamp with microseconds of the start of the request */ - public static $time; - - /** @var string URI pattern mask to open editor */ - public static $editor = 'editor://%action/?file=%file&line=%line&search=%search&replace=%replace'; - - /** @var array replacements in path */ - public static $editorMapping = []; - - /** @var string command to open browser (use 'start ""' in Windows) */ - public static $browser; - - /** @var string custom static error template */ - public static $errorTemplate; - - /** @var array[] */ - public static $customCssFiles = []; - - /** @var array[] */ - public static $customJsFiles = []; - - /** @var string */ - public static $customCssStr = null; - - /** @var string */ - public static $customJsStr = null; - - /** @var string */ - public static $customBodyStr = null; - - /** @var array */ - private static $cpuUsage; - - /********************* services ****************d*g**/ - - /** @var BlueScreen */ - private static $blueScreen; - - /** @var Bar */ - private static $bar; - - /** @var ILogger */ - private static $logger; - - /** @var ILogger */ - private static $fireLogger; - - - /** - * Static class - cannot be instantiated. - */ - final public function __construct() - { - throw new \LogicException; - } - - - /** - * Enables displaying or logging errors and exceptions. - * @param mixed $mode production, development mode, autodetection or IP address(es) whitelist. - * @param string $logDirectory error log directory - * @param string $email administrator email; enables email sending in production mode - * @return void - */ - public static function enable($mode = null, $logDirectory = null, $email = null) - { - if ($mode !== null || self::$productionMode === null) { - self::$productionMode = is_bool($mode) ? $mode : !self::detectDebugMode($mode); - } - - self::$maxLen = &self::$maxLength; - self::$reserved = str_repeat('t', 30000); - self::$time = isset($_SERVER['REQUEST_TIME_FLOAT']) ? $_SERVER['REQUEST_TIME_FLOAT'] : microtime(true); - self::$obLevel = ob_get_level(); - self::$cpuUsage = !self::$productionMode && function_exists('getrusage') ? getrusage() : null; - - // logging configuration - if ($email !== null) { - self::$email = $email; - } - if ($logDirectory !== null) { - self::$logDirectory = $logDirectory; - } - if (self::$logDirectory) { - if (!preg_match('#([a-z]+:)?[/\\\\]#Ai', self::$logDirectory)) { - self::exceptionHandler(new \RuntimeException('Logging directory must be absolute path.')); - self::$logDirectory = null; - } elseif (!is_dir(self::$logDirectory)) { - self::exceptionHandler(new \RuntimeException("Logging directory '" . self::$logDirectory . "' is not found.")); - self::$logDirectory = null; - } - } - - // php configuration - if (function_exists('ini_set')) { - ini_set('display_errors', self::$productionMode ? '0' : '1'); // or 'stderr' - ini_set('html_errors', '0'); - ini_set('log_errors', '0'); - - } elseif ( - ini_get('display_errors') != !self::$productionMode // intentionally == - && ini_get('display_errors') !== (self::$productionMode ? 'stderr' : 'stdout') - ) { - self::exceptionHandler(new \RuntimeException("Unable to set 'display_errors' because function ini_set() is disabled.")); - } - error_reporting(E_ALL); - - if (self::$enabled) { - return; - } - - register_shutdown_function([__CLASS__, 'shutdownHandler']); - set_exception_handler([__CLASS__, 'exceptionHandler']); - set_error_handler([__CLASS__, 'errorHandler']); - - array_map('class_exists', ['Tracy\Bar', 'Tracy\BlueScreen', 'Tracy\DefaultBarPanel', 'Tracy\Dumper', - 'Tracy\FireLogger', 'Tracy\Helpers', 'Tracy\Logger', ]); - - self::dispatch(); - self::$enabled = true; - } - - - /** - * @return void - */ - public static function dispatch() - { - if (self::$productionMode || PHP_SAPI === 'cli') { - return; - - } elseif (headers_sent($file, $line) || ob_get_length()) { - throw new \LogicException( - __METHOD__ . '() called after some output has been sent. ' - . ($file ? "Output started at $file:$line." : 'Try Tracy\OutputDebugger to find where output started.') - ); - - } elseif (self::$enabled && session_status() !== PHP_SESSION_ACTIVE) { - ini_set('session.use_cookies', '1'); - ini_set('session.use_only_cookies', '1'); - ini_set('session.use_trans_sid', '0'); - ini_set('session.cookie_path', '/'); - ini_set('session.cookie_httponly', '1'); - session_start(); - } - - if (self::getBar()->dispatchAssets()) { - exit; - } - } - - - /** - * Renders loading <script> - * @return void - */ - public static function renderLoader() - { - if (!self::$productionMode) { - self::getBar()->renderLoader(); - } - } - - - /** - * @return bool - */ - public static function isEnabled() - { - return self::$enabled; - } - - - /** - * Shutdown handler to catch fatal errors and execute of the planned activities. - * @return void - * @internal - */ - public static function shutdownHandler() - { - if (self::$disableShutdownHandler) { - return false; - } - - if (!self::$reserved) { - return; - } - self::$reserved = null; - - $error = error_get_last(); - if (isset($error['type']) && in_array($error['type'], [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE, E_RECOVERABLE_ERROR, E_USER_ERROR], true)) { - self::exceptionHandler( - Helpers::fixStack(new ErrorException($error['message'], 0, $error['type'], $error['file'], $error['line'])), - false - ); - - } elseif (self::$showBar && !self::$productionMode) { - self::removeOutputBuffers(false); - self::getBar()->render(); - } - } - - - /** - * Handler to catch uncaught exception. - * @param \Exception|\Throwable $exception - * @return void - * @internal - */ - public static function exceptionHandler($exception, $exit = true) - { - if (!self::$reserved && $exit) { - return; - } - self::$reserved = null; - - if (!headers_sent()) { - http_response_code(isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE ') !== false ? 503 : 500); - if (Helpers::isHtmlMode()) { - header('Content-Type: text/html; charset=UTF-8'); - } - } - - Helpers::improveException($exception); - self::removeOutputBuffers(true); - - if (self::$productionMode) { - try { - self::log($exception, self::EXCEPTION); - } catch (\Exception $e) { - } catch (\Throwable $e) { - } - - if (Helpers::isHtmlMode()) { - $logged = empty($e); - require self::$errorTemplate ?: __DIR__ . '/assets/Debugger/error.500.phtml'; - } elseif (PHP_SAPI === 'cli') { - @fwrite(STDERR, 'ERROR: application encountered an error and can not continue. ' - . (isset($e) ? "Unable to log error.\n" : "Error was logged.\n")); // @ triggers E_NOTICE when strerr is closed since PHP 7.4 - } - - } elseif (!connection_aborted() && (Helpers::isHtmlMode() || Helpers::isAjax())) { - self::getBlueScreen()->render($exception); - if (self::$showBar) { - self::getBar()->render(); - } - - } else { - self::fireLog($exception); - $s = get_class($exception) . ($exception->getMessage() === '' ? '' : ': ' . $exception->getMessage()) - . ' in ' . $exception->getFile() . ':' . $exception->getLine() - . "\nStack trace:\n" . $exception->getTraceAsString(); - try { - $file = self::log($exception, self::EXCEPTION); - if ($file && !headers_sent()) { - header("X-Tracy-Error-Log: $file"); - } - echo "$s\n" . ($file ? "(stored in $file)\n" : ''); - if ($file && self::$browser) { - exec(self::$browser . ' ' . escapeshellarg($file)); - } - } catch (\Exception $e) { - echo "$s\nUnable to log error: {$e->getMessage()}\n"; - } catch (\Throwable $e) { - echo "$s\nUnable to log error: {$e->getMessage()}\n"; - } - } - - try { - $e = null; - foreach (self::$onFatalError as $handler) { - call_user_func($handler, $exception); - } - } catch (\Exception $e) { - } catch (\Throwable $e) { - } - if ($e) { - try { - self::log($e, self::EXCEPTION); - } catch (\Exception $e) { - } catch (\Throwable $e) { - } - } - - if ($exit) { - exit(255); - } - } - - - /** - * Handler to catch warnings and notices. - * @return bool|null false to call normal error handler, null otherwise - * @throws ErrorException - * @internal - */ - public static function errorHandler($severity, $message, $file, $line, $context = []) - { - if (self::$scream) { - error_reporting(E_ALL); - } - - if ($severity === E_RECOVERABLE_ERROR || $severity === E_USER_ERROR) { - if (Helpers::findTrace(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), '*::__toString')) { - $previous = isset($context['e']) && ($context['e'] instanceof \Exception || $context['e'] instanceof \Throwable) ? $context['e'] : null; - $e = new ErrorException($message, 0, $severity, $file, $line, $previous); - $e->context = $context; - self::exceptionHandler($e); - } - - $e = new ErrorException($message, 0, $severity, $file, $line); - $e->context = $context; - throw $e; - - } elseif (($severity & error_reporting()) !== $severity) { - return false; // calls normal error handler to fill-in error_get_last() - - } elseif (self::$productionMode && ($severity & self::$logSeverity) === $severity) { - $e = new ErrorException($message, 0, $severity, $file, $line); - $e->context = $context; - Helpers::improveException($e); - try { - self::log($e, self::ERROR); - } catch (\Exception $foo) { - } catch (\Throwable $foo) { - } - return null; - - } elseif ( - !self::$productionMode - && !isset($_GET['_tracy_skip_error']) - && (is_bool(self::$strictMode) ? self::$strictMode : ((self::$strictMode & $severity) === $severity)) - ) { - $e = new ErrorException($message, 0, $severity, $file, $line); - $e->context = $context; - $e->skippable = true; - self::exceptionHandler($e); - } - - $message = 'PHP ' . Helpers::errorTypeToString($severity) . ': ' . Helpers::improveError($message, $context); - $count = &self::getBar()->getPanel('Tracy:errors')->data["$file|$line|$message"]; - - if ($count++) { // repeated error - return null; - - } elseif (self::$productionMode) { - try { - self::log("$message in $file:$line", self::ERROR); - } catch (\Exception $foo) { - } catch (\Throwable $foo) { - } - return null; - - } else { - self::fireLog(new ErrorException($message, 0, $severity, $file, $line)); - return Helpers::isHtmlMode() || Helpers::isAjax() ? null : false; // false calls normal error handler - } - } - - - private static function removeOutputBuffers($errorOccurred) - { - while (ob_get_level() > self::$obLevel) { - $status = ob_get_status(); - if (in_array($status['name'], ['ob_gzhandler', 'zlib output compression'], true)) { - break; - } - $fnc = $status['chunk_size'] || !$errorOccurred ? 'ob_end_flush' : 'ob_end_clean'; - if (!@$fnc()) { // @ may be not removable - break; - } - } - } - - - /********************* services ****************d*g**/ - - - /** - * @return BlueScreen - */ - public static function getBlueScreen() - { - if (!self::$blueScreen) { - self::$blueScreen = new BlueScreen; - self::$blueScreen->info = [ - 'PHP ' . PHP_VERSION, - isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : null, - 'Tracy ' . self::VERSION, - ]; - } - return self::$blueScreen; - } - - - /** - * @return Bar - */ - public static function getBar() - { - if (!self::$bar) { - self::$bar = new Bar; - self::$bar->addPanel($info = new DefaultBarPanel('info'), 'Tracy:info'); - $info->cpuUsage = self::$cpuUsage; - self::$bar->addPanel(new DefaultBarPanel('errors'), 'Tracy:errors'); // filled by errorHandler() - } - return self::$bar; - } - - - /** - * @return void - */ - public static function setLogger(ILogger $logger) - { - self::$logger = $logger; - } - - - /** - * @return ILogger - */ - public static function getLogger() - { - if (!self::$logger) { - self::$logger = new Logger(self::$logDirectory, self::$email, self::getBlueScreen()); - self::$logger->directory = &self::$logDirectory; // back compatiblity - self::$logger->email = &self::$email; - } - return self::$logger; - } - - - /** - * @return ILogger - */ - public static function getFireLogger() - { - if (!self::$fireLogger) { - self::$fireLogger = new FireLogger; - } - return self::$fireLogger; - } - - - /********************* useful tools ****************d*g**/ - - - /** - * Dumps information about a variable in readable format. - * @tracySkipLocation - * @param mixed $var variable to dump - * @param bool $return return output instead of printing it? (bypasses $productionMode) - * @return mixed variable itself or dump - */ - public static function dump($var, $return = false) - { - if ($return) { - ob_start(function () {}); - Dumper::dump($var, [ - Dumper::DEPTH => self::$maxDepth, - Dumper::TRUNCATE => self::$maxLength, - ]); - return ob_get_clean(); - - } elseif (!self::$productionMode) { - Dumper::dump($var, [ - Dumper::DEPTH => self::$maxDepth, - Dumper::TRUNCATE => self::$maxLength, - Dumper::LOCATION => self::$showLocation, - ]); - } - - return $var; - } - - - /** - * Starts/stops stopwatch. - * @param string $name - * @return float elapsed seconds - */ - public static function timer($name = null) - { - static $time = []; - $now = microtime(true); - $delta = isset($time[$name]) ? $now - $time[$name] : 0; - $time[$name] = $now; - return $delta; - } - - - /** - * Dumps information about a variable in Tracy Debug Bar. - * @tracySkipLocation - * @param mixed $var - * @param string $title - * @param array $options - * @return mixed variable itself - */ - public static function barDump($var, $title = null, array $options = null) - { - if (!self::$productionMode) { - static $panel; - if (!$panel) { - self::getBar()->addPanel($panel = new DefaultBarPanel('dumps'), 'Tracy:dumps'); - } - $panel->data[] = ['title' => $title, 'dump' => Dumper::toHtml($var, (array) $options + [ - Dumper::DEPTH => self::$maxDepth, - Dumper::TRUNCATE => self::$maxLength, - Dumper::LOCATION => self::$showLocation ?: Dumper::LOCATION_CLASS | Dumper::LOCATION_SOURCE, - ])]; - } - return $var; - } - - - /** - * Logs message or exception. - * @param mixed $message - * @return mixed - */ - public static function log($message, $priority = ILogger::INFO) - { - return self::getLogger()->log($message, $priority); - } - - - /** - * Sends message to FireLogger console. - * @param mixed $message - * @return bool was successful? - */ - public static function fireLog($message) - { - if (!self::$productionMode && self::$showFireLogger) { - return self::getFireLogger()->log($message); - } - } - - - /** - * Detects debug mode by IP address. - * @param string|array $list IP addresses or computer names whitelist detection - * @return bool - */ - public static function detectDebugMode($list = null) - { - $addr = isset($_SERVER['REMOTE_ADDR']) - ? $_SERVER['REMOTE_ADDR'] - : php_uname('n'); - $secret = isset($_COOKIE[self::COOKIE_SECRET]) && is_string($_COOKIE[self::COOKIE_SECRET]) - ? $_COOKIE[self::COOKIE_SECRET] - : null; - $list = is_string($list) - ? preg_split('#[,\s]+#', $list) - : (array) $list; - if (!isset($_SERVER['HTTP_X_FORWARDED_FOR']) && !isset($_SERVER['HTTP_FORWARDED'])) { - $list[] = '127.0.0.1'; - $list[] = '::1'; - } - return in_array($addr, $list, true) || in_array("$secret@$addr", $list, true); - } -} diff --git a/tracy-2.5.x/src/Tracy/DefaultBarPanel.php b/tracy-2.5.x/src/Tracy/DefaultBarPanel.php deleted file mode 100644 index f34e2b4f..00000000 --- a/tracy-2.5.x/src/Tracy/DefaultBarPanel.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php - -/** - * This file is part of the Tracy (https://tracy.nette.org) - * Copyright (c) 2004 David Grudl (https://davidgrudl.com) - */ - -namespace Tracy; - - -/** - * IBarPanel implementation helper. - * @internal - */ -class DefaultBarPanel implements IBarPanel -{ - public $data; - - private $id; - - - public function __construct($id) - { - $this->id = $id; - } - - - /** - * Renders HTML code for custom tab. - * @return string - */ - public function getTab() - { - ob_start(function () {}); - $data = $this->data; - require __DIR__ . "/assets/Bar/{$this->id}.tab.phtml"; - return ob_get_clean(); - } - - - /** - * Renders HTML code for custom panel. - * @return string - */ - public function getPanel() - { - ob_start(function () {}); - if (is_file(__DIR__ . "/assets/Bar/{$this->id}.panel.phtml")) { - $data = $this->data; - require __DIR__ . "/assets/Bar/{$this->id}.panel.phtml"; - } - return ob_get_clean(); - } -} diff --git a/tracy-2.5.x/src/Tracy/Dumper.php b/tracy-2.5.x/src/Tracy/Dumper.php deleted file mode 100644 index f1fe4f53..00000000 --- a/tracy-2.5.x/src/Tracy/Dumper.php +++ /dev/null @@ -1,595 +0,0 @@ -<?php - -/** - * This file is part of the Tracy (https://tracy.nette.org) - * Copyright (c) 2004 David Grudl (https://davidgrudl.com) - */ - -namespace Tracy; - - -/** - * Dumps a variable. - */ -class Dumper -{ - const - DEPTH = 'depth', // how many nested levels of array/object properties display (defaults to 4) - TRUNCATE = 'truncate', // how truncate long strings? (defaults to 150) - COLLAPSE = 'collapse', // collapse top array/object or how big are collapsed? (defaults to 14) - COLLAPSE_COUNT = 'collapsecount', // how big array/object are collapsed? (defaults to 7) - LOCATION = 'location', // show location string? (defaults to 0) - OBJECT_EXPORTERS = 'exporters', // custom exporters for objects (defaults to Dumper::$objectexporters) - LIVE = 'live', // will be rendered using JavaScript - DEBUGINFO = 'debuginfo', // use magic method __debugInfo if exists (defaults to false) - KEYS_TO_HIDE = 'keystohide'; // sensitive keys not displayed (defaults to []) - - const - LOCATION_SOURCE = 0b0001, // shows where dump was called - LOCATION_LINK = 0b0010, // appends clickable anchor - LOCATION_CLASS = 0b0100; // shows where class is defined - - const - HIDDEN_VALUE = '*****'; - - /** @var array */ - public static $terminalColors = [ - 'bool' => '1;33', - 'null' => '1;33', - 'number' => '1;32', - 'string' => '1;36', - 'array' => '1;31', - 'key' => '1;37', - 'object' => '1;31', - 'visibility' => '1;30', - 'resource' => '1;37', - 'indent' => '1;30', - ]; - - /** @var array */ - public static $resources = [ - 'stream' => 'stream_get_meta_data', - 'stream-context' => 'stream_context_get_options', - 'curl' => 'curl_getinfo', - ]; - - /** @var array */ - public static $objectExporters = [ - 'Closure' => 'Tracy\Dumper::exportClosure', - 'SplFileInfo' => 'Tracy\Dumper::exportSplFileInfo', - 'SplObjectStorage' => 'Tracy\Dumper::exportSplObjectStorage', - '__PHP_Incomplete_Class' => 'Tracy\Dumper::exportPhpIncompleteClass', - ]; - - /** @var string @internal */ - public static $livePrefix; - - /** @var array */ - private static $liveStorage = []; - - - /** - * Dumps variable to the output. - * @return mixed variable - */ - public static function dump($var, array $options = null) - { - if (PHP_SAPI !== 'cli' && !preg_match('#^Content-Type: (?!text/html)#im', implode("\n", headers_list()))) { - echo self::toHtml($var, $options); - } elseif (self::detectColors()) { - echo self::toTerminal($var, $options); - } else { - echo self::toText($var, $options); - } - return $var; - } - - - /** - * Dumps variable to HTML. - * @return string - */ - public static function toHtml($var, array $options = null) - { - $options = (array) $options + [ - self::DEPTH => 4, - self::TRUNCATE => 150, - self::COLLAPSE => 14, - self::COLLAPSE_COUNT => 7, - self::OBJECT_EXPORTERS => null, - self::DEBUGINFO => false, - self::KEYS_TO_HIDE => [], - ]; - $loc = &$options[self::LOCATION]; - $loc = $loc === true ? ~0 : (int) $loc; - - $options[self::KEYS_TO_HIDE] = array_flip(array_map('strtolower', $options[self::KEYS_TO_HIDE])); - $options[self::OBJECT_EXPORTERS] = (array) $options[self::OBJECT_EXPORTERS] + self::$objectExporters; - uksort($options[self::OBJECT_EXPORTERS], function ($a, $b) { - return $b === '' || (class_exists($a, false) && is_subclass_of($a, $b)) ? -1 : 1; - }); - - $live = !empty($options[self::LIVE]) && $var && (is_array($var) || is_object($var) || is_resource($var)); - list($file, $line, $code) = $loc ? self::findLocation() : null; - $locAttrs = $file && $loc & self::LOCATION_SOURCE ? Helpers::formatHtml( - ' title="%in file % on line %" data-tracy-href="%"', "$code\n", $file, $line, Helpers::editorUri($file, $line) - ) : null; - - return '<pre class="tracy-dump' . ($live && $options[self::COLLAPSE] === true ? ' tracy-collapsed' : '') . '"' - . $locAttrs - . ($live ? " data-tracy-dump='" . json_encode(self::toJson($var, $options), JSON_HEX_APOS | JSON_HEX_AMP) . "'>" : '>') - . ($live ? '' : self::dumpVar($var, $options)) - . ($file && $loc & self::LOCATION_LINK ? '<small>in ' . Helpers::editorLink($file, $line) . '</small>' : '') - . "</pre>\n"; - } - - - /** - * Dumps variable to plain text. - * @return string - */ - public static function toText($var, array $options = null) - { - return htmlspecialchars_decode(strip_tags(self::toHtml($var, $options)), ENT_QUOTES); - } - - - /** - * Dumps variable to x-terminal. - * @return string - */ - public static function toTerminal($var, array $options = null) - { - return htmlspecialchars_decode(strip_tags(preg_replace_callback('#<span class="tracy-dump-(\w+)">|</span>#', function ($m) { - return "\033[" . (isset($m[1], self::$terminalColors[$m[1]]) ? self::$terminalColors[$m[1]] : '0') . 'm'; - }, self::toHtml($var, $options))), ENT_QUOTES); - } - - - /** - * Internal toHtml() dump implementation. - * @param mixed $var - * @param array $options - * @param int $level recursion level - * @return string - */ - private static function dumpVar(&$var, array $options, $level = 0) - { - if (method_exists(__CLASS__, $m = 'dump' . gettype($var))) { - return self::$m($var, $options, $level); - } else { - return "<span>unknown type</span>\n"; - } - } - - - private static function dumpNull() - { - return "<span class=\"tracy-dump-null\">null</span>\n"; - } - - - private static function dumpBoolean(&$var) - { - return '<span class="tracy-dump-bool">' . ($var ? 'true' : 'false') . "</span>\n"; - } - - - private static function dumpInteger(&$var) - { - return "<span class=\"tracy-dump-number\">$var</span>\n"; - } - - - private static function dumpDouble(&$var) - { - $var = is_finite($var) - ? ($tmp = json_encode($var)) . (strpos($tmp, '.') === false ? '.0' : '') - : str_replace('.0', '', var_export($var, true)); // workaround for PHP 7.0.2 - return "<span class=\"tracy-dump-number\">$var</span>\n"; - } - - - private static function dumpString(&$var, $options) - { - return '<span class="tracy-dump-string">"' - . Helpers::escapeHtml(self::encodeString($var, $options[self::TRUNCATE])) - . '"</span>' . (strlen($var) > 1 ? ' (' . strlen($var) . ')' : '') . "\n"; - } - - - private static function dumpArray(&$var, $options, $level) - { - static $marker; - if ($marker === null) { - $marker = uniqid("\x00", true); - } - - $out = '<span class="tracy-dump-array">array</span> ('; - - if (empty($var)) { - return $out . ")\n"; - - } elseif (isset($var[$marker])) { - return $out . (count($var) - 1) . ") [ <i>RECURSION</i> ]\n"; - - } elseif (!$options[self::DEPTH] || $level < $options[self::DEPTH]) { - $collapsed = $level ? count($var) >= $options[self::COLLAPSE_COUNT] - : (is_int($options[self::COLLAPSE]) ? count($var) >= $options[self::COLLAPSE] : $options[self::COLLAPSE]); - $out = '<span class="tracy-toggle' . ($collapsed ? ' tracy-collapsed' : '') . '">' - . $out . count($var) . ")</span>\n<div" . ($collapsed ? ' class="tracy-collapsed"' : '') . '>'; - $var[$marker] = true; - foreach ($var as $k => &$v) { - if ($k !== $marker) { - $hide = is_string($k) && isset($options[self::KEYS_TO_HIDE][strtolower($k)]) ? self::HIDDEN_VALUE : null; - $k = is_int($k) || preg_match('#^\w{1,50}\z#', $k) ? $k : '"' . Helpers::escapeHtml(self::encodeString($k, $options[self::TRUNCATE])) . '"'; - $out .= '<span class="tracy-dump-indent"> ' . str_repeat('| ', $level) . '</span>' - . '<span class="tracy-dump-key">' . $k . '</span> => ' - . ($hide ? self::dumpString($hide, $options) : self::dumpVar($v, $options, $level + 1)); - } - } - unset($var[$marker]); - return $out . '</div>'; - - } else { - return $out . count($var) . ") [ ... ]\n"; - } - } - - - private static function dumpObject(&$var, $options, $level) - { - $fields = self::exportObject($var, $options[self::OBJECT_EXPORTERS], $options[self::DEBUGINFO]); - - $editorAttributes = ''; - if ($options[self::LOCATION] & self::LOCATION_CLASS) { - $rc = $var instanceof \Closure ? new \ReflectionFunction($var) : new \ReflectionClass($var); - $editor = Helpers::editorUri($rc->getFileName(), $rc->getStartLine()); - if ($editor) { - $editorAttributes = Helpers::formatHtml( - ' title="Declared in file % on line %" data-tracy-href="%"', - $rc->getFileName(), - $rc->getStartLine(), - $editor - ); - } - } - $out = '<span class="tracy-dump-object"' . $editorAttributes . '>' - . Helpers::escapeHtml(Helpers::getClass($var)) - . '</span> <span class="tracy-dump-hash">#' . substr(md5(spl_object_hash($var)), 0, 4) . '</span>'; - - static $list = []; - - if (empty($fields)) { - return $out . "\n"; - - } elseif (in_array($var, $list, true)) { - return $out . " { <i>RECURSION</i> }\n"; - - } elseif (!$options[self::DEPTH] || $level < $options[self::DEPTH] || $var instanceof \Closure) { - $collapsed = $level ? count($fields) >= $options[self::COLLAPSE_COUNT] - : (is_int($options[self::COLLAPSE]) ? count($fields) >= $options[self::COLLAPSE] : $options[self::COLLAPSE]); - $out = '<span class="tracy-toggle' . ($collapsed ? ' tracy-collapsed' : '') . '">' - . $out . "</span>\n<div" . ($collapsed ? ' class="tracy-collapsed"' : '') . '>'; - $list[] = $var; - foreach ($fields as $k => &$v) { - $vis = ''; - if (isset($k[0]) && $k[0] === "\x00") { - $vis = ' <span class="tracy-dump-visibility">' . ($k[1] === '*' ? 'protected' : 'private') . '</span>'; - $k = substr($k, strrpos($k, "\x00") + 1); - } - $hide = is_string($k) && isset($options[self::KEYS_TO_HIDE][strtolower($k)]) ? self::HIDDEN_VALUE : null; - $k = is_int($k) || preg_match('#^\w{1,50}\z#', $k) ? $k : '"' . Helpers::escapeHtml(self::encodeString($k, $options[self::TRUNCATE])) . '"'; - $out .= '<span class="tracy-dump-indent"> ' . str_repeat('| ', $level) . '</span>' - . '<span class="tracy-dump-key">' . $k . "</span>$vis => " - . ($hide ? self::dumpString($hide, $options) : self::dumpVar($v, $options, $level + 1)); - } - array_pop($list); - return $out . '</div>'; - - } else { - return $out . " { ... }\n"; - } - } - - - private static function dumpResource(&$var, $options, $level) - { - $type = get_resource_type($var); - $out = '<span class="tracy-dump-resource">' . Helpers::escapeHtml($type) . ' resource</span> ' - . '<span class="tracy-dump-hash">#' . (int) $var . '</span>'; - if (isset(self::$resources[$type])) { - $out = "<span class=\"tracy-toggle tracy-collapsed\">$out</span>\n<div class=\"tracy-collapsed\">"; - foreach (call_user_func(self::$resources[$type], $var) as $k => $v) { - $out .= '<span class="tracy-dump-indent"> ' . str_repeat('| ', $level) . '</span>' - . '<span class="tracy-dump-key">' . Helpers::escapeHtml($k) . '</span> => ' . self::dumpVar($v, $options, $level + 1); - } - return $out . '</div>'; - } - return "$out\n"; - } - - - /** - * @return mixed - */ - private static function toJson(&$var, $options, $level = 0) - { - if (is_bool($var) || $var === null || is_int($var)) { - return $var; - - } elseif (is_float($var)) { - return is_finite($var) - ? (strpos($tmp = json_encode($var), '.') ? $var : ['number' => "$tmp.0"]) - : ['type' => (string) $var]; - - } elseif (is_string($var)) { - return self::encodeString($var, $options[self::TRUNCATE]); - - } elseif (is_array($var)) { - static $marker; - if ($marker === null) { - $marker = uniqid("\x00", true); - } - if (isset($var[$marker]) || $level >= $options[self::DEPTH]) { - return [null]; - } - $res = []; - $var[$marker] = true; - foreach ($var as $k => &$v) { - if ($k !== $marker) { - $hide = is_string($k) && isset($options[self::KEYS_TO_HIDE][strtolower($k)]); - $k = is_int($k) || preg_match('#^\w{1,50}\z#', $k) ? $k : '"' . self::encodeString($k, $options[self::TRUNCATE]) . '"'; - $res[] = [$k, $hide ? self::HIDDEN_VALUE : self::toJson($v, $options, $level + 1)]; - } - } - unset($var[$marker]); - return $res; - - } elseif (is_object($var)) { - $obj = &self::$liveStorage[spl_object_hash($var)]; - if ($obj && $obj['level'] <= $level) { - return ['object' => $obj['id']]; - } - - $editorInfo = null; - if ($options[self::LOCATION] & self::LOCATION_CLASS) { - $rc = $var instanceof \Closure ? new \ReflectionFunction($var) : new \ReflectionClass($var); - $editor = Helpers::editorUri($rc->getFileName(), $rc->getStartLine()); - $editorInfo = $editor ? ['file' => $rc->getFileName(), 'line' => $rc->getStartLine(), 'url' => $editor] : null; - } - static $counter = 1; - $obj = $obj ?: [ - 'id' => self::$livePrefix . '0' . $counter++, // differentiate from resources - 'name' => Helpers::getClass($var), - 'editor' => $editorInfo, - 'level' => $level, - 'object' => $var, - ]; - - if ($level < $options[self::DEPTH] || !$options[self::DEPTH]) { - $obj['level'] = $level; - $obj['items'] = []; - - foreach (self::exportObject($var, $options[self::OBJECT_EXPORTERS], $options[self::DEBUGINFO]) as $k => $v) { - $vis = 0; - if (isset($k[0]) && $k[0] === "\x00") { - $vis = $k[1] === '*' ? 1 : 2; - $k = substr($k, strrpos($k, "\x00") + 1); - } - $hide = is_string($k) && isset($options[self::KEYS_TO_HIDE][strtolower($k)]); - $k = is_int($k) || preg_match('#^\w{1,50}\z#', $k) ? $k : '"' . self::encodeString($k, $options[self::TRUNCATE]) . '"'; - $obj['items'][] = [$k, $hide ? self::HIDDEN_VALUE : self::toJson($v, $options, $level + 1), $vis]; - } - } - return ['object' => $obj['id']]; - - } elseif (is_resource($var)) { - $obj = &self::$liveStorage[(string) $var]; - if (!$obj) { - $type = get_resource_type($var); - $obj = ['id' => self::$livePrefix . (int) $var, 'name' => $type . ' resource']; - if (isset(self::$resources[$type])) { - foreach (call_user_func(self::$resources[$type], $var) as $k => $v) { - $obj['items'][] = [$k, self::toJson($v, $options, $level + 1)]; - } - } - } - return ['resource' => $obj['id']]; - - } else { - return ['type' => 'unknown type']; - } - } - - - /** @return array */ - public static function fetchLiveData() - { - $res = []; - foreach (self::$liveStorage as $obj) { - $id = $obj['id']; - unset($obj['level'], $obj['object'], $obj['id']); - $res[$id] = $obj; - } - self::$liveStorage = []; - return $res; - } - - - /** - * @internal - * @return string UTF-8 - */ - public static function encodeString($s, $maxLength = null) - { - static $table; - if ($table === null) { - foreach (array_merge(range("\x00", "\x1F"), range("\x7F", "\xFF")) as $ch) { - $table[$ch] = '\x' . str_pad(dechex(ord($ch)), 2, '0', STR_PAD_LEFT); - } - $table['\\'] = '\\\\'; - $table["\r"] = '\r'; - $table["\n"] = '\n'; - $table["\t"] = '\t'; - } - - if ($maxLength && strlen($s) > $maxLength) { // shortens to $maxLength in UTF-8 or longer - if (function_exists('mb_substr')) { - $s = mb_substr($tmp = $s, 0, $maxLength, 'UTF-8'); - $shortened = $s !== $tmp; - } else { - $i = $len = 0; - $maxI = $maxLength * 4; // max UTF-8 length - do { - if (($s[$i] < "\x80" || $s[$i] >= "\xC0") && (++$len > $maxLength) || $i >= $maxI) { - $s = substr($s, 0, $i); - $shortened = true; - break; - } - } while (isset($s[++$i])); - } - } - - if (preg_match('#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{10FFFF}]#u', $s) || preg_last_error()) { // is binary? - if ($maxLength && strlen($s) > $maxLength) { - $s = substr($s, 0, $maxLength); - $shortened = true; - } - $s = strtr($s, $table); - } - - return $s . (empty($shortened) ? '' : ' ... '); - } - - - /** - * @return array - */ - private static function exportObject($obj, array $exporters, $useDebugInfo) - { - foreach ($exporters as $type => $dumper) { - if (!$type || $obj instanceof $type) { - return call_user_func($dumper, $obj); - } - } - - if ($useDebugInfo && method_exists($obj, '__debugInfo')) { - return $obj->__debugInfo(); - } - - return (array) $obj; - } - - - /** - * @return array - */ - private static function exportClosure(\Closure $obj) - { - $rc = new \ReflectionFunction($obj); - $res = []; - foreach ($rc->getParameters() as $param) { - $res[] = '$' . $param->getName(); - } - return [ - 'file' => $rc->getFileName(), - 'line' => $rc->getStartLine(), - 'variables' => $rc->getStaticVariables(), - 'parameters' => implode(', ', $res), - ]; - } - - - /** - * @return array - */ - private static function exportSplFileInfo(\SplFileInfo $obj) - { - return ['path' => $obj->getPathname()]; - } - - - /** - * @return array - */ - private static function exportSplObjectStorage(\SplObjectStorage $obj) - { - $res = []; - foreach (clone $obj as $item) { - $res[] = ['object' => $item, 'data' => $obj[$item]]; - } - return $res; - } - - - /** - * @return array - */ - private static function exportPhpIncompleteClass(\__PHP_Incomplete_Class $obj) - { - $info = ['className' => null, 'private' => [], 'protected' => [], 'public' => []]; - foreach ((array) $obj as $name => $value) { - if ($name === '__PHP_Incomplete_Class_Name') { - $info['className'] = $value; - } elseif (preg_match('#^\x0\*\x0(.+)\z#', $name, $m)) { - $info['protected'][$m[1]] = $value; - } elseif (preg_match('#^\x0(.+)\x0(.+)\z#', $name, $m)) { - $info['private'][$m[1] . '::$' . $m[2]] = $value; - } else { - $info['public'][$name] = $value; - } - } - return $info; - } - - - /** - * Finds the location where dump was called. - * @return array|null [file, line, code] - */ - private static function findLocation() - { - foreach (debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS) as $item) { - if (isset($item['class']) && $item['class'] === __CLASS__) { - $location = $item; - continue; - } elseif (isset($item['function'])) { - try { - $reflection = isset($item['class']) - ? new \ReflectionMethod($item['class'], $item['function']) - : new \ReflectionFunction($item['function']); - if ($reflection->isInternal() || preg_match('#\s@tracySkipLocation\s#', (string) $reflection->getDocComment())) { - $location = $item; - continue; - } - } catch (\ReflectionException $e) { - } - } - break; - } - - if (isset($location['file'], $location['line']) && is_file($location['file'])) { - $lines = file($location['file']); - $line = $lines[$location['line'] - 1]; - return [ - $location['file'], - $location['line'], - trim(preg_match('#\w*dump(er::\w+)?\(.*\)#i', $line, $m) ? $m[0] : $line), - ]; - } - } - - - /** - * @return bool - */ - private static function detectColors() - { - return self::$terminalColors && - (getenv('ConEmuANSI') === 'ON' - || getenv('ANSICON') !== false - || getenv('term') === 'xterm-256color' - || (defined('STDOUT') && function_exists('posix_isatty') && posix_isatty(STDOUT))); - } -} diff --git a/tracy-2.5.x/src/Tracy/FireLogger.php b/tracy-2.5.x/src/Tracy/FireLogger.php deleted file mode 100644 index 0167bf67..00000000 --- a/tracy-2.5.x/src/Tracy/FireLogger.php +++ /dev/null @@ -1,180 +0,0 @@ -<?php - -/** - * This file is part of the Tracy (https://tracy.nette.org) - * Copyright (c) 2004 David Grudl (https://davidgrudl.com) - */ - -namespace Tracy; - - -/** - * FireLogger console logger. - * - * @see http://firelogger.binaryage.com - * @see https://chrome.google.com/webstore/detail/firelogger-for-chrome/hmagilfopmdjkeomnjpchokglfdfjfeh - */ -class FireLogger implements ILogger -{ - /** @var int */ - public $maxDepth = 3; - - /** @var int */ - public $maxLength = 150; - - /** @var array */ - private $payload = ['logs' => []]; - - - /** - * Sends message to FireLogger console. - * @param mixed $message - * @return bool was successful? - */ - public function log($message, $priority = self::DEBUG) - { - if (!isset($_SERVER['HTTP_X_FIRELOGGER']) || headers_sent()) { - return false; - } - - $item = [ - 'name' => 'PHP', - 'level' => $priority, - 'order' => count($this->payload['logs']), - 'time' => str_pad(number_format((microtime(true) - Debugger::$time) * 1000, 1, '.', ' '), 8, '0', STR_PAD_LEFT) . ' ms', - 'template' => '', - 'message' => '', - 'style' => 'background:#767ab6', - ]; - - $args = func_get_args(); - if (isset($args[0]) && is_string($args[0])) { - $item['template'] = array_shift($args); - } - - if (isset($args[0]) && ($args[0] instanceof \Exception || $args[0] instanceof \Throwable)) { - $e = array_shift($args); - $trace = $e->getTrace(); - if ( - isset($trace[0]['class']) - && $trace[0]['class'] === 'Tracy\Debugger' - && ($trace[0]['function'] === 'shutdownHandler' || $trace[0]['function'] === 'errorHandler') - ) { - unset($trace[0]); - } - - $file = str_replace(dirname(dirname(dirname($e->getFile()))), "\xE2\x80\xA6", $e->getFile()); - $item['template'] = ($e instanceof \ErrorException ? '' : Helpers::getClass($e) . ': ') - . $e->getMessage() . ($e->getCode() ? ' #' . $e->getCode() : '') . ' in ' . $file . ':' . $e->getLine(); - $item['pathname'] = $e->getFile(); - $item['lineno'] = $e->getLine(); - - } else { - $trace = debug_backtrace(); - if ( - isset($trace[1]['class']) - && $trace[1]['class'] === 'Tracy\Debugger' - && ($trace[1]['function'] === 'fireLog') - ) { - unset($trace[0]); - } - - foreach ($trace as $frame) { - if (isset($frame['file']) && is_file($frame['file'])) { - $item['pathname'] = $frame['file']; - $item['lineno'] = $frame['line']; - break; - } - } - } - - $item['exc_info'] = ['', '', []]; - $item['exc_frames'] = []; - - foreach ($trace as $frame) { - $frame += ['file' => null, 'line' => null, 'class' => null, 'type' => null, 'function' => null, 'object' => null, 'args' => null]; - $item['exc_info'][2][] = [$frame['file'], $frame['line'], "$frame[class]$frame[type]$frame[function]", $frame['object']]; - $item['exc_frames'][] = $frame['args']; - } - - if (isset($args[0]) && in_array($args[0], [self::DEBUG, self::INFO, self::WARNING, self::ERROR, self::CRITICAL], true)) { - $item['level'] = array_shift($args); - } - - $item['args'] = $args; - - $this->payload['logs'][] = $this->jsonDump($item, -1); - foreach (str_split(base64_encode(json_encode($this->payload)), 4990) as $k => $v) { - header("FireLogger-de11e-$k:$v"); - } - return true; - } - - - /** - * Dump implementation for JSON. - * @param mixed $var - * @param int $level recursion level - * @return array|null|int|float|bool|string - */ - private function jsonDump(&$var, $level = 0) - { - if (is_bool($var) || $var === null || is_int($var) || is_float($var)) { - return $var; - - } elseif (is_string($var)) { - return Dumper::encodeString($var, $this->maxLength); - - } elseif (is_array($var)) { - static $marker; - if ($marker === null) { - $marker = uniqid("\x00", true); - } - if (isset($var[$marker])) { - return "\xE2\x80\xA6RECURSION\xE2\x80\xA6"; - - } elseif ($level < $this->maxDepth || !$this->maxDepth) { - $var[$marker] = true; - $res = []; - foreach ($var as $k => &$v) { - if ($k !== $marker) { - $res[$this->jsonDump($k)] = $this->jsonDump($v, $level + 1); - } - } - unset($var[$marker]); - return $res; - - } else { - return " \xE2\x80\xA6 "; - } - - } elseif (is_object($var)) { - $arr = (array) $var; - static $list = []; - if (in_array($var, $list, true)) { - return "\xE2\x80\xA6RECURSION\xE2\x80\xA6"; - - } elseif ($level < $this->maxDepth || !$this->maxDepth) { - $list[] = $var; - $res = ["\x00" => '(object) ' . Helpers::getClass($var)]; - foreach ($arr as $k => &$v) { - if (isset($k[0]) && $k[0] === "\x00") { - $k = substr($k, strrpos($k, "\x00") + 1); - } - $res[$this->jsonDump($k)] = $this->jsonDump($v, $level + 1); - } - array_pop($list); - return $res; - - } else { - return " \xE2\x80\xA6 "; - } - - } elseif (is_resource($var)) { - return 'resource ' . get_resource_type($var); - - } else { - return 'unknown type'; - } - } -} diff --git a/tracy-2.5.x/src/Tracy/Helpers.php b/tracy-2.5.x/src/Tracy/Helpers.php deleted file mode 100644 index 2dfc9c37..00000000 --- a/tracy-2.5.x/src/Tracy/Helpers.php +++ /dev/null @@ -1,307 +0,0 @@ -<?php - -/** - * This file is part of the Tracy (https://tracy.nette.org) - * Copyright (c) 2004 David Grudl (https://davidgrudl.com) - */ - -namespace Tracy; - - -/** - * Rendering helpers for Debugger. - */ -class Helpers -{ - - /** - * Returns HTML link to editor. - * @return string - */ - public static function editorLink($file, $line = null) - { - $file = strtr($origFile = $file, Debugger::$editorMapping); - if ($editor = self::editorUri($origFile, $line)) { - $file = strtr($file, '\\', '/'); - if (preg_match('#(^[a-z]:)?/.{1,40}$#i', $file, $m) && strlen($file) > strlen($m[0])) { - $file = '...' . $m[0]; - } - $file = strtr($file, '/', DIRECTORY_SEPARATOR); - return self::formatHtml('<a href="%" title="%">%<b>%</b>%</a>', - $editor, - $origFile . ($line ? ":$line" : ''), - rtrim(dirname($file), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR, - basename($file), - $line ? ":$line" : '' - ); - } else { - return self::formatHtml('<span>%</span>', $file . ($line ? ":$line" : '')); - } - } - - - /** - * Returns link to editor. - * @return string|null - */ - public static function editorUri($file, $line = null, $action = 'open', $search = null, $replace = null) - { - if (Debugger::$editor && $file && ($action === 'create' || is_file($file))) { - $file = strtr($file, '/', DIRECTORY_SEPARATOR); - $file = strtr($file, Debugger::$editorMapping); - return strtr(Debugger::$editor, [ - '%action' => $action, - '%file' => rawurlencode($file), - '%line' => $line ? (int) $line : 1, - '%search' => rawurlencode($search), - '%replace' => rawurlencode($replace), - ]); - } - } - - - public static function formatHtml($mask) - { - $args = func_get_args(); - return preg_replace_callback('#%#', function () use (&$args, &$count) { - return self::escapeHtml($args[++$count]); - }, $mask); - } - - - public static function escapeHtml($s) - { - return htmlspecialchars((string) $s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); - } - - - public static function findTrace(array $trace, $method, &$index = null) - { - $m = explode('::', $method); - foreach ($trace as $i => $item) { - if ( - isset($item['function']) - && $item['function'] === end($m) - && isset($item['class']) === isset($m[1]) - && (!isset($item['class']) || $m[0] === '*' || is_a($item['class'], $m[0], true)) - ) { - $index = $i; - return $item; - } - } - } - - - /** - * @return string - */ - public static function getClass($obj) - { - return explode("\x00", get_class($obj))[0]; - } - - - /** @internal */ - public static function fixStack($exception) - { - if (function_exists('xdebug_get_function_stack')) { - $stack = []; - foreach (array_slice(array_reverse(xdebug_get_function_stack()), 2, -1) as $row) { - $frame = [ - 'file' => $row['file'], - 'line' => $row['line'], - 'function' => isset($row['function']) ? $row['function'] : '*unknown*', - 'args' => [], - ]; - if (!empty($row['class'])) { - $frame['type'] = isset($row['type']) && $row['type'] === 'dynamic' ? '->' : '::'; - $frame['class'] = $row['class']; - } - $stack[] = $frame; - } - $ref = new \ReflectionProperty('Exception', 'trace'); - $ref->setAccessible(true); - $ref->setValue($exception, $stack); - } - return $exception; - } - - - /** @internal */ - public static function fixEncoding($s) - { - return htmlspecialchars_decode(htmlspecialchars($s, ENT_NOQUOTES | ENT_IGNORE, 'UTF-8'), ENT_NOQUOTES); - } - - - /** @internal */ - public static function errorTypeToString($type) - { - $types = [ - E_ERROR => 'Fatal Error', - E_USER_ERROR => 'User Error', - E_RECOVERABLE_ERROR => 'Recoverable Error', - E_CORE_ERROR => 'Core Error', - E_COMPILE_ERROR => 'Compile Error', - E_PARSE => 'Parse Error', - E_WARNING => 'Warning', - E_CORE_WARNING => 'Core Warning', - E_COMPILE_WARNING => 'Compile Warning', - E_USER_WARNING => 'User Warning', - E_NOTICE => 'Notice', - E_USER_NOTICE => 'User Notice', - E_STRICT => 'Strict standards', - E_DEPRECATED => 'Deprecated', - E_USER_DEPRECATED => 'User Deprecated', - ]; - return isset($types[$type]) ? $types[$type] : 'Unknown error'; - } - - - /** @internal */ - public static function getSource() - { - if (isset($_SERVER['REQUEST_URI'])) { - return (!empty($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'off') ? 'https://' : 'http://') - . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '') - . $_SERVER['REQUEST_URI']; - } else { - return 'CLI (PID: ' . getmypid() . ')' - . (empty($_SERVER['argv']) ? '' : ': ' . implode(' ', $_SERVER['argv'])); - } - } - - - /** @internal */ - public static function improveException($e) - { - $message = $e->getMessage(); - if (!$e instanceof \Error && !$e instanceof \ErrorException) { - // do nothing - } elseif (preg_match('#^Call to undefined function (\S+\\\\)?(\w+)\(#', $message, $m)) { - $funcs = array_merge(get_defined_functions()['internal'], get_defined_functions()['user']); - $hint = self::getSuggestion($funcs, $m[1] . $m[2]) ?: self::getSuggestion($funcs, $m[2]); - $message = "Call to undefined function $m[2](), did you mean $hint()?"; - $replace = ["$m[2](", "$hint("]; - - } elseif (preg_match('#^Call to undefined method ([\w\\\\]+)::(\w+)#', $message, $m)) { - $hint = self::getSuggestion(get_class_methods($m[1]) ?: [], $m[2]); - $message .= ", did you mean $hint()?"; - $replace = ["$m[2](", "$hint("]; - - } elseif (preg_match('#^Undefined variable: (\w+)#', $message, $m) && !empty($e->context)) { - $hint = self::getSuggestion(array_keys($e->context), $m[1]); - $message = "Undefined variable $$m[1], did you mean $$hint?"; - $replace = ["$$m[1]", "$$hint"]; - - } elseif (preg_match('#^Undefined property: ([\w\\\\]+)::\$(\w+)#', $message, $m)) { - $rc = new \ReflectionClass($m[1]); - $items = array_diff($rc->getProperties(\ReflectionProperty::IS_PUBLIC), $rc->getProperties(\ReflectionProperty::IS_STATIC)); - $hint = self::getSuggestion($items, $m[2]); - $message .= ", did you mean $$hint?"; - $replace = ["->$m[2]", "->$hint"]; - - } elseif (preg_match('#^Access to undeclared static property: ([\w\\\\]+)::\$(\w+)#', $message, $m)) { - $rc = new \ReflectionClass($m[1]); - $items = array_intersect($rc->getProperties(\ReflectionProperty::IS_PUBLIC), $rc->getProperties(\ReflectionProperty::IS_STATIC)); - $hint = self::getSuggestion($items, $m[2]); - $message .= ", did you mean $$hint?"; - $replace = ["::$$m[2]", "::$$hint"]; - } - - if (isset($hint)) { - $ref = new \ReflectionProperty($e, 'message'); - $ref->setAccessible(true); - $ref->setValue($e, $message); - $e->tracyAction = [ - 'link' => self::editorUri($e->getFile(), $e->getLine(), 'fix', $replace[0], $replace[1]), - 'label' => 'fix it', - ]; - } - } - - - /** @internal */ - public static function improveError($message, array $context = []) - { - if (preg_match('#^Undefined variable: (\w+)#', $message, $m) && $context) { - $hint = self::getSuggestion(array_keys($context), $m[1]); - return $hint ? "Undefined variable $$m[1], did you mean $$hint?" : $message; - - } elseif (preg_match('#^Undefined property: ([\w\\\\]+)::\$(\w+)#', $message, $m)) { - $rc = new \ReflectionClass($m[1]); - $items = array_diff($rc->getProperties(\ReflectionProperty::IS_PUBLIC), $rc->getProperties(\ReflectionProperty::IS_STATIC)); - $hint = self::getSuggestion($items, $m[2]); - return $hint ? $message . ", did you mean $$hint?" : $message; - } - return $message; - } - - - /** @internal */ - public static function guessClassFile($class) - { - $segments = explode(DIRECTORY_SEPARATOR, $class); - $res = null; - $max = 0; - foreach (get_declared_classes() as $class) { - $parts = explode(DIRECTORY_SEPARATOR, $class); - foreach ($parts as $i => $part) { - if (!isset($segments[$i]) || $part !== $segments[$i]) { - break; - } - } - if ($i > $max && ($file = (new \ReflectionClass($class))->getFileName())) { - $max = $i; - $res = array_merge(array_slice(explode(DIRECTORY_SEPARATOR, $file), 0, $i - count($parts)), array_slice($segments, $i)); - $res = implode(DIRECTORY_SEPARATOR, $res) . '.php'; - } - } - return $res; - } - - - /** - * Finds the best suggestion. - * @return string|null - * @internal - */ - public static function getSuggestion(array $items, $value) - { - $best = null; - $min = (strlen($value) / 4 + 1) * 10 + .1; - foreach (array_unique($items, SORT_REGULAR) as $item) { - $item = is_object($item) ? $item->getName() : $item; - if (($len = levenshtein($item, $value, 10, 11, 10)) > 0 && $len < $min) { - $min = $len; - $best = $item; - } - } - return $best; - } - - - /** @internal */ - public static function isHtmlMode() - { - return empty($_SERVER['HTTP_X_REQUESTED_WITH']) && empty($_SERVER['HTTP_X_TRACY_AJAX']) - && PHP_SAPI !== 'cli' - && !preg_match('#^Content-Type: (?!text/html)#im', implode("\n", headers_list())); - } - - - /** @internal */ - public static function isAjax() - { - return isset($_SERVER['HTTP_X_TRACY_AJAX']) && preg_match('#^\w{10}\z#', $_SERVER['HTTP_X_TRACY_AJAX']); - } - - - /** @internal */ - public static function getNonce() - { - return preg_match('#^Content-Security-Policy(?:-Report-Only)?:.*\sscript-src\s+(?:[^;]+\s)?\'nonce-([\w+/]+=*)\'#mi', implode("\n", headers_list()), $m) - ? $m[1] - : null; - } -} diff --git a/tracy-2.5.x/src/Tracy/IBarPanel.php b/tracy-2.5.x/src/Tracy/IBarPanel.php deleted file mode 100644 index 49b6f77e..00000000 --- a/tracy-2.5.x/src/Tracy/IBarPanel.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php - -/** - * This file is part of the Tracy (https://tracy.nette.org) - * Copyright (c) 2004 David Grudl (https://davidgrudl.com) - */ - -namespace Tracy; - - -/** - * Custom output for Debugger. - */ -interface IBarPanel -{ - - /** - * Renders HTML code for custom tab. - * @return string - */ - function getTab(); - - /** - * Renders HTML code for custom panel. - * @return string - */ - function getPanel(); -} diff --git a/tracy-2.5.x/src/Tracy/ILogger.php b/tracy-2.5.x/src/Tracy/ILogger.php deleted file mode 100644 index 873be928..00000000 --- a/tracy-2.5.x/src/Tracy/ILogger.php +++ /dev/null @@ -1,25 +0,0 @@ -<?php - -/** - * This file is part of the Tracy (https://tracy.nette.org) - * Copyright (c) 2004 David Grudl (https://davidgrudl.com) - */ - -namespace Tracy; - - -/** - * Logger. - */ -interface ILogger -{ - const - DEBUG = 'debug', - INFO = 'info', - WARNING = 'warning', - ERROR = 'error', - EXCEPTION = 'exception', - CRITICAL = 'critical'; - - function log($value, $priority = self::INFO); -} diff --git a/tracy-2.5.x/src/Tracy/Logger.php b/tracy-2.5.x/src/Tracy/Logger.php deleted file mode 100644 index 0f85a052..00000000 --- a/tracy-2.5.x/src/Tracy/Logger.php +++ /dev/null @@ -1,209 +0,0 @@ -<?php - -/** - * This file is part of the Tracy (https://tracy.nette.org) - * Copyright (c) 2004 David Grudl (https://davidgrudl.com) - */ - -namespace Tracy; - - -/** - * Logger. - */ -class Logger implements ILogger -{ - /** @var string|null name of the directory where errors should be logged */ - public $directory; - - /** @var string|array|null email or emails to which send error notifications */ - public $email; - - /** @var string|null sender of email notifications */ - public $fromEmail; - - /** @var mixed interval for sending email is 2 days */ - public $emailSnooze = '2 days'; - - /** @var callable handler for sending emails */ - public $mailer; - - /** @var BlueScreen|null */ - private $blueScreen; - - - /** - * @param string|null $directory - * @param string|array|null $email - */ - public function __construct($directory, $email = null, BlueScreen $blueScreen = null) - { - $this->directory = $directory; - $this->email = $email; - $this->blueScreen = $blueScreen; - $this->mailer = [$this, 'defaultMailer']; - } - - - /** - * Logs message or exception to file and sends email notification. - * @param mixed $message - * @param string $priority one of constant ILogger::INFO, WARNING, ERROR (sends email), EXCEPTION (sends email), CRITICAL (sends email) - * @return string|null logged error filename - */ - public function log($message, $priority = self::INFO) - { - if (!$this->directory) { - throw new \LogicException('Logging directory is not specified.'); - } elseif (!is_dir($this->directory)) { - throw new \RuntimeException("Logging directory '$this->directory' is not found or is not directory."); - } - - $exceptionFile = $message instanceof \Exception || $message instanceof \Throwable - ? $this->getExceptionFile($message) - : null; - $line = static::formatLogLine($message, $exceptionFile); - $file = $this->directory . '/' . strtolower($priority ?: self::INFO) . '.log'; - - if (!@file_put_contents($file, $line . PHP_EOL, FILE_APPEND | LOCK_EX)) { // @ is escalated to exception - throw new \RuntimeException("Unable to write to log file '$file'. Is directory writable?"); - } - - if ($exceptionFile) { - $this->logException($message, $exceptionFile); - } - - if (in_array($priority, [self::ERROR, self::EXCEPTION, self::CRITICAL], true)) { - $this->sendEmail($message); - } - - return $exceptionFile; - } - - - /** - * @param mixed $message - * @return string - */ - public static function formatMessage($message) - { - if ($message instanceof \Exception || $message instanceof \Throwable) { - while ($message) { - $tmp[] = ($message instanceof \ErrorException - ? Helpers::errorTypeToString($message->getSeverity()) . ': ' . $message->getMessage() - : Helpers::getClass($message) . ': ' . $message->getMessage() . ($message->getCode() ? ' #' . $message->getCode() : '') - ) . ' in ' . $message->getFile() . ':' . $message->getLine(); - $message = $message->getPrevious(); - } - $message = implode("\ncaused by ", $tmp); - - } elseif (!is_string($message)) { - $message = Dumper::toText($message); - } - - return trim($message); - } - - - /** - * @param mixed $message - * @return string - */ - public static function formatLogLine($message, $exceptionFile = null) - { - return implode(' ', [ - @date('[Y-m-d H-i-s]'), // @ timezone may not be set - preg_replace('#\s*\r?\n\s*#', ' ', static::formatMessage($message)), - ' @ ' . Helpers::getSource(), - $exceptionFile ? ' @@ ' . basename($exceptionFile) : null, - ]); - } - - - /** - * @param \Exception|\Throwable $exception - * @return string - */ - public function getExceptionFile($exception) - { - while ($exception) { - $data[] = [ - get_class($exception), $exception->getMessage(), $exception->getCode(), $exception->getFile(), $exception->getLine(), - array_map(function ($item) { unset($item['args']); return $item; }, $exception->getTrace()), - ]; - $exception = $exception->getPrevious(); - } - $hash = substr(md5(serialize($data)), 0, 10); - $dir = strtr($this->directory . '/', '\\/', DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR); - foreach (new \DirectoryIterator($this->directory) as $file) { - if (strpos($file->getBasename(), $hash)) { - return $dir . $file; - } - } - return $dir . 'exception--' . @date('Y-m-d--H-i') . "--$hash.html"; // @ timezone may not be set - } - - - /** - * Logs exception to the file if file doesn't exist. - * @param \Exception|\Throwable $exception - * @return string logged error filename - */ - protected function logException($exception, $file = null) - { - $file = $file ?: $this->getExceptionFile($exception); - $bs = $this->blueScreen ?: new BlueScreen; - $bs->renderToFile($exception, $file); - return $file; - } - - - /** - * @param mixed $message - * @return void - */ - protected function sendEmail($message) - { - $snooze = is_numeric($this->emailSnooze) - ? $this->emailSnooze - : @strtotime($this->emailSnooze) - time(); // @ timezone may not be set - - if ( - $this->email - && $this->mailer - && @filemtime($this->directory . '/email-sent') + $snooze < time() // @ file may not exist - && @file_put_contents($this->directory . '/email-sent', 'sent') // @ file may not be writable - ) { - call_user_func($this->mailer, $message, implode(', ', (array) $this->email)); - } - } - - - /** - * Default mailer. - * @param mixed $message - * @param string $email - * @return void - * @internal - */ - public function defaultMailer($message, $email) - { - $host = preg_replace('#[^\w.-]+#', '', isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : php_uname('n')); - $parts = str_replace( - ["\r\n", "\n"], - ["\n", PHP_EOL], - [ - 'headers' => implode("\n", [ - 'From: ' . ($this->fromEmail ?: "noreply@$host"), - 'X-Mailer: Tracy', - 'Content-Type: text/plain; charset=UTF-8', - 'Content-Transfer-Encoding: 8bit', - ]) . "\n", - 'subject' => "PHP: An error occurred on the server $host", - 'body' => static::formatMessage($message) . "\n\nsource: " . Helpers::getSource(), - ] - ); - - mail($email, $parts['subject'], $parts['body'], $parts['headers']); - } -} diff --git a/tracy-2.5.x/src/Tracy/OutputDebugger.php b/tracy-2.5.x/src/Tracy/OutputDebugger.php deleted file mode 100644 index 2e9c599c..00000000 --- a/tracy-2.5.x/src/Tracy/OutputDebugger.php +++ /dev/null @@ -1,78 +0,0 @@ -<?php - -/** - * This file is part of the Tracy (https://tracy.nette.org) - * Copyright (c) 2004 David Grudl (https://davidgrudl.com) - */ - -namespace Tracy; - - -/** - * Debugger for outputs. - */ -class OutputDebugger -{ - const BOM = "\xEF\xBB\xBF"; - - /** @var array of [file, line, output, stack] */ - private $list = []; - - - public static function enable() - { - $me = new static; - $me->start(); - } - - - public function start() - { - foreach (get_included_files() as $file) { - if (fread(fopen($file, 'r'), 3) === self::BOM) { - $this->list[] = [$file, 1, self::BOM]; - } - } - ob_start([$this, 'handler'], 1); - } - - - /** @internal */ - public function handler($s, $phase) - { - $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); - if (isset($trace[0]['file'], $trace[0]['line'])) { - $stack = $trace; - unset($stack[0]['line'], $stack[0]['args']); - $i = count($this->list); - if ($i && $this->list[$i - 1][3] === $stack) { - $this->list[$i - 1][2] .= $s; - } else { - $this->list[] = [$trace[0]['file'], $trace[0]['line'], $s, $stack]; - } - } - if ($phase === PHP_OUTPUT_HANDLER_FINAL) { - return $this->renderHtml(); - } - } - - - private function renderHtml() - { - $res = '<style>code, pre {white-space:nowrap} a {text-decoration:none} pre {color:gray;display:inline} big {color:red}</style><code>'; - foreach ($this->list as $item) { - $stack = []; - foreach (array_slice($item[3], 1) as $t) { - $t += ['class' => '', 'type' => '', 'function' => '']; - $stack[] = "$t[class]$t[type]$t[function]()" - . (isset($t['file'], $t['line']) ? ' in ' . basename($t['file']) . ":$t[line]" : ''); - } - - $res .= '<span title="' . Helpers::escapeHtml(implode("\n", $stack)) . '">' - . Helpers::editorLink($item[0], $item[1]) . ' ' - . str_replace(self::BOM, '<big>BOM</big>', Dumper::toHtml($item[2])) - . "</span><br>\n"; - } - return $res . '</code>'; - } -} diff --git a/tracy-2.5.x/src/Tracy/assets/Bar/bar.css b/tracy-2.5.x/src/Tracy/assets/Bar/bar.css deleted file mode 100644 index 1df4abc3..00000000 --- a/tracy-2.5.x/src/Tracy/assets/Bar/bar.css +++ /dev/null @@ -1,374 +0,0 @@ -/** - * This file is part of the Tracy (https://tracy.nette.org) - */ - -/* common styles */ -#tracy-debug { - display: none; - direction: ltr; - line-height: 1.5; -} - -body#tracy-debug { /* in popup window */ - margin: 0; - display: block; -} - -#tracy-debug:not(body) { - position: absolute; - left: 0; - top: 0; -} - -#tracy-debug-bar * { - font: inherit; - line-height: inherit; - color: inherit; - background: transparent; - margin: 0; - padding: 0; - border: none; - text-align: inherit; - list-style: inherit; - opacity: 1; - border-radius: 0; - box-shadow: none; - text-shadow: none; - box-sizing: border-box; - text-decoration: none; - text-transform: inherit; - white-space: inherit; - float: none; - clear: none; -} - -#tracy-debug-bar *:before, -#tracy-debug-bar *:after { - all: unset; -} - -#tracy-debug b, -#tracy-debug strong { - font-weight: bold; -} - -#tracy-debug small { - font-size: smaller; -} - -#tracy-debug i, -#tracy-debug em { - font-style: italic; -} - -#tracy-debug a { - color: #125EAE; - text-decoration: none; -} - -#tracy-debug a:hover, -#tracy-debug a:focus { - background-color: #125EAE; - color: white; -} - -#tracy-debug h2, -#tracy-debug h3, -#tracy-debug p { - margin: .4em 0; -} - -#tracy-debug table { - border-collapse: collapse; - background: #FDF5CE; - width: 100%; -} - -#tracy-debug tr:nth-child(2n) td { - background: #F7F0CB; -} - -#tracy-debug td, -#tracy-debug th { - border: 1px solid #E6DFBF; - padding: 2px 5px; - vertical-align: top; - text-align: left; -} - -#tracy-debug th { - background: #F4F3F1; - color: #655E5E; - font-size: 90%; - font-weight: bold; -} - -#tracy-debug pre, -#tracy-debug code { - font: 9pt/1.5 Consolas, monospace; -} - -#tracy-debug pre { - white-space: pre; -} - -#tracy-debug table .tracy-right { - text-align: right; -} - -#tracy-debug svg { - display: inline; -} - - -/* bar */ -#tracy-debug-bar { - font: normal normal 13px/1.55 Tahoma, sans-serif; - color: #333; - border: 1px solid #c9c9c9; - background: #EDEAE0 url('data:image/png;base64,R0lGODlhAQAUALMAAOzq4e/t5e7s4/Dt5vDu5e3r4vDu5uvp4O/t5AAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAAAAAAALAAAAAABABQAAAQM0EgySEAYi1LA+UcEADs=') top; - background-size: 1em; - position: fixed; - - min-width: 50px; - white-space: nowrap; - - z-index: 30000; - opacity: .9; - transition: opacity 0.2s; - will-change: opacity, top, left; - - border-radius: 3px; - box-shadow: 1px 1px 10px rgba(0, 0, 0, .15); -} - -#tracy-debug-bar:hover { - opacity: 1; - transition: opacity 0.1s; -} - -#tracy-debug-bar ul { - list-style: none none; - display: flex; -} - -#tracy-debug-bar ul:not(:first-child) li { - opacity: .6; - background: #F5F3EE; -} - -#tracy-debug-bar ul:not(:first-child) li:first-child { - width: 4.1em; - text-align: center; -} - -#tracy-debug-bar img { - vertical-align: bottom; - position: relative; - top: -2px; -} - -#tracy-debug-bar svg { - vertical-align: bottom; - width: 1.23em; - height: 1.55em; -} - -#tracy-debug-bar .tracy-label { - margin-left: .2em; -} - -#tracy-debug-bar li > a, -#tracy-debug-bar li > span { - color: #000; - display: block; - padding: 0 .4em; -} - -#tracy-debug-bar li > a:hover { - color: black; - background: #c3c1b8; -} - -#tracy-debug-bar li:first-child { - cursor: move; -} - -#tracy-debug-logo svg { - width: 3.4em; - margin: 0 .2em 0 .5em; -} - - -/* panels */ -#tracy-debug .tracy-panel { - display: none; - font: normal normal 12px/1.5 sans-serif; - background: white; - color: #333; - text-align: left; -} - -body#tracy-debug .tracy-panel { /* in popup window */ - display: block; -} - -#tracy-debug h1 { - font: normal normal 23px/1.4 Tahoma, sans-serif; - color: #575753; - margin: -5px -5px 5px; - padding: 0 25px 0 5px; - word-wrap: break-word; -} - -#tracy-debug .tracy-inner { - overflow: auto; - flex: 1; -} - -#tracy-debug .tracy-panel .tracy-icons { - display: none; -} - -#tracy-debug .tracy-mode-peek, -#tracy-debug .tracy-mode-float { - position: fixed; - flex-direction: column; - padding: 10px; - min-width: 200px; - min-height: 80px; - border-radius: 5px; - box-shadow: 1px 1px 20px rgba(102, 102, 102, 0.36); - border: 1px solid rgba(0, 0, 0, 0.1); -} - -#tracy-debug .tracy-mode-peek, -#tracy-debug .tracy-mode-float:not(.tracy-panel-resized) { - max-width: 700px; - max-height: 500px; -} - -@media (max-height: 555px) { - #tracy-debug .tracy-mode-peek, - #tracy-debug .tracy-mode-float:not(.tracy-panel-resized) { - max-height: 100vh; - } -} - -#tracy-debug .tracy-mode-peek h1 { - cursor: move; -} - -#tracy-debug .tracy-mode-float { - display: flex; - opacity: .95; - transition: opacity 0.2s; - will-change: opacity, top, left; - overflow: auto; - resize: both; -} - -#tracy-debug .tracy-focused { - display: flex; - opacity: 1; - transition: opacity 0.1s; -} - -#tracy-debug .tracy-mode-float h1 { - cursor: move; -} - -#tracy-debug .tracy-mode-float .tracy-icons { - display: block; - position: absolute; - top: 0; - right: 5px; - font-size: 18px; -} - -#tracy-debug .tracy-mode-window { - padding: 10px; -} - -#tracy-debug .tracy-icons a { - color: #575753; -} - -#tracy-debug .tracy-icons a:hover { - color: white; -} - - -#tracy-debug .tracy-inner-container { - min-width: 100%; - float: left; -} - - -/* dump */ -#tracy-debug pre.tracy-dump div { - padding-left: 3ex; -} - -#tracy-debug pre.tracy-dump div div { - border-left: 1px solid rgba(0, 0, 0, .1); - margin-left: .5ex; -} - -#tracy-debug pre.tracy-dump { - background: #FDF5CE; - padding: .4em .7em; - border: 1px dotted silver; - overflow: auto; -} - -#tracy-debug table pre.tracy-dump { - padding: 0; - margin: 0; - border: none; -} - -#tracy-debug .tracy-dump-array, -#tracy-debug .tracy-dump-object { - color: #C22; -} - -#tracy-debug .tracy-dump-string { - color: #35D; -} - -#tracy-debug .tracy-dump-number { - color: #090; -} - -#tracy-debug .tracy-dump-null, -#tracy-debug .tracy-dump-bool { - color: #850; -} - -#tracy-debug .tracy-dump-visibility, -#tracy-debug .tracy-dump-hash { - font-size: 85%; color: #999; -} - -#tracy-debug .tracy-dump-indent { - display: none; -} - - -/* toggle */ -#tracy-debug .tracy-toggle:after { - content: "\A0\25BC"; - opacity: .4; -} - -#tracy-debug .tracy-toggle.tracy-collapsed:after { - content: "\A0\25BA"; -} - - -@media print { - #tracy-debug * { - display: none; - } -} diff --git a/tracy-2.5.x/src/Tracy/assets/Bar/bar.js b/tracy-2.5.x/src/Tracy/assets/Bar/bar.js deleted file mode 100644 index 62baa00f..00000000 --- a/tracy-2.5.x/src/Tracy/assets/Bar/bar.js +++ /dev/null @@ -1,675 +0,0 @@ -/** - * This file is part of the Tracy (https://tracy.nette.org) - */ - -(function(){ - let nonce, contentId; - let baseUrl = location.href.split('#')[0]; - baseUrl += (baseUrl.indexOf('?') < 0 ? '?' : '&'); - - class Panel - { - constructor(id) { - this.id = id; - this.elem = document.getElementById(this.id); - this.elem.Tracy = this.elem.Tracy || {}; - } - - - init() { - let elem = this.elem; - - this.init = function() {}; - elem.innerHTML = addNonces(elem.dataset.tracyContent); - Tracy.Dumper.init(this.dumps, elem); - delete elem.dataset.tracyContent; - delete this.dumps; - evalScripts(elem); - - draggable(elem, { - handles: elem.querySelectorAll('h1'), - start: () => { - if (!this.is(Panel.FLOAT)) { - this.toFloat(); - } - this.focus(); - this.peekPosition = false; - } - }); - - elem.addEventListener('mousedown', () => { - this.focus(); - }); - - elem.addEventListener('mouseenter', () => { - clearTimeout(elem.Tracy.displayTimeout); - }); - - elem.addEventListener('mouseleave', () => { - this.blur(); - }); - - elem.addEventListener('mousemove', (e) => { - if (e.buttons && !this.is(Panel.RESIZED) && (elem.style.width || elem.style.height)) { - elem.classList.add(Panel.RESIZED); - } - }); - - elem.addEventListener('tracy-toggle', () => { - this.reposition(); - }); - - forEach(elem.querySelectorAll('.tracy-icons a'), (link) => { - link.addEventListener('click', (e) => { - if (link.rel === 'close') { - this.toPeek(); - } else if (link.rel === 'window') { - this.toWindow(); - } - e.preventDefault(); - }); - }); - - if (!this.is('tracy-ajax')) { - Tracy.Toggle.persist(elem); - } - } - - - is(mode) { - return this.elem.classList.contains(mode); - } - - - focus() { - let elem = this.elem; - if (this.is(Panel.WINDOW)) { - elem.Tracy.window.focus(); - - } else if (!this.is(Panel.FOCUSED)) { - for (let id in Debug.panels) { - Debug.panels[id].elem.classList.remove(Panel.FOCUSED); - } - elem.classList.add(Panel.FOCUSED); - elem.style.zIndex = Tracy.panelZIndex + Panel.zIndexCounter++; - } - } - - - blur() { - let elem = this.elem; - if (this.is(Panel.PEEK)) { - clearTimeout(elem.Tracy.displayTimeout); - elem.Tracy.displayTimeout = setTimeout(() => { - elem.classList.remove(Panel.FOCUSED); - }, 50); - } - } - - - toFloat() { - this.elem.classList.remove(Panel.WINDOW); - this.elem.classList.remove(Panel.PEEK); - this.elem.classList.add(Panel.FLOAT); - this.elem.classList.remove(Panel.RESIZED); - this.reposition(); - } - - - toPeek() { - this.elem.classList.remove(Panel.WINDOW); - this.elem.classList.remove(Panel.FLOAT); - this.elem.classList.remove(Panel.FOCUSED); - this.elem.classList.add(Panel.PEEK); - this.elem.style.width = ''; - this.elem.style.height = ''; - this.elem.classList.remove(Panel.RESIZED); - } - - - toWindow() { - let offset = getOffset(this.elem); - offset.left += typeof window.screenLeft === 'number' ? window.screenLeft : (window.screenX + 10); - offset.top += typeof window.screenTop === 'number' ? window.screenTop : (window.screenY + 50); - - let win = window.open('', this.id.replace(/-/g, '_'), 'left=' + offset.left + ',top=' + offset.top - + ',width=' + this.elem.offsetWidth + ',height=' + this.elem.offsetHeight + ',resizable=yes,scrollbars=yes'); - if (!win) { - return false; - } - - let doc = win.document; - doc.write('<!DOCTYPE html><meta charset="utf-8">' - + '<script src="' + (baseUrl.replace('&', '&').replace('"', '"')) + '_tracy_bar=js&XDEBUG_SESSION_STOP=1" onload="Tracy.Dumper.init()" async></script>' - + '<body id="tracy-debug">' - ); - doc.body.innerHTML = '<div class="tracy-panel tracy-mode-window" id="' + this.elem.id + '">' + this.elem.innerHTML + '</div>'; - evalScripts(doc.body); - if (this.elem.querySelector('h1')) { - doc.title = this.elem.querySelector('h1').textContent; - } - - win.addEventListener('beforeunload', () => { - this.toPeek(); - win.close(); // forces closing, can be invoked by F5 - }); - - doc.addEventListener('keyup', (e) => { - if (e.keyCode === 27 && !e.shiftKey && !e.altKey && !e.ctrlKey && !e.metaKey) { - win.close(); - } - }); - - this.elem.classList.remove(Panel.FLOAT); - this.elem.classList.remove(Panel.PEEK); - this.elem.classList.remove(Panel.FOCUSED); - this.elem.classList.remove(Panel.RESIZED); - this.elem.classList.add(Panel.WINDOW); - this.elem.Tracy.window = win; - return true; - } - - - reposition(deltaX, deltaY) { - let pos = getPosition(this.elem); - if (pos.width) { // is visible? - setPosition(this.elem, {left: pos.left + (deltaX || 0), top: pos.top + (deltaY || 0)}); - if (this.is(Panel.RESIZED)) { - let size = getWindowSize(); - this.elem.style.width = Math.min(size.width, pos.width) + 'px'; - this.elem.style.height = Math.min(size.height, pos.height) + 'px'; - } - } - } - - - savePosition() { - let pos = getPosition(this.elem); - if (this.is(Panel.WINDOW)) { - localStorage.setItem(this.id, JSON.stringify({window: true})); - } else if (pos.width) { // is visible? - localStorage.setItem(this.id, JSON.stringify({right: pos.right, bottom: pos.bottom, width: pos.width, height: pos.height, zIndex: this.elem.style.zIndex - Tracy.panelZIndex, resized: this.is(Panel.RESIZED)})); - } else { - localStorage.removeItem(this.id); - } - } - - - restorePosition() { - let pos = JSON.parse(localStorage.getItem(this.id)); - if (!pos) { - this.elem.classList.add(Panel.PEEK); - } else if (pos.window) { - this.init(); - this.toWindow() || this.toFloat(); - } else if (this.elem.dataset.tracyContent) { - this.init(); - this.toFloat(); - if (pos.resized) { - this.elem.classList.add(Panel.RESIZED); - this.elem.style.width = pos.width + 'px'; - this.elem.style.height = pos.height + 'px'; - } - setPosition(this.elem, pos); - this.elem.style.zIndex = Tracy.panelZIndex + (pos.zIndex || 1); - Panel.zIndexCounter = Math.max(Panel.zIndexCounter, (pos.zIndex || 1)) + 1; - } - } - } - - Panel.PEEK = 'tracy-mode-peek'; - Panel.FLOAT = 'tracy-mode-float'; - Panel.WINDOW = 'tracy-mode-window'; - Panel.FOCUSED = 'tracy-focused'; - Panel.RESIZED = 'tracy-panel-resized'; - Panel.zIndexCounter = 1; - - - class Bar - { - init() { - this.id = 'tracy-debug-bar'; - this.elem = document.getElementById(this.id); - - draggable(this.elem, { - handles: this.elem.querySelectorAll('li:first-child'), - draggedClass: 'tracy-dragged', - stop: () => { - this.savePosition(); - } - }); - - this.elem.addEventListener('mousedown', (e) => { - e.preventDefault(); - }); - - this.initTabs(this.elem); - this.restorePosition(); - - (new MutationObserver(() => { - this.restorePosition(); - })).observe(this.elem, {childList: true, characterData: true, subtree: true}); - } - - - initTabs(elem) { - forEach(elem.getElementsByTagName('a'), (link) => { - link.addEventListener('click', (e) => { - if (link.rel === 'close') { - this.close(); - - } else if (link.rel) { - let panel = Debug.panels[link.rel]; - panel.init(); - - if (e.shiftKey) { - panel.toFloat(); - panel.toWindow(); - - } else if (panel.is(Panel.FLOAT)) { - panel.toPeek(); - - } else { - panel.toFloat(); - if (panel.peekPosition) { - panel.reposition(-Math.round(Math.random() * 100) - 20, (Math.round(Math.random() * 100) + 20) * (this.isAtTop() ? 1 : -1)); - panel.peekPosition = false; - } - } - } - e.preventDefault(); - }); - - link.addEventListener('mouseenter', (e) => { - if (e.buttons || !link.rel || link.rel === 'close' || elem.classList.contains('tracy-dragged')) { - return; - } - - clearTimeout(this.displayTimeout); - this.displayTimeout = setTimeout(() => { - let panel = Debug.panels[link.rel]; - panel.focus(); - - if (panel.is(Panel.PEEK)) { - panel.init(); - - let pos = getPosition(panel.elem); - setPosition(panel.elem, { - left: getOffset(link).left + getPosition(link).width + 4 - pos.width, - top: this.isAtTop() - ? getOffset(this.elem).top + getPosition(this.elem).height + 4 - : getOffset(this.elem).top - pos.height - 4 - }); - panel.peekPosition = true; - } - }, 50); - }); - - link.addEventListener('mouseleave', () => { - clearTimeout(this.displayTimeout); - - if (link.rel && link.rel !== 'close' && !elem.classList.contains('tracy-dragged')) { - Debug.panels[link.rel].blur(); - } - }); - }); - this.autoHideLabels(); - } - - - autoHideLabels() { - let width = getWindowSize().width; - forEach(this.elem.children, (ul) => { - let i, labels = ul.querySelectorAll('.tracy-label'); - for (i = 0; i < labels.length && ul.clientWidth < width; i++) { - labels.item(i).hidden = false; - } - for (i = labels.length - 1; i >= 0 && ul.clientWidth >= width; i--) { - labels.item(i).hidden = true; - } - }); - } - - - close() { - document.getElementById('tracy-debug').style.display = 'none'; - } - - - reposition(deltaX, deltaY) { - let pos = getPosition(this.elem); - if (pos.width) { // is visible? - setPosition(this.elem, {left: pos.left + (deltaX || 0), top: pos.top + (deltaY || 0)}); - this.savePosition(); - } - } - - - savePosition() { - let pos = getPosition(this.elem); - if (pos.width) { // is visible? - localStorage.setItem(this.id, JSON.stringify(this.isAtTop() ? {right: pos.right, top: pos.top} : {right: pos.right, bottom: pos.bottom})); - } - } - - - restorePosition() { - let pos = JSON.parse(localStorage.getItem(this.id)); - setPosition(this.elem, pos || {right: 0, bottom: 0}); - this.savePosition(); - } - - - isAtTop() { - let pos = getPosition(this.elem); - return pos.top < 100 && pos.bottom > pos.top; - } - } - - - class Debug - { - static init(content, dumps) { - Debug.layer = document.createElement('div'); - Debug.layer.setAttribute('id', 'tracy-debug'); - Debug.layer.innerHTML = addNonces(content); - (document.body || document.documentElement).appendChild(Debug.layer); - evalScripts(Debug.layer); - Tracy.Dumper.init(); - Debug.layer.style.display = 'block'; - Debug.bar.init(); - - forEach(document.querySelectorAll('.tracy-panel'), (panel) => { - Debug.panels[panel.id] = new Panel(panel.id); - Debug.panels[panel.id].dumps = dumps; - Debug.panels[panel.id].restorePosition(); - }); - - Debug.captureWindow(); - Debug.captureAjax(); - } - - - static loadAjax(content, dumps) { - forEach(Debug.layer.querySelectorAll('.tracy-panel.tracy-ajax'), (panel) => { - Debug.panels[panel.id].savePosition(); - delete Debug.panels[panel.id]; - panel.parentNode.removeChild(panel); - }); - - let ajaxBar = document.getElementById('tracy-ajax-bar'); - if (ajaxBar) { - ajaxBar.parentNode.removeChild(ajaxBar); - } - - Debug.layer.insertAdjacentHTML('beforeend', content); - evalScripts(Debug.layer); - ajaxBar = document.getElementById('tracy-ajax-bar'); - Debug.bar.elem.appendChild(ajaxBar); - - forEach(document.querySelectorAll('.tracy-panel'), (panel) => { - if (!Debug.panels[panel.id]) { - Debug.panels[panel.id] = new Panel(panel.id); - Debug.panels[panel.id].dumps = dumps; - Debug.panels[panel.id].restorePosition(); - } - }); - - Debug.bar.initTabs(ajaxBar); - } - - - static captureWindow() { - let size = getWindowSize(); - - window.addEventListener('resize', () => { - let newSize = getWindowSize(); - - Debug.bar.reposition(newSize.width - size.width, newSize.height - size.height); - Debug.bar.autoHideLabels(); - - for (let id in Debug.panels) { - Debug.panels[id].reposition(newSize.width - size.width, newSize.height - size.height); - } - - size = newSize; - }); - - window.addEventListener('unload', () => { - for (let id in Debug.panels) { - Debug.panels[id].savePosition(); - } - }); - } - - - static captureAjax() { - let header = Tracy.getAjaxHeader(); - if (!header) { - return; - } - let oldOpen = XMLHttpRequest.prototype.open; - - XMLHttpRequest.prototype.open = function() { - oldOpen.apply(this, arguments); - if (window.TracyAutoRefresh !== false && new URL(arguments[1], location.origin).host === location.host) { - this.setRequestHeader('X-Tracy-Ajax', header); - this.addEventListener('load', function() { - if (this.getAllResponseHeaders().match(/^X-Tracy-Ajax: 1/mi)) { - Debug.loadScript(baseUrl + '_tracy_bar=content-ajax.' + header + '&XDEBUG_SESSION_STOP=1&v=' + Math.random()); - } - }); - } - }; - - if (window.fetch) { - let oldFetch = window.fetch; - window.fetch = function(request, options) { - request = request instanceof Request ? request : new Request(request, options || {}); - - if (window.TracyAutoRefresh !== false && new URL(request.url, location.origin).host === location.host) { - request.headers.set('X-Tracy-Ajax', header); - return oldFetch(request).then((response) => { - if (response.headers.has('X-Tracy-Ajax') && response.headers.get('X-Tracy-Ajax')[0] === '1') { - Debug.loadScript(baseUrl + '_tracy_bar=content-ajax.' + header + '&XDEBUG_SESSION_STOP=1&v=' + Math.random()); - } - - return response; - }); - } - - return oldFetch(request); - }; - } - } - - - static loadScript(url) { - if (Debug.scriptElem) { - Debug.scriptElem.parentNode.removeChild(Debug.scriptElem); - } - Debug.scriptElem = document.createElement('script'); - Debug.scriptElem.src = url; - Debug.scriptElem.setAttribute('nonce', nonce); - (document.body || document.documentElement).appendChild(Debug.scriptElem); - } - } - - - function evalScripts(elem) { - forEach(elem.getElementsByTagName('script'), (script) => { - if ((!script.hasAttribute('type') || script.type === 'text/javascript' || script.type === 'application/javascript') && !script.tracyEvaluated) { - let document = script.ownerDocument; - let dolly = document.createElement('script'); - dolly.textContent = script.textContent; - dolly.setAttribute('nonce', nonce); - (document.body || document.documentElement).appendChild(dolly); - script.tracyEvaluated = true; - } - }); - } - - - let dragging; - - function draggable(elem, options) { - let dE = document.documentElement, started, deltaX, deltaY, clientX, clientY; - options = options || {}; - - let redraw = function () { - if (dragging) { - setPosition(elem, {left: clientX + deltaX, top: clientY + deltaY}); - requestAnimationFrame(redraw); - } - }; - - let onMove = function(e) { - if (e.buttons === 0) { - return onEnd(e); - } - if (!started) { - if (options.draggedClass) { - elem.classList.add(options.draggedClass); - } - if (options.start) { - options.start(e, elem); - } - started = true; - } - - clientX = e.touches ? e.touches[0].clientX : e.clientX; - clientY = e.touches ? e.touches[0].clientY : e.clientY; - return false; - }; - - let onEnd = function(e) { - if (started) { - if (options.draggedClass) { - elem.classList.remove(options.draggedClass); - } - if (options.stop) { - options.stop(e, elem); - } - } - dragging = null; - dE.removeEventListener('mousemove', onMove); - dE.removeEventListener('mouseup', onEnd); - dE.removeEventListener('touchmove', onMove); - dE.removeEventListener('touchend', onEnd); - return false; - }; - - let onStart = function(e) { - e.preventDefault(); - e.stopPropagation(); - - if (dragging) { // missed mouseup out of window? - return onEnd(e); - } - - let pos = getPosition(elem); - clientX = e.touches ? e.touches[0].clientX : e.clientX; - clientY = e.touches ? e.touches[0].clientY : e.clientY; - deltaX = pos.left - clientX; - deltaY = pos.top - clientY; - dragging = true; - started = false; - dE.addEventListener('mousemove', onMove); - dE.addEventListener('mouseup', onEnd); - dE.addEventListener('touchmove', onMove); - dE.addEventListener('touchend', onEnd); - requestAnimationFrame(redraw); - if (options.start) { - options.start(e, elem); - } - }; - - forEach(options.handles, (handle) => { - handle.addEventListener('mousedown', onStart); - handle.addEventListener('touchstart', onStart); - - handle.addEventListener('click', (e) => { - if (started) { - e.stopImmediatePropagation(); - } - }); - }); - } - - - // returns total offset for element - function getOffset(elem) { - let res = {left: elem.offsetLeft, top: elem.offsetTop}; - while (elem = elem.offsetParent) { // eslint-disable-line no-cond-assign - res.left += elem.offsetLeft; res.top += elem.offsetTop; - } - return res; - } - - - function getWindowSize() { - return { - width: document.documentElement.clientWidth, - height: document.compatMode === 'BackCompat' ? window.innerHeight : document.documentElement.clientHeight - }; - } - - - // move to new position - function setPosition(elem, coords) { - let win = getWindowSize(); - if (typeof coords.right !== 'undefined') { - coords.left = win.width - elem.offsetWidth - coords.right; - } - if (typeof coords.bottom !== 'undefined') { - coords.top = win.height - elem.offsetHeight - coords.bottom; - } - elem.style.left = Math.max(0, Math.min(coords.left, win.width - elem.offsetWidth)) + 'px'; - elem.style.top = Math.max(0, Math.min(coords.top, win.height - elem.offsetHeight)) + 'px'; - } - - - // returns current position - function getPosition(elem) { - let win = getWindowSize(); - return { - left: elem.offsetLeft, - top: elem.offsetTop, - right: win.width - elem.offsetWidth - elem.offsetLeft, - bottom: win.height - elem.offsetHeight - elem.offsetTop, - width: elem.offsetWidth, - height: elem.offsetHeight - }; - } - - - function addNonces(html) { - let el = document.createElement('div'); - el.innerHTML = html; - forEach(el.getElementsByTagName('style'), (style) => { - style.setAttribute('nonce', nonce); - }); - return el.innerHTML; - } - - - function forEach(arr, cb) { - Array.prototype.forEach.call(arr, cb); - } - - - if (document.currentScript) { - nonce = document.currentScript.getAttribute('nonce') || document.currentScript.nonce; - contentId = document.currentScript.dataset.id; - } - - Tracy = window.Tracy || {}; - Tracy.panelZIndex = Tracy.panelZIndex || 20000; - Tracy.DebugPanel = Panel; - Tracy.DebugBar = Bar; - Tracy.Debug = Debug; - Tracy.getAjaxHeader = () => contentId; - - Debug.bar = new Bar; - Debug.panels = {}; -})(); diff --git a/tracy-2.5.x/src/Tracy/assets/Bar/bar.phtml b/tracy-2.5.x/src/Tracy/assets/Bar/bar.phtml deleted file mode 100644 index 70b30df5..00000000 --- a/tracy-2.5.x/src/Tracy/assets/Bar/bar.phtml +++ /dev/null @@ -1,47 +0,0 @@ -<?php - -/** - * Debug Bar template. - * - * This file is part of the Tracy (http://tracy.nette.org) - * Copyright (c) 2004 David Grudl (http://davidgrudl.com) - * - * @param array $rows - */ - -namespace Tracy; - -$tabs = function ($panels) { - foreach ($panels as $panel) { - if ($panel->tab) { ?> - <li><?php if ($panel->panel): ?><a href="#" rel="tracy-debug-panel-<?= $panel->id ?>"><?= trim($panel->tab) ?></a><?php else: echo '<span>', trim($panel->tab), '</span>'; endif ?></li><?php - } - } -}; -?> - -<?php if ($rows[0]->type === 'main'): ?><div id="tracy-debug-bar"><?php endif ?> - -<?php foreach ($rows as $row): ?> -<?php if ($row->type === 'main'): ?> - <ul> - <li id="tracy-debug-logo" title="Tracy Debugger <?= Debugger::VERSION, " \nhttps://tracy.nette.org" ?>"> - <svg viewBox="0 -10 1561 333"><path fill="#585755" d="m176 327h-57v-269h-119v-57h291v57h-115v269zm208-191h114c50 0 47-78 0-78h-114v78zm106-135c17 0 33 2 46 7 75 30 75 144 1 175-13 6-29 8-47 8h-27l132 74v68l-211-128v122h-57v-326h163zm300 57c-5 0-9 3-11 9l-56 156h135l-55-155c-2-7-6-10-13-10zm-86 222l-17 47h-61l102-285c20-56 107-56 126 0l102 285h-61l-17-47h-174zm410 47c-98 0-148-55-148-163v-2c0-107 50-161 149-161h118v57h-133c-26 0-45 8-58 25-12 17-19 44-19 81 0 71 26 106 77 106h133v57h-119zm270-145l-121-181h68l81 130 81-130h68l-121 178v148h-56v-145z"/></svg> - </li> - <?php $tabs($row->panels) ?> - <li><a href="#" rel="close" title="close debug bar">×</a></li> - </ul> -<?php endif; if ($row->type === 'redirect'): ?> - <ul> - <li><span title="Previous request before redirect">redirect</span></li> - <?php $tabs($row->panels) ?> - </ul> -<?php endif; if ($row->type === 'ajax'): ?> - <ul id="tracy-ajax-bar"> - <li>AJAX</li> - <?php $tabs($row->panels) ?> - </ul> -<?php endif ?> -<?php endforeach ?> - -<?php if ($rows[0]->type === 'main'): ?></div><?php endif ?> diff --git a/tracy-2.5.x/src/Tracy/assets/Bar/dumps.panel.phtml b/tracy-2.5.x/src/Tracy/assets/Bar/dumps.panel.phtml deleted file mode 100644 index a3f0a339..00000000 --- a/tracy-2.5.x/src/Tracy/assets/Bar/dumps.panel.phtml +++ /dev/null @@ -1,33 +0,0 @@ -<?php - -/** - * Debug Bar: panel "dumps" template. - * - * This file is part of the Tracy (https://tracy.nette.org) - * Copyright (c) 2004 David Grudl (https://davidgrudl.com) - */ - -namespace Tracy; - -?> -<style class="tracy-debug"> - #tracy-debug .tracy-DumpPanel h2 { - font: 11pt/1.5 sans-serif; - margin: 0; - padding: 2px 8px; - background: #3484d2; - color: white; - } -</style> - -<h1>Dumps</h1> - -<div class="tracy-inner tracy-DumpPanel"> -<?php foreach ($data as $item): ?> - <?php if ($item['title']):?> - <h2><?= Helpers::escapeHtml($item['title']) ?></h2> - <?php endif ?> - - <?= $item['dump'] ?> -<?php endforeach ?> -</div> diff --git a/tracy-2.5.x/src/Tracy/assets/Bar/dumps.tab.phtml b/tracy-2.5.x/src/Tracy/assets/Bar/dumps.tab.phtml deleted file mode 100644 index a5c27202..00000000 --- a/tracy-2.5.x/src/Tracy/assets/Bar/dumps.tab.phtml +++ /dev/null @@ -1,17 +0,0 @@ -<?php - -/** - * Debug Bar: tab "dumps" template. - * - * This file is part of the Tracy (https://tracy.nette.org) - * Copyright (c) 2004 David Grudl (https://davidgrudl.com) - */ - -namespace Tracy; - -if (empty($data)) { - return; -} -?> -<svg viewBox="0 0 2048 2048"><path fill="#154ABD" d="m1084 540c-110-1-228-2-325 58-54 35-87 94-126 143-94 162-71 383 59 519 83 94 207 151 333 149 132 3 261-60 344-160 122-138 139-355 44-511-73-66-133-158-234-183-31-9-65-9-95-14zm-60 116c73 0 53 115-16 97-105 5-195 102-192 207-2 78-122 48-95-23 8-153 151-285 304-280l-1-1zM1021 511"/><path fill="#4B6193" d="m1021 511c-284-2-560 131-746 344-53 64-118 125-145 206-16 86 59 152 103 217 219 267 575 428 921 377 312-44 600-241 755-515 39-81-30-156-74-217-145-187-355-327-581-384-77-19-156-29-234-28zm0 128c263-4 512 132 679 330 33 52 132 110 58 168-170 237-449 409-747 399-309 0-590-193-752-447 121-192 305-346 526-407 75-25 170-38 237-43z"/> -</svg><span class="tracy-label">dumps</span> diff --git a/tracy-2.5.x/src/Tracy/assets/Bar/errors.panel.phtml b/tracy-2.5.x/src/Tracy/assets/Bar/errors.panel.phtml deleted file mode 100644 index cada6501..00000000 --- a/tracy-2.5.x/src/Tracy/assets/Bar/errors.panel.phtml +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * Debug Bar: panel "error" template. - * - * This file is part of the Tracy (https://tracy.nette.org) - * Copyright (c) 2004 David Grudl (https://davidgrudl.com) - */ - -namespace Tracy; - -?> -<h1>Errors</h1> - -<div class="tracy-inner"> -<table> -<?php foreach ($data as $item => $count): list($file, $line, $message) = explode('|', $item, 3) ?> -<tr> - <td class="tracy-right"><?= $count ? "$count\xC3\x97" : '' ?></td> - <td><pre><?= Helpers::escapeHtml($message), ' in ', Helpers::editorLink($file, $line) ?></pre></td> -</tr> -<?php endforeach ?> -</table> -</div> diff --git a/tracy-2.5.x/src/Tracy/assets/Bar/errors.tab.phtml b/tracy-2.5.x/src/Tracy/assets/Bar/errors.tab.phtml deleted file mode 100644 index cfcc2f75..00000000 --- a/tracy-2.5.x/src/Tracy/assets/Bar/errors.tab.phtml +++ /dev/null @@ -1,29 +0,0 @@ -<?php - -/** - * Debug Bar: tab "error" template. - * - * This file is part of the Tracy (https://tracy.nette.org) - * Copyright (c) 2004 David Grudl (https://davidgrudl.com) - */ - -namespace Tracy; - -if (empty($data)) { - return; -} -?> -<style class="tracy-debug"> - #tracy-debug .tracy-ErrorTab { - display: block; - background: #D51616; - color: white; - font-weight: bold; - margin: -1px -.4em; - padding: 1px .4em; - } -</style> -<span class="tracy-ErrorTab"> -<svg viewBox="0 0 2048 2048"><path fill="#fff" d="M1152 1503v-190q0-14-9.5-23.5t-22.5-9.5h-192q-13 0-22.5 9.5t-9.5 23.5v190q0 14 9.5 23.5t22.5 9.5h192q13 0 22.5-9.5t9.5-23.5zm-2-374l18-459q0-12-10-19-13-11-24-11h-220q-11 0-24 11-10 7-10 21l17 457q0 10 10 16.5t24 6.5h185q14 0 23.5-6.5t10.5-16.5zm-14-934l768 1408q35 63-2 126-17 29-46.5 46t-63.5 17h-1536q-34 0-63.5-17t-46.5-46q-37-63-2-126l768-1408q17-31 47-49t65-18 65 18 47 49z"/> -</svg><span class="tracy-label"><?= $sum = array_sum($data), $sum > 1 ? ' errors' : ' error' ?></span> -</span> diff --git a/tracy-2.5.x/src/Tracy/assets/Bar/info.panel.phtml b/tracy-2.5.x/src/Tracy/assets/Bar/info.panel.phtml deleted file mode 100644 index 060be3aa..00000000 --- a/tracy-2.5.x/src/Tracy/assets/Bar/info.panel.phtml +++ /dev/null @@ -1,119 +0,0 @@ -<?php - -/** - * Debug Bar: panel "info" template. - * - * This file is part of the Tracy (https://tracy.nette.org) - * Copyright (c) 2004 David Grudl (https://davidgrudl.com) - */ - -namespace Tracy; - -if (isset($this->cpuUsage) && $this->time) { - foreach (getrusage() as $key => $val) { - $this->cpuUsage[$key] -= $val; - } - $userUsage = -round(($this->cpuUsage['ru_utime.tv_sec'] * 1e6 + $this->cpuUsage['ru_utime.tv_usec']) / $this->time / 10000); - $systemUsage = -round(($this->cpuUsage['ru_stime.tv_sec'] * 1e6 + $this->cpuUsage['ru_stime.tv_usec']) / $this->time / 10000); -} - -$countClasses = function ($list) { - return count(array_filter($list, function ($name) { - return (new \ReflectionClass($name))->isUserDefined(); - })); -}; - -$opcache = function_exists('opcache_get_status') ? @opcache_get_status() : null; // @ can be restricted -$cachedFiles = isset($opcache['scripts']) ? array_intersect(array_keys($opcache['scripts']), get_included_files()) : []; - -$info = [ - 'Execution time' => number_format($this->time * 1000, 1, '.', ' ') . ' ms', - 'CPU usage user + system' => isset($userUsage) ? (int) $userUsage . ' % + ' . (int) $systemUsage . ' %' : null, - 'Peak of allocated memory' => number_format(memory_get_peak_usage() / 1000000, 2, '.', ' ') . ' MB', - 'Included files' => count(get_included_files()), - 'OPcache' => $opcache ? round(count($cachedFiles) * 100 / count(get_included_files())) . '% cached' : null, - 'Classes + interfaces + traits' => $countClasses(get_declared_classes()) . ' + ' - . $countClasses(get_declared_interfaces()) . ' + ' . $countClasses(get_declared_traits()), - 'Your IP' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null, - 'Server IP' => isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : null, - 'HTTP method / response code' => isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] . ' / ' . http_response_code() : null, - 'PHP' => PHP_VERSION, - 'Xdebug' => extension_loaded('xdebug') ? phpversion('xdebug') : null, - 'Tracy' => Debugger::VERSION, - 'Server' => isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : null, -]; - -$info = array_map('strval', array_filter($info + (array) $this->data)); - -$packages = $devPackages = []; -if (class_exists('Composer\Autoload\ClassLoader', false)) { - $baseDir = call_user_func(function () { - @include dirname((new \ReflectionClass('Composer\Autoload\ClassLoader'))->getFileName()) . '/autoload_psr4.php'; // @ may not exist - return $baseDir; - }); - $composer = @json_decode(file_get_contents($baseDir . '/composer.lock')); // @ may not exist or be valid - list($packages, $devPackages) = [(array) @$composer->packages, (array) @$composer->{'packages-dev'}]; // @ keys may not exist - $tmp = [&$packages, &$devPackages]; - foreach ($tmp as &$items) { - @array_walk($items, function($package) { // @ keys may not exist - $package->hash = $package->source->reference ?: $package->dist->reference; - }, $items); - usort($items, function ($a, $b) { return strcmp($a->name, $b->name); }); - } -} - -?> -<style class="tracy-debug"> - #tracy-debug .tracy-InfoPanel td { - white-space: nowrap; - } - #tracy-debug .tracy-InfoPanel td:nth-child(2) { - font-weight: bold; - width: 30%; - } - #tracy-debug .tracy-InfoPanel td[colspan='2'] b { - float: right; - margin-left: 2em; - } -</style> - -<h1>System info</h1> - -<div class="tracy-inner tracy-InfoPanel"> -<div class="tracy-inner-container"> -<table> -<?php foreach ($info as $key => $val): ?> -<tr> -<?php if (strlen($val) > 25): ?> - <td colspan=2><?= Helpers::escapeHtml($key) ?> <b><?= Helpers::escapeHtml($val) ?></b></td> -<?php else: ?> - <td><?= Helpers::escapeHtml($key) ?></td><td><?= Helpers::escapeHtml($val) ?></td> -<?php endif ?> -</tr> -<?php endforeach ?> -</table> - -<?php if ($packages || $devPackages): ?> - <h2><a class="tracy-toggle tracy-collapsed" data-tracy-ref="^div .tracy-InfoPanel-packages">Composer Packages (<?= count($packages), $devPackages ? ' + ' . count($devPackages) . ' dev' : '' ?>)</a></h2> - - <div class="tracy-InfoPanel-packages tracy-collapsed"> - <?php if ($packages): ?> - <table> - <?php foreach ($packages as $package): ?> - <tr><td><?= Helpers::escapeHtml($package->name) ?></td><td><?= Helpers::escapeHtml($package->version . (strpos($package->version, 'dev') !== false && $package->hash ? ' #' . substr($package->hash, 0, 4) : '')) ?></td></tr> - <?php endforeach ?> - </table> - <?php endif ?> - - <?php if ($devPackages): ?> - <h2>Dev Packages</h2> - <table> - <?php foreach ($devPackages as $package): ?> - <tr><td><?= Helpers::escapeHtml($package->name) ?></td><td><?= Helpers::escapeHtml($package->version . (strpos($package->version, 'dev') !== false && $package->hash ? ' #' . substr($package->hash, 0, 4) : '')) ?></td></tr> - <?php endforeach ?> - </table> - <?php endif ?> - </div> -<?php endif ?> -</div> -</div> diff --git a/tracy-2.5.x/src/Tracy/assets/Bar/info.tab.phtml b/tracy-2.5.x/src/Tracy/assets/Bar/info.tab.phtml deleted file mode 100644 index 7e0997b7..00000000 --- a/tracy-2.5.x/src/Tracy/assets/Bar/info.tab.phtml +++ /dev/null @@ -1,18 +0,0 @@ -<?php - -/** - * Debug Bar: tab "info" template. - * - * This file is part of the Tracy (https://tracy.nette.org) - * Copyright (c) 2004 David Grudl (https://davidgrudl.com) - */ - -namespace Tracy; - -$this->time = microtime(true) - Debugger::$time; - -?> -<span title="Execution time"> -<svg viewBox="0 0 2048 2048"><path fill="#86bbf0" d="m640 1153.6v639.3h-256v-639.3z"/><path fill="#6ba9e6" d="m1024 254.68v1538.2h-256v-1538.2z"/><path fill="#4f96dc" d="m1408 897.57v894.3h-256v-894.3z"/><path fill="#3987d4" d="m1792 513.08v1279.8h-256v-1279.8z"/> -</svg><span class="tracy-label"><?= number_format($this->time * 1000, 1, '.', ' ') ?> ms</span> -</span> diff --git a/tracy-2.5.x/src/Tracy/assets/Bar/loader.phtml b/tracy-2.5.x/src/Tracy/assets/Bar/loader.phtml deleted file mode 100644 index b40870ce..00000000 --- a/tracy-2.5.x/src/Tracy/assets/Bar/loader.phtml +++ /dev/null @@ -1,31 +0,0 @@ -<?php - -/** - * Debug Bar loader template. - * - * It uses Font Awesome by Dave Gandy - http://fontawesome.io - * - * This file is part of the Tracy (https://tracy.nette.org) - * Copyright (c) 2004 David Grudl (https://davidgrudl.com) - */ - -namespace Tracy; - -$baseUrl = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''; -$baseUrl .= strpos($baseUrl, '?') === false ? '?' : '&'; -$nonceAttr = $nonce ? ' nonce="' . Helpers::escapeHtml($nonce) . '"' : ''; -$asyncAttr = $async ? ' async' : ''; -?> -<?php if (empty($content)): ?> -<script src="<?= Helpers::escapeHtml($baseUrl) ?>_tracy_bar=<?= urlencode("content.$contentId") ?>&XDEBUG_SESSION_STOP=1" data-id="<?= Helpers::escapeHtml($contentId) ?>"<?= $asyncAttr, $nonceAttr ?>></script> -<?php else: ?> - - - - -<!-- Tracy Debug Bar --> -<script src="<?= Helpers::escapeHtml($baseUrl) ?>_tracy_bar=js&v=<?= urlencode(Debugger::VERSION) ?>&XDEBUG_SESSION_STOP=1" data-id="<?= Helpers::escapeHtml($contentId) ?>"<?= $nonceAttr ?>></script> -<script<?= $nonceAttr ?>> -Tracy.Debug.init(<?= str_replace('<!--', '<\!--', json_encode($content) . ', ' . json_encode($dumps)) ?>); -</script> -<?php endif ?> diff --git a/tracy-2.5.x/src/Tracy/assets/Bar/panels.phtml b/tracy-2.5.x/src/Tracy/assets/Bar/panels.phtml deleted file mode 100644 index 5292f122..00000000 --- a/tracy-2.5.x/src/Tracy/assets/Bar/panels.phtml +++ /dev/null @@ -1,29 +0,0 @@ -<?php - -/** - * Debug Bar panels template. - * - * This file is part of the Tracy (http://tracy.nette.org) - * Copyright (c) 2004 David Grudl (http://davidgrudl.com) - * - * @param array $rows - */ - -namespace Tracy; - -use Tracy\Helpers; - -$icons = ' - <div class="tracy-icons"> - <a href="#" rel="window" title="open in window">¤</a> - <a href="#" rel="close" title="close window">×</a> - </div> -'; - -foreach ($rows as $row) { - foreach ($row->panels as $panel) { - $content = $panel->panel ? ($panel->panel . "\n" . $icons) : ''; - $class = 'tracy-panel ' . ($row->type === 'ajax' ? 'tracy-ajax' : ''); ?> - <div class="<?= $class ?>" id="tracy-debug-panel-<?= $panel->id ?>" data-tracy-content="<?= Helpers::escapeHtml($content) ?>"></div><?php - } -} diff --git a/tracy-2.5.x/src/Tracy/assets/BlueScreen/bluescreen.css b/tracy-2.5.x/src/Tracy/assets/BlueScreen/bluescreen.css deleted file mode 100644 index ba593fe0..00000000 --- a/tracy-2.5.x/src/Tracy/assets/BlueScreen/bluescreen.css +++ /dev/null @@ -1,329 +0,0 @@ -/** - * This file is part of the Tracy (https://tracy.nette.org) - */ - -#tracy-bs { - font: 9pt/1.5 Verdana, sans-serif; - background: white; - color: #333; - position: absolute; - z-index: 20000; - left: 0; - top: 0; - width: 100%; - text-align: left; -} - -#tracy-bs * { - font: inherit; - color: inherit; - background: transparent; - border: none; - margin: 0; - padding: 0; - text-align: inherit; - text-indent: 0; -} - -#tracy-bs *:before, -#tracy-bs *:after { - all: unset; -} - -#tracy-bs b { - font-weight: bold; -} - -#tracy-bs i { - font-style: italic; -} - -#tracy-bs a { - text-decoration: none; - color: #328ADC; - padding: 2px 4px; - margin: -2px -4px; -} - -#tracy-bs a:hover, -#tracy-bs a:focus { - color: #085AA3; -} - -#tracy-bs-toggle { - position: absolute; - right: .5em; - top: .5em; - text-decoration: none; - background: #CD1818; - color: white !important; - padding: 3px; -} - -#tracy-bs-error { - background: #CD1818; - color: white; - font-size: 13pt; -} - -#tracy-bs-error a { - color: white !important; - opacity: 0; - font-size: .7em; - border-bottom: none !important; -} - -#tracy-bs-error:hover a { - opacity: .6; -} - -#tracy-bs-error a:hover { - opacity: 1; -} - -#tracy-bs-error i { - color: #ffefa1; - font-style: normal; -} - -#tracy-bs h1 { - font-size: 15pt; - font-weight: normal; - text-shadow: 1px 1px 2px rgba(0, 0, 0, .3); - margin: .7em 0; -} - -#tracy-bs h1 span { - white-space: pre-wrap; -} - -#tracy-bs h2 { - font-size: 14pt; - margin: .6em 0; -} - -#tracy-bs h3 { - font-size: 10pt; - font-weight: bold; - margin: 1em 0; - padding: 0; -} - -#tracy-bs p, -#tracy-bs pre { - margin: .8em 0 -} - -#tracy-bs pre, -#tracy-bs code, -#tracy-bs table { - font: 9pt/1.5 Consolas, monospace !important; -} - -#tracy-bs pre, -#tracy-bs table { - background: #FDF5CE; - padding: .4em .7em; - border: 1px dotted silver; - overflow: auto; -} - -#tracy-bs table pre { - padding: 0; - margin: 0; - border: none; -} - -#tracy-bs table { - border-collapse: collapse; - width: 100%; -} - -#tracy-bs td, -#tracy-bs th { - vertical-align: top; - text-align: left; - padding: 2px 6px; - border: 1px solid #e6dfbf; -} - -#tracy-bs th { - font-weight: bold; -} - -#tracy-bs tr > :first-child { - width: 20%; -} - -#tracy-bs tr:nth-child(2n), -#tracy-bs tr:nth-child(2n) pre { - background-color: #F7F0CB; -} - -#tracy-bs ol { - margin: 1em 0; - padding-left: 2.5em; -} - -#tracy-bs ul { - font-size: 7pt; - padding: 2em 3em; - margin: 1em 0 0; - color: #777; - background: #F6F5F3; - border-top: 1px solid #DDD; - list-style: none; -} - -#tracy-bs-logo a { - position: absolute; - bottom: 0; - right: 0; - width: 100px; - height: 50px; - background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFoAAAAUBAMAAAD/1DctAAAAMFBMVEWupZzj39rEvbTy8O3X0sz9/PvGwLu8tavQysHq6OS0rKP5+Pbd2dT29fPMxbzPx8DKErMJAAAACXBIWXMAAAsTAAALEwEAmpwYAAACGUlEQVQoFX3TQWgTQRQA0MWLIJJDYehBTykhG5ERTx56K1u8eEhCYtomE7x5L4iLh0ViF7egewuFFqSIYE6hIHsIYQ6CQSg9CDKn4QsNCRlB59C74J/ZNHW1+An5+bOPyf6/s46oz2P+A0yIeZZ2ieEHi6TOnLKTxvWq+b52mxlVO3xnM1s7xLX1504XQH65OnW2dBqn7cCkYsFsfYsWpyY/2salmFTpEyzeR8zosYqMdiPDXdyU52K1wgEa/SjGpdEwUAxqvRfckQCDOyFearsEHe2grvkh/cFAHKvdtI3lcVceKQIOFpv+FOZaNPQBwJZLPp+hfrvT5JZXaUFsR8zqQc9qSgAharkfS5M/5F6nGJJAtXq/eLr3ucZpHccSxOOIPaQhtHohpCH2Xu6rLmQ0djnr4/+J3C6v+AW8/XWYxwYNdlhWj/P5fPSTQwVr0T9lGxdaBCqErNZaqYnEwbkjEB3NasGF3lPdrHa1nnxNOMgj0+neePUPjd2v/qVvUv29ifvc19huQ48qwXShy/9o8o3OSk0cs37mOFd0Ydgvsf/oZEnPVtggfd66lORn9mDyyzXU13SRtH2L6aR5T/snGAcZPfAXz5J1YlJWBEuxdMYqQecpBrlM49xAbmqyHA+xlA1FxBtqT2xmJoNXZlIt74ZBLeJ9ZGDqByNI7p543idzJ23vXEv7IgnsxiS+eNtwNbFdLq7+Bi4wQ0I4SVb9AAAAAElFTkSuQmCC') no-repeat; - opacity: .6; - padding: 0; - margin: 0; -} - -#tracy-bs-logo a:hover, -#tracy-bs-logo a:focus { - opacity: 1; - transition: opacity 0.1s; -} - - -#tracy-bs div.panel { - padding: 1px 25px; -} - -#tracy-bs div.inner { - background: #F4F3F1; - padding: .1em 1em 1em; - border-radius: 8px; -} - -#tracy-bs .outer { - overflow: auto; -} - - -/* source code */ -#tracy-bs pre.code > div { - min-width: 100%; - float: left; - white-space: pre; -} - -#tracy-bs .highlight { - background: #CD1818; - color: white; - font-weight: bold; - font-style: normal; - display: block; - padding: 0 .4em; - margin: 0 -.4em; -} - -#tracy-bs .line { - color: #9F9C7F; - font-weight: normal; - font-style: normal; -} - -#tracy-bs pre:hover span[title] { - border-bottom: 1px solid rgba(0, 0, 0, 0.2); -} - -#tracy-bs a[href^=editor\:] { - color: inherit; - border-bottom: 1px dotted rgba(0, 0, 0, .3); -} - -#tracy-bs span[data-tracy-href] { - border-bottom: 1px dotted rgba(0, 0, 0, .3); -} - - -/* toggle */ -html.tracy-js #tracy-bs .tracy-collapsed { - display: none; -} - -html.tracy-js #tracy-bs .tracy-toggle.tracy-collapsed { - display: inline; -} - -#tracy-bs .tracy-toggle { - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - -khtml-user-select: none; - -ms-user-select: none; - user-select: none; -} - -#tracy-bs .tracy-toggle:after { - content: "\A0\25BC"; - opacity: .4; -} - -#tracy-bs .tracy-toggle.tracy-collapsed:after { - content: "\A0\25BA"; -} - - -/* dump */ -#tracy-bs .tracy-dump-array, -#tracy-bs .tracy-dump-object { - color: #C22; -} - -#tracy-bs .tracy-dump-string { - color: #35D; -} - -#tracy-bs .tracy-dump-number { - color: #090; -} - -#tracy-bs .tracy-dump-whitespace { - color: #0003; -} - -#tracy-bs .tracy-dump-null, -#tracy-bs .tracy-dump-bool { - color: #850; -} - -#tracy-bs .tracy-dump-visibility, -#tracy-bs .tracy-dump-hash { - font-size: 85%; - color: #998; -} - -#tracy-bs .tracy-dump-indent { - display: none; -} - -#tracy-bs pre.tracy-dump div { - padding-left: 3ex; -} - -#tracy-bs pre.tracy-dump div div { - border-left: 1px solid rgba(0, 0, 0, .1); - margin-left: .5ex; -} - -#tracy-bs .caused { - float: right; - padding: .3em .6em; - background: #df8075; - border-radius: 0 0 0 8px; - white-space: nowrap; -} - -#tracy-bs .caused a { - color: white; -} diff --git a/tracy-2.5.x/src/Tracy/assets/BlueScreen/bluescreen.js b/tracy-2.5.x/src/Tracy/assets/BlueScreen/bluescreen.js deleted file mode 100644 index 584e611b..00000000 --- a/tracy-2.5.x/src/Tracy/assets/BlueScreen/bluescreen.js +++ /dev/null @@ -1,67 +0,0 @@ -/** - * This file is part of the Tracy (https://tracy.nette.org) - */ - -(function(){ - class BlueScreen - { - static init(ajax) { - let blueScreen = document.getElementById('tracy-bs'); - let styles = []; - - for (let i = 0; i < document.styleSheets.length; i++) { - let style = document.styleSheets[i]; - if (!style.ownerNode.classList.contains('tracy-debug')) { - style.oldDisabled = style.disabled; - style.disabled = true; - styles.push(style); - } - } - - document.getElementById('tracy-bs-toggle').addEventListener('tracy-toggle', function() { - let collapsed = this.classList.contains('tracy-collapsed'); - for (let i = 0; i < styles.length; i++) { - styles[i].disabled = collapsed ? styles[i].oldDisabled : true; - } - }); - - if (!ajax) { - document.body.appendChild(blueScreen); - let id = location.href + document.getElementById('tracy-bs-error').textContent; - Tracy.Toggle.persist(blueScreen, sessionStorage.getItem('tracy-toggles-bskey') === id); - sessionStorage.setItem('tracy-toggles-bskey', id); - } - - if (inited) { - return; - } - inited = true; - - // enables toggling via ESC - document.addEventListener('keyup', (e) => { - if (e.keyCode === 27 && !e.shiftKey && !e.altKey && !e.ctrlKey && !e.metaKey) { // ESC - Tracy.Toggle.toggle(document.getElementById('tracy-bs-toggle')); - } - }); - } - - - static loadAjax(content, dumps) { - let ajaxBs = document.getElementById('tracy-bs'); - if (ajaxBs) { - ajaxBs.parentNode.removeChild(ajaxBs); - } - document.body.insertAdjacentHTML('beforeend', content); - ajaxBs = document.getElementById('tracy-bs'); - Tracy.Dumper.init(dumps, ajaxBs); - BlueScreen.init(true); - window.scrollTo(0, 0); - } - } - - let inited; - - - Tracy = window.Tracy || {}; - Tracy.BlueScreen = BlueScreen; -})(); diff --git a/tracy-2.5.x/src/Tracy/assets/BlueScreen/content.phtml b/tracy-2.5.x/src/Tracy/assets/BlueScreen/content.phtml deleted file mode 100644 index 91074494..00000000 --- a/tracy-2.5.x/src/Tracy/assets/BlueScreen/content.phtml +++ /dev/null @@ -1,339 +0,0 @@ -<?php - -/** - * Debugger bluescreen template. - * - * This file is part of the Tracy (https://tracy.nette.org) - * Copyright (c) 2004 David Grudl (https://davidgrudl.com) - * - * @param array $exception - * @param string $messageHtml - * @param array[] $actions - * @param array $info - * @param string $title - * @param string $source - * @param bool $sourceIsUrl - * @param array $lastError - * @param callable $dump - * @return void - */ - -namespace Tracy; - -$code = $exception->getCode() ? ' #' . $exception->getCode() : ''; - -?> -<div id="tracy-bs"> - <a id="tracy-bs-toggle" href="#" class="tracy-toggle"></a> - <div> - <div id="tracy-bs-error" class="panel"> - <?php if ($exception->getMessage()): ?><p><?= Helpers::escapeHtml($title . $code) ?></p><?php endif ?> - - - <h1><span><?= $messageHtml ?: Helpers::escapeHtml($title . $code) ?></span> - <?php foreach ($actions as $item): ?> - <a href="<?= Helpers::escapeHtml($item['link']) ?>"<?= empty($item['external']) ? '' : ' target="_blank" rel="noreferrer noopener"'?>><?= Helpers::escapeHtml($item['label']) ?>►</a> - <?php endforeach ?></h1> - </div> - - <?php if ($prev = $exception->getPrevious()): ?> - <div class="caused"> - <a href="#tracyCaused">Caused by <?= Helpers::escapeHtml(Helpers::getClass($prev)) ?></a> - </div> - <?php endif ?> - - - <?php $ex = $exception; $level = 0; ?> - <?php do { ?> - - <?php if ($level++): ?> - <div class="panel"<?php if ($level === 2) echo ' id="tracyCaused"' ?>> - <h2><a data-tracy-ref="^+" class="tracy-toggle<?= ($collapsed = $level > 2) ? ' tracy-collapsed' : '' ?>">Caused by</a></h2> - - <div class="<?= $collapsed ? 'tracy-collapsed ' : '' ?>inner"> - <div class="panel"> - <h2><?= Helpers::escapeHtml(Helpers::getClass($ex) . ($ex->getCode() ? ' #' . $ex->getCode() : '')) ?></h2> - - <h2><?= Helpers::escapeHtml($ex->getMessage()) ?></h2> - </div> - <?php endif ?> - - - <?php foreach ($this->renderPanels($ex) as $panel): ?> - <div class="panel"> - <h2><a data-tracy-ref="^+" class="tracy-toggle"><?= Helpers::escapeHtml($panel->tab) ?></a></h2> - - <div class="inner"> - <?= $panel->panel ?> - </div></div> - <?php endforeach ?> - - - <?php $stack = $ex->getTrace(); $expanded = null ?> - <?php if ((!$exception instanceof \ErrorException || in_array($exception->getSeverity(), [E_USER_NOTICE, E_USER_WARNING, E_USER_DEPRECATED], true)) && $this->isCollapsed($ex->getFile())) { - foreach ($stack as $key => $row) { - if (isset($row['file']) && !$this->isCollapsed($row['file'])) { $expanded = $key; break; } - } - } ?> - - <div class="panel"> - <h2><a data-tracy-ref="^+" class="tracy-toggle<?= ($collapsed = $expanded !== null) ? ' tracy-collapsed' : '' ?>">Source file</a></h2> - - <div class="<?= $collapsed ? 'tracy-collapsed ' : '' ?>inner"> - <p><b>File:</b> <?= Helpers::editorLink($ex->getFile(), $ex->getLine()) ?></p> - <?php if (is_file($ex->getFile())): ?><?= self::highlightFile($ex->getFile(), $ex->getLine(), 15, $ex instanceof \ErrorException && isset($ex->context) ? $ex->context : null) ?><?php endif ?> - </div></div> - - - <?php if (isset($stack[0]['class']) && $stack[0]['class'] === 'Tracy\Debugger' && ($stack[0]['function'] === 'shutdownHandler' || $stack[0]['function'] === 'errorHandler')) unset($stack[0]) ?> - <?php if ($stack): ?> - <div class="panel"> - <h2><a data-tracy-ref="^+" class="tracy-toggle">Call stack</a></h2> - - <div class="inner"> - <ol> - <?php foreach ($stack as $key => $row): ?> - <li><p> - - <?php if (isset($row['file']) && is_file($row['file'])): ?> - <?= Helpers::editorLink($row['file'], $row['line']) ?> - <?php else: ?> - <i>inner-code</i><?php if (isset($row['line'])) echo ':', $row['line'] ?> - <?php endif ?> - - <?php if (isset($row['file']) && is_file($row['file'])): ?><a data-tracy-ref="^p + .file" class="tracy-toggle<?php if ($expanded !== $key) echo ' tracy-collapsed' ?>">source</a>  <?php endif ?> - - <?php - if (isset($row['object'])) echo "<a data-tracy-ref='^p + .object' class='tracy-toggle tracy-collapsed'>"; - if (isset($row['class'])) echo Helpers::escapeHtml($row['class'] . $row['type']); - if (isset($row['object'])) echo '</a>'; - echo Helpers::escapeHtml($row['function']), '('; - if (!empty($row['args'])): ?><a data-tracy-ref="^p + .args" class="tracy-toggle tracy-collapsed">arguments</a><?php endif ?>) - </p> - - <?php if (isset($row['file']) && is_file($row['file'])): ?> - <div class="<?php if ($expanded !== $key) echo 'tracy-collapsed ' ?>file"><?= self::highlightFile($row['file'], $row['line']) ?></div> - <?php endif ?> - - <?php if (isset($row['object'])): ?> - <div class="tracy-collapsed outer object"><?= $dump($row['object']) ?></div> - <?php endif ?> - - <?php if (!empty($row['args'])): ?> - <div class="tracy-collapsed outer args"> - <table> - <?php - try { - $r = isset($row['class']) ? new \ReflectionMethod($row['class'], $row['function']) : new \ReflectionFunction($row['function']); - $params = $r->getParameters(); - } catch (\Exception $e) { - $params = []; - } - foreach ($row['args'] as $k => $v) { - echo '<tr><th>', Helpers::escapeHtml(isset($params[$k]) ? '$' . $params[$k]->name : "#$k"), '</th><td>'; - echo $dump($v, isset($params[$k]) ? $params[$k]->name : null); - echo "</td></tr>\n"; - } - ?> - </table> - </div> - <?php endif ?> - </li> - <?php endforeach ?> - </ol> - </div></div> - <?php endif ?> - - - <?php if ($ex instanceof \ErrorException && isset($ex->context) && is_array($ex->context)):?> - <div class="panel"> - <h2><a data-tracy-ref="^+" class="tracy-toggle tracy-collapsed">Variables</a></h2> - - <div class="tracy-collapsed inner"> - <div class="outer"> - <table> - <?php - foreach ($ex->context as $k => $v) { - echo '<tr><th>$', Helpers::escapeHtml($k), '</th><td>', $dump($v, $k), "</td></tr>\n"; - } - ?> - </table> - </div> - </div></div> - <?php endif ?> - - <?php } while ($ex = $ex->getPrevious()); ?> - <?php while (--$level) echo '</div></div>' ?> - - - <?php if (count((array) $exception) > count((array) new \Exception)):?> - <div class="panel"> - <h2><a data-tracy-ref="^+" class="tracy-toggle tracy-collapsed">Exception</a></h2> - <div class="tracy-collapsed inner"> - <?= $dump($exception) ?> - </div></div> - <?php endif ?> - - - <?php if ($lastError): ?> - <div class="panel"> - <h2><a data-tracy-ref="^+" class="tracy-toggle tracy-collapsed">Last muted error</a></h2> - <div class="tracy-collapsed inner"> - - <h3><?= Helpers::errorTypeToString($lastError['type']) ?>: <?= Helpers::escapeHtml($lastError['message']) ?></h3> - <?php if (isset($lastError['file']) && is_file($lastError['file'])): ?> - <p><?= Helpers::editorLink($lastError['file'], $lastError['line']) ?></p> - <div><?= self::highlightFile($lastError['file'], $lastError['line']) ?></div> - <?php else: ?> - <p><i>inner-code</i><?php if (isset($lastError['line'])) echo ':', $lastError['line'] ?></p> - <?php endif ?> - - </div></div> - <?php endif ?> - - - <?php $bottomPanels = [] ?> - <?php foreach ($this->renderPanels(null) as $panel): ?> - <?php if (!empty($panel->bottom)) { $bottomPanels[] = $panel; continue; } ?> - <?php $collapsedClass = !isset($panel->collapsed) || $panel->collapsed ? ' tracy-collapsed' : ''; ?> - <div class="panel"> - <h2><a data-tracy-ref="^+" class="tracy-toggle<?= $collapsedClass ?>"><?= Helpers::escapeHtml($panel->tab) ?></a></h2> - - <div class="inner<?= $collapsedClass ?>"> - <?= $panel->panel ?> - </div></div> - <?php endforeach ?> - - - <div class="panel"> - <h2><a data-tracy-ref="^+" class="tracy-toggle tracy-collapsed">Environment</a></h2> - - <div class="tracy-collapsed inner"> - <h3><a data-tracy-ref="^+" class="tracy-toggle">$_SERVER</a></h3> - <div class="outer"> - <table> - <?php - foreach ($_SERVER as $k => $v) echo '<tr><th>', Helpers::escapeHtml($k), '</th><td>', $dump($v, $k), "</td></tr>\n"; - ?> - </table> - </div> - - - <h3><a data-tracy-ref="^+" class="tracy-toggle">$_SESSION</a></h3> - <div class="outer"> - <?php if (empty($_SESSION)):?> - <p><i>empty</i></p> - <?php else: ?> - <table> - <?php - foreach ($_SESSION as $k => $v) echo '<tr><th>', Helpers::escapeHtml($k), '</th><td>', $k === '__NF' ? '<i>Nette Session</i>' : $dump($v, $k), "</td></tr>\n"; - ?> - </table> - <?php endif ?> - </div> - - - <?php if (!empty($_SESSION['__NF']['DATA'])):?> - <h3><a data-tracy-ref="^+" class="tracy-toggle">Nette Session</a></h3> - <div class="outer"> - <table> - <?php - foreach ($_SESSION['__NF']['DATA'] as $k => $v) echo '<tr><th>', Helpers::escapeHtml($k), '</th><td>', $dump($v, $k), "</td></tr>\n"; - ?> - </table> - </div> - <?php endif ?> - - - <?php - $list = get_defined_constants(true); - if (!empty($list['user'])):?> - <h3><a data-tracy-ref="^+" class="tracy-toggle tracy-collapsed">Constants</a></h3> - <div class="outer tracy-collapsed"> - <table> - <?php - foreach ($list['user'] as $k => $v) { - echo '<tr><th>', Helpers::escapeHtml($k), '</th>'; - echo '<td>', $dump($v, $k), "</td></tr>\n"; - } - ?> - </table> - </div> - <?php endif ?> - - - <h3><a data-tracy-ref="^+" class="tracy-toggle tracy-collapsed">Configuration options</a></h3> - <div class="outer tracy-collapsed"> - <?php ob_start(); @phpinfo(INFO_CONFIGURATION | INFO_MODULES); echo preg_replace('#^.+<body>|</body>.+\z#s', '', ob_get_clean()) // @ phpinfo can be disabled ?> - </div> - </div></div> - - - <div class="panel"> - <h2><a data-tracy-ref="^+" class="tracy-toggle tracy-collapsed">HTTP request</a></h2> - - <div class="tracy-collapsed inner"> - <?php if (function_exists('apache_request_headers')): ?> - <h3>Headers</h3> - <div class="outer"> - <table> - <?php - foreach (apache_request_headers() as $k => $v) echo '<tr><th>', Helpers::escapeHtml($k), '</th><td>', Helpers::escapeHtml($v), "</td></tr>\n"; - ?> - </table> - </div> - <?php endif ?> - - - <?php foreach (['_GET', '_POST', '_COOKIE'] as $name): ?> - <h3>$<?= Helpers::escapeHtml($name) ?></h3> - <?php if (empty($GLOBALS[$name])):?> - <p><i>empty</i></p> - <?php else: ?> - <div class="outer"> - <table> - <?php - foreach ($GLOBALS[$name] as $k => $v) echo '<tr><th>', Helpers::escapeHtml($k), '</th><td>', $dump($v, $k), "</td></tr>\n"; - ?> - </table> - </div> - <?php endif ?> - <?php endforeach ?> - </div></div> - - - <div class="panel"> - <h2><a data-tracy-ref="^+" class="tracy-toggle tracy-collapsed">HTTP response</a></h2> - - <div class="tracy-collapsed inner"> - <h3>Headers</h3> - <?php if (headers_list()): ?> - <pre><?php - foreach (headers_list() as $s) echo Helpers::escapeHtml($s), '<br>'; - ?></pre> - <?php else: ?> - <p><i>no headers</i></p> - <?php endif ?> - </div></div> - - - <?php foreach ($bottomPanels as $panel): ?> - <div class="panel"> - <h2><a data-tracy-ref="^+" class="tracy-toggle"><?= Helpers::escapeHtml($panel->tab) ?></a></h2> - - <div class="inner"> - <?= $panel->panel ?> - </div></div> - <?php endforeach ?> - - - <ul> - <li><b><a href="https://nette.org/make-donation?to=tracy" target="_blank" rel="noreferrer noopener">Please support Tracy via a donation</a></b></li> - <li>Report generated at <?= @date('Y/m/d H:i:s') // @ timezone may not be set ?></li> - <li><?php if ($sourceIsUrl): ?><a href="<?= Helpers::escapeHtml($source) ?>"><?php endif ?><?= Helpers::escapeHtml($source) ?><?php if ($sourceIsUrl): ?></a><?php endif ?></li> - <?php foreach ($info as $item): ?><li><?= Helpers::escapeHtml($item) ?></li><?php endforeach ?> - </ul> - - <div id="tracy-bs-logo"><a href="https://tracy.nette.org" rel="noreferrer"></a></div> - </div> -</div> diff --git a/tracy-2.5.x/src/Tracy/assets/BlueScreen/page.phtml b/tracy-2.5.x/src/Tracy/assets/BlueScreen/page.phtml deleted file mode 100644 index ee9b1df6..00000000 --- a/tracy-2.5.x/src/Tracy/assets/BlueScreen/page.phtml +++ /dev/null @@ -1,55 +0,0 @@ -<?php - -/** - * Debugger bluescreen template. - * - * This file is part of the Tracy (https://tracy.nette.org) - * Copyright (c) 2004 David Grudl (https://davidgrudl.com) - * - * @param array $exception - * @param string $title - * @param string $nonce - * @return void - */ - -namespace Tracy; - -$code = $exception->getCode() ? ' #' . $exception->getCode() : ''; -$nonceAttr = $nonce ? ' nonce="' . Helpers::escapeHtml($nonce) . '"' : ''; -?><!DOCTYPE html><!-- "' --></textarea></script></style></pre></xmp></a></iframe></noembed></noframes></noscript></option></select></template>
    '.$columnName.'
    - - - - - - - <?= Helpers::escapeHtml($title . ': ' . $exception->getMessage() . $code) ?> - getPrevious()): ?> - - - - - >document.documentElement.className+=' tracy-js' - - - - - - -> -(function() { - - - -})(); - -> -Tracy && Tracy.Dumper.init(); - -> -Tracy && Tracy.BlueScreen.init(); - - - diff --git a/tracy-2.5.x/src/Tracy/assets/Debugger/error.500.phtml b/tracy-2.5.x/src/Tracy/assets/Debugger/error.500.phtml deleted file mode 100644 index 37105ac5..00000000 --- a/tracy-2.5.x/src/Tracy/assets/Debugger/error.500.phtml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - -Server Error - - - -
    -
    -

    Server Error

    - -

    We're sorry! The server encountered an internal error and - was unable to complete your request. Please try again later.

    - -

    error 500 |
    Tracy is unable to log error.

    -
    -
    - - diff --git a/tracy-2.5.x/src/Tracy/assets/Dumper/dumper.css b/tracy-2.5.x/src/Tracy/assets/Dumper/dumper.css deleted file mode 100644 index 49904dbe..00000000 --- a/tracy-2.5.x/src/Tracy/assets/Dumper/dumper.css +++ /dev/null @@ -1,60 +0,0 @@ -/** - * This file is part of the Tracy (https://tracy.nette.org) - */ - -pre.tracy-dump { - text-align: left; - color: #444; - background: white; -} - -pre.tracy-dump div { - padding-left: 3ex; -} - -pre.tracy-dump div div { - border-left: 1px solid rgba(0, 0, 0, .1); - margin-left: .5ex; -} - -pre.tracy-dump a { - color: #125EAE; - text-decoration: none; -} - -pre.tracy-dump a:hover, -pre.tracy-dump a:focus { - background-color: #125EAE; - color: white; -} - -.tracy-dump-array, -.tracy-dump-object { - color: #C22; -} - -.tracy-dump-string { - color: #35D; -} - -.tracy-dump-number { - color: #090; -} - -.tracy-dump-null, -.tracy-dump-bool { - color: #850; -} - -.tracy-dump-visibility, -.tracy-dump-hash { - font-size: 85%; color: #999; -} - -.tracy-dump-indent { - display: none; -} - -span[data-tracy-href] { - border-bottom: 1px dotted rgba(0, 0, 0, .2); -} diff --git a/tracy-2.5.x/src/Tracy/assets/Dumper/dumper.js b/tracy-2.5.x/src/Tracy/assets/Dumper/dumper.js deleted file mode 100644 index a66ca206..00000000 --- a/tracy-2.5.x/src/Tracy/assets/Dumper/dumper.js +++ /dev/null @@ -1,180 +0,0 @@ -/** - * This file is part of the Tracy (https://tracy.nette.org) - */ - -(function() { - const - COLLAPSE_COUNT = 7, - COLLAPSE_COUNT_TOP = 14; - - class Dumper - { - static init(repository, context) { - if (repository) { - [].forEach.call((context || document).querySelectorAll('.tracy-dump[data-tracy-dump]'), (el) => { - try { - let built = build(JSON.parse(el.getAttribute('data-tracy-dump')), repository, el.classList.contains('tracy-collapsed')); - el.insertBefore(built, el.lastChild); - el.classList.remove('tracy-collapsed'); - el.removeAttribute('data-tracy-dump'); - } catch (e) { - if (!(e instanceof UnknownEntityException)) { - throw e; - } - } - }); - } - - if (Dumper.inited) { - return; - } - Dumper.inited = true; - - // enables & ctrl key - document.documentElement.addEventListener('click', (e) => { - let el; - if (e.ctrlKey && (el = e.target.closest('[data-tracy-href]'))) { - location.href = el.getAttribute('data-tracy-href'); - return false; - } - }); - - Tracy.Toggle.init(); - } - } - - - function build(data, repository, collapsed, parentIds) { - let type = data === null ? 'null' : typeof data, - collapseCount = collapsed === null ? COLLAPSE_COUNT : COLLAPSE_COUNT_TOP; - - if (type === 'null' || type === 'string' || type === 'number' || type === 'boolean') { - data = type === 'string' ? '"' + data + '"' : (data + ''); - return createEl(null, null, [ - createEl( - 'span', - {'class': 'tracy-dump-' + type.replace('ean', '')}, - [data + '\n'] - ) - ]); - - } else if (Array.isArray(data)) { - return buildStruct( - [ - createEl('span', {'class': 'tracy-dump-array'}, ['array']), - ' (' + (data[0] && data.length || '') + ')' - ], - ' [ ... ]', - data[0] === null ? null : data, - collapsed === true || data.length >= collapseCount, - repository, - parentIds - ); - - } else if (type === 'object' && data.number) { - return createEl(null, null, [ - createEl('span', {'class': 'tracy-dump-number'}, [data.number + '\n']) - ]); - - } else if (type === 'object' && data.type) { - return createEl(null, null, [ - createEl('span', null, [data.type + '\n']) - ]); - - } else if (type === 'object') { - let id = data.object || data.resource, - object = repository[id]; - - if (!object) { - throw new UnknownEntityException; - } - parentIds = parentIds || []; - let recursive = parentIds.indexOf(id) > -1; - parentIds.push(id); - - return buildStruct( - [ - createEl('span', { - 'class': data.object ? 'tracy-dump-object' : 'tracy-dump-resource', - title: object.editor ? 'Declared in file ' + object.editor.file + ' on line ' + object.editor.line : null, - 'data-tracy-href': object.editor ? object.editor.url : null - }, [object.name]), - ' ', - createEl('span', {'class': 'tracy-dump-hash'}, ['#' + id]) - ], - ' { ... }', - object.items, - collapsed === true || recursive || (object.items && object.items.length >= collapseCount), - repository, - parentIds - ); - } - } - - - function buildStruct(span, ellipsis, items, collapsed, repository, parentIds) { - let res, toggle, div, handler; - - if (!items || !items.length) { - span.push(!items || items.length ? ellipsis + '\n' : '\n'); - return createEl(null, null, span); - } - - res = createEl(null, null, [ - toggle = createEl('span', {'class': collapsed ? 'tracy-toggle tracy-collapsed' : 'tracy-toggle'}, span), - '\n', - div = createEl('div', {'class': collapsed ? 'tracy-collapsed' : ''}) - ]); - - if (collapsed) { - toggle.addEventListener('tracy-toggle', handler = function() { - toggle.removeEventListener('tracy-toggle', handler); - createItems(div, items, repository, parentIds); - }); - } else { - createItems(div, items, repository, parentIds); - } - return res; - } - - - function createEl(el, attrs, content) { - if (!(el instanceof Node)) { - el = el ? document.createElement(el) : document.createDocumentFragment(); - } - for (let id in attrs || {}) { - if (attrs[id] !== null) { - el.setAttribute(id, attrs[id]); - } - } - content = content || []; - for (let id = 0; id < content.length; id++) { - let child = content[id]; - if (child !== null) { - el.appendChild(child instanceof Node ? child : document.createTextNode(child)); - } - } - return el; - } - - - function createItems(el, items, repository, parentIds) { - for (let i = 0; i < items.length; i++) { - let vis = items[i][2]; - createEl(el, null, [ - createEl('span', {'class': 'tracy-dump-key'}, [items[i][0]]), - vis ? ' ' : null, - vis ? createEl('span', {'class': 'tracy-dump-visibility'}, [vis === 1 ? 'protected' : 'private']) : null, - ' => ', - build(items[i][1], repository, null, parentIds) - ]); - } - } - - - function UnknownEntityException() {} - - - Tracy = window.Tracy || {}; - Tracy.Dumper = Dumper; -})(); diff --git a/tracy-2.5.x/src/Tracy/assets/Toggle/toggle.css b/tracy-2.5.x/src/Tracy/assets/Toggle/toggle.css deleted file mode 100644 index eb985f1c..00000000 --- a/tracy-2.5.x/src/Tracy/assets/Toggle/toggle.css +++ /dev/null @@ -1,29 +0,0 @@ -/** - * This file is part of the Tracy (https://tracy.nette.org) - */ - -.tracy-collapsed { - display: none; -} - -.tracy-toggle.tracy-collapsed { - display: inline; -} - -.tracy-toggle { - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - -khtml-user-select: none; - -ms-user-select: none; - user-select: none; -} - -.tracy-toggle:after { - content: "\A0\25BC"; - opacity: .4; -} - -.tracy-toggle.tracy-collapsed:after { - content: "\A0\25BA"; -} diff --git a/tracy-2.5.x/src/Tracy/assets/Toggle/toggle.js b/tracy-2.5.x/src/Tracy/assets/Toggle/toggle.js deleted file mode 100644 index 77b3b3b9..00000000 --- a/tracy-2.5.x/src/Tracy/assets/Toggle/toggle.js +++ /dev/null @@ -1,108 +0,0 @@ -/** - * This file is part of the Tracy (https://tracy.nette.org) - */ - -(function() { - - // enables or toggling - class Toggle - { - static init() { - document.documentElement.addEventListener('click', (e) => { - let el = e.target.closest('.tracy-toggle'); - if (el && !e.shiftKey && !e.altKey && !e.ctrlKey && !e.metaKey) { - Toggle.toggle(el); - } - }); - Toggle.init = function() {}; - } - - - // changes element visibility - static toggle(el, show) { - let collapsed = el.classList.contains('tracy-collapsed'), - ref = el.getAttribute('data-tracy-ref') || el.getAttribute('href', 2), - dest = el; - - if (typeof show === 'undefined') { - show = collapsed; - } else if (!show === collapsed) { - return; - } - - if (!ref || ref === '#') { - ref = '+'; - } else if (ref.substr(0, 1) === '#') { - dest = document; - } - ref = ref.match(/(\^\s*([^+\s]*)\s*)?(\+\s*(\S*)\s*)?(.*)/); - dest = ref[1] ? dest.parentNode : dest; - dest = ref[2] ? dest.closest(ref[2]) : dest; - dest = ref[3] ? Toggle.nextElement(dest.nextElementSibling, ref[4]) : dest; - dest = ref[5] ? dest.querySelector(ref[5]) : dest; - - el.classList.toggle('tracy-collapsed', !show); - dest.classList.toggle('tracy-collapsed', !show); - - let toggleEvent; - if (typeof window.Event === 'function') { - toggleEvent = new Event('tracy-toggle', {bubbles: true}); - } else { - toggleEvent = document.createEvent('Event'); - toggleEvent.initEvent('tracy-toggle', true, false); - } - el.dispatchEvent(toggleEvent); - } - - - // save & restore toggles - static persist(baseEl, restore) { - let saved = []; - baseEl.addEventListener('tracy-toggle', (e) => { - if (saved.indexOf(e.target) < 0) { - saved.push(e.target); - } - }); - - let toggles = JSON.parse(sessionStorage.getItem('tracy-toggles-' + baseEl.id)); - if (toggles && restore !== false) { - toggles.forEach((item) => { - let el = baseEl; - for (let i in item.path) { - if (!(el = el.children[item.path[i]])) { - return; - } - } - if (el.textContent === item.text) { - Toggle.toggle(el, item.show); - } - }); - } - - window.addEventListener('unload', () => { - toggles = [].map.call(saved, (el) => { - let item = {path: [], text: el.textContent, show: !el.classList.contains('tracy-collapsed')}; - do { - item.path.unshift([].indexOf.call(el.parentNode.children, el)); - el = el.parentNode; - } while (el && el !== baseEl); - return item; - }); - sessionStorage.setItem('tracy-toggles-' + baseEl.id, JSON.stringify(toggles)); - }); - } - - - // finds next matching element - static nextElement(el, selector) { - while (el && selector && !el.matches(selector)) { - el = el.nextElementSibling; - } - return el; - } - } - - - Tracy = window.Tracy || {}; - Tracy.Toggle = Tracy.Toggle || Toggle; -})(); diff --git a/tracy-2.5.x/src/shortcuts.php b/tracy-2.5.x/src/shortcuts.php deleted file mode 100644 index d5b93dd2..00000000 --- a/tracy-2.5.x/src/shortcuts.php +++ /dev/null @@ -1,44 +0,0 @@ -setStub("startBuffering(); -foreach ($iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator(__DIR__ . '/../../src', RecursiveDirectoryIterator::SKIP_DOTS)) as $file) { - echo "adding: {$iterator->getSubPathname()}\n"; - - $s = file_get_contents($file->getPathname()); - if (strpos($s, '@tracySkipLocation') === false) { - $s = php_strip_whitespace($file->getPathname()); - } - - if ($file->getExtension() === 'js') { - $s = compressJs($s); - - } elseif ($file->getExtension() === 'css') { - $s = compressCss($s); - - } elseif ($file->getExtension() === 'phtml') { - $s = preg_replace_callback('#(<(script|style).*(?)(.*)(getSubPathname()] = $s; -} - -$phar->stopBuffering(); -$phar->compressFiles(Phar::GZ); - -echo "OK\n"; diff --git a/tracy-2.5.x/tools/open-in-editor/linux/install.sh b/tracy-2.5.x/tools/open-in-editor/linux/install.sh deleted file mode 100644 index 5a7bfb31..00000000 --- a/tracy-2.5.x/tools/open-in-editor/linux/install.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -# This shell script sets open-editor.sh as handler for editor:// protocol - -matches=0 -while read -r line -do - if [ "editor=" == "${line:0:7}" ]; then - matches=1 - break - fi -done < "open-editor.sh" - -if [ "$matches" == "0" ]; then - echo -e "\e[31;1mError: it seems like you have not set command to run your editor." - echo -e "Before install, set variable \`\$editor\` in file \`open-editor.sh\`.\e[0m" - exit 1 -fi - -# -------------------------------------------------------------- - -echo "[Desktop Entry] -Name=Tracy Open Editor -Exec=tracy-openeditor.sh %u -Terminal=false -NoDisplay=true -Type=Application -MimeType=x-scheme-handler/editor;" > tracy-openeditor.desktop - -chmod +x open-editor.sh -chmod +x tracy-openeditor.desktop - -sudo cp open-editor.sh /usr/bin/tracy-openeditor.sh -sudo xdg-desktop-menu install tracy-openeditor.desktop -sudo update-desktop-database -rm tracy-openeditor.desktop - -echo -e "\e[32;1mDone.\e[0m" diff --git a/tracy-2.5.x/tools/open-in-editor/linux/open-editor.sh b/tracy-2.5.x/tools/open-in-editor/linux/open-editor.sh deleted file mode 100644 index a9754e3d..00000000 --- a/tracy-2.5.x/tools/open-in-editor/linux/open-editor.sh +++ /dev/null @@ -1,101 +0,0 @@ -#!/bin/bash -declare -A mapping - -# -# Configure your editor by setting the $editor variable: -# - -# Visual Studio Code -#editor='code --goto "$FILE":"$LINE"' -# Emacs -#editor='emacs +$LINE "$FILE"' -# gVim -#editor='gvim +$LINE "$FILE"' -# gEdit -#editor='gedit +$LINE "$FILE"' -# Pluma -#editor='pluma +$LINE "$FILE"' - -# -# Optionally configure custom mapping here: -# - -#mapping["/remotepath"]="/localpath" -#mapping["/mnt/d/"]="d:/" - -# -# Please, do not modify the code below. -# - -# Find and return URI parameter value. Or nothing, if the param is missing. -# Arguments: 1) URI, 2) Parameter name. -function get_param { - echo "$1" | sed -n -r "s/.*$2=([^&]*).*/\1/ip" -} - -if [[ -z "$editor" ]]; then - echo "You need to set the \$editor variable in file '`realpath $0`'" - exit -fi - -url=$1 -if [ "${url:0:9}" != "editor://" ]; then - exit -fi - -# Parse action and essential data from the URI. -regex='editor\:\/\/(open|create|fix)\/\?(.*)' -action=`echo $url | sed -r "s/$regex/\1/i"` -uri_params=`echo $url | sed -r "s/$regex/\2/i"` - -file=`get_param $uri_params "file"` -line=`get_param $uri_params "line"` -search=`get_param $uri_params "search"` -replace=`get_param $uri_params "replace"` - -# Debug? -#echo "action '$action'" -#echo "file '$file'" -#echo "line '$line'" -#echo "search '$search'" -#echo "replace '$replace'" - -# Convert URI encoded codes to normal characters (e.g. '%2F' => '/'). -printf -v file "${file//%/\\x}" -# And escape double-quotes. -file=${file//\"/\\\"} - -# Action: Create a file (only if it does not already exist). -if [ "$action" == "create" ] && [[ ! -f "$file" ]]; then - mkdir -p $(dirname "$file") - touch "$file" -fi - -# Action: Fix the file (if the file exists and while creating backup beforehand). -if [ "$action" == "fix" ]; then - - if [[ ! -f "$file" ]]; then - echo "Cannot fix non-existing file '$file'" - exit - fi - - # Backup the original file. - cp $file "$file.bak" - # Search and replace in place - only on the specified line. - sed -i "${line}s/${search}/${replace}/" $file - -fi - -# Apply custom mapping conversion. -for path in "${!mapping[@]}"; do - file="${file//$path/${mapping[$path]}}" -done - -# Format the command according to the selected editor. -command="${editor//\$FILE/$file}" -command="${command//\$LINE/$line}" - -# Debug? -#echo $command - -eval $command diff --git a/tracy-2.5.x/tools/open-in-editor/windows/install.cmd b/tracy-2.5.x/tools/open-in-editor/windows/install.cmd deleted file mode 100644 index ecee89f6..00000000 --- a/tracy-2.5.x/tools/open-in-editor/windows/install.cmd +++ /dev/null @@ -1,8 +0,0 @@ -@echo off -:: This Windows batch file sets open-editor.js as handler for editor:// protocol - -if defined PROCESSOR_ARCHITEW6432 (set reg="%systemroot%\sysnative\reg.exe") else (set reg=reg) - -%reg% ADD HKCR\editor /ve /d "URL:editor Protocol" /f -%reg% ADD HKCR\editor /v "URL Protocol" /d "" /f -%reg% ADD HKCR\editor\shell\open\command /ve /d "wscript \"%~dp0open-editor.js\" \"%%1\"" /f diff --git a/tracy-2.5.x/tools/open-in-editor/windows/open-editor.js b/tracy-2.5.x/tools/open-in-editor/windows/open-editor.js deleted file mode 100644 index 6d17f166..00000000 --- a/tracy-2.5.x/tools/open-in-editor/windows/open-editor.js +++ /dev/null @@ -1,81 +0,0 @@ -var settings = { - - // PhpStorm - // editor: '"C:\\Program Files\\JetBrains\\PhpStorm 2018.1.2\\bin\\phpstorm64.exe" --line %line% "%file%"', - // title: 'PhpStorm', - - // NetBeans - // editor: '"C:\\Program Files\\NetBeans 8.1\\bin\\netbeans.exe" "%file%:%line%" --console suppress', - - // Nusphere PHPEd - // editor: '"C:\\Program Files\\NuSphere\\PhpED\\phped.exe" "%file%" --line=%line%', - - // SciTE - // editor: '"C:\\Program Files\\SciTE\\scite.exe" "-open:%file%" -goto:%line%', - - // EmEditor - // editor: '"C:\\Program Files\\EmEditor\\EmEditor.exe" "%file%" /l %line%', - - // PSPad Editor - // editor: '"C:\\Program Files\\PSPad editor\\PSPad.exe" -%line% "%file%"', - - // gVim - // editor: '"C:\\Program Files\\Vim\\vim73\\gvim.exe" "%file%" +%line%', - - // Sublime Text 2 - // editor: '"C:\\Program Files\\Sublime Text 2\\sublime_text.exe" "%file%:%line%"', - - mappings: { - // '/remotepath': '/localpath' - } -}; - - - -if (!settings.editor) { - WScript.Echo('Create variable "settings.editor" in ' + WScript.ScriptFullName); - WScript.Quit(); -} - -var url = WScript.Arguments(0); -var match = /^editor:\/\/(open|create|fix)\/\?file=([^&]+)&line=(\d+)(?:&search=([^&]*)&replace=([^&]*))?/.exec(url); -if (!match) { - WScript.Echo('Unexpected URI ' + url); - WScript.Quit(); -} -for (var i in match) { - match[i] = decodeURIComponent(match[i]).replace(/\+/g, ' '); -} - -var action = match[1]; -var file = match[2]; -var line = match[3]; -var search = match[4]; -var replace = match[5]; - -var shell = new ActiveXObject('WScript.Shell'); -var fileSystem = new ActiveXObject('Scripting.FileSystemObject'); - -for (var id in settings.mappings) { - if (file.indexOf(id) === 0) { - file = settings.mappings[id] + file.substr(id.length); - break; - } -} - -if (action === 'create' && !fileSystem.FileExists(file)) { - shell.Run('cmd /c mkdir "' + fileSystem.GetParentFolderName(file) + '"', 0, 1); - fileSystem.CreateTextFile(file); - -} else if (action === 'fix') { - var lines = fileSystem.OpenTextFile(file).ReadAll().split('\n'); - lines[line-1] = lines[line-1].replace(search, replace); - fileSystem.OpenTextFile(file, 2).Write(lines.join('\n')); -} - -var command = settings.editor.replace(/%line%/, line).replace(/%file%/, file); -shell.Exec(command); - -if (settings.title) { - shell.AppActivate(settings.title); -} diff --git a/tracy-2.7.x/src/Tracy/Bar/assets/bar.js b/tracy-2.7.x/src/Tracy/Bar/assets/bar.js index 5c266916..f37809cc 100644 --- a/tracy-2.7.x/src/Tracy/Bar/assets/bar.js +++ b/tracy-2.7.x/src/Tracy/Bar/assets/bar.js @@ -147,10 +147,17 @@ } let doc = win.document; - doc.write('' - + '' - + '' - ); + doc.open(); + doc.close(); + let charset = doc.createElement('meta'); + charset.setAttribute('charset', 'utf-8'); + doc.head.appendChild(charset); + doc.body.id = 'tracy-debug'; + let s = doc.createElement('script'); + s.src = baseUrl + '_tracy_bar=js&XDEBUG_SESSION_STOP=1'; + s.async = true; + s.addEventListener('load', function() { Tracy.Dumper.init(); }); + doc.head.appendChild(s); doc.body.innerHTML = '
    ' + this.elem.innerHTML + '
    '; evalScripts(doc.body); if (this.elem.querySelector('h1')) { diff --git a/tracy-2.7.x/src/Tracy/Helpers.php b/tracy-2.7.x/src/Tracy/Helpers.php index 6a4c8bf6..1b7a8f2b 100644 --- a/tracy-2.7.x/src/Tracy/Helpers.php +++ b/tracy-2.7.x/src/Tracy/Helpers.php @@ -313,7 +313,7 @@ public static function isAjax(): bool /** @internal */ public static function getNonce(): ?string { - return preg_match('#^Content-Security-Policy(?:-Report-Only)?:.*\sscript-src\s+(?:[^;]+\s)?\'nonce-([\w+/]+=*)\'#mi', implode("\n", headers_list()), $m) + return preg_match('#^Content-Security-Policy(?:-Report-Only)?:.*\s(?:script-src|script-src-elem)\s+(?:[^;]+\s)?\'nonce-([\w+/]+=*)\'#mi', implode("\n", headers_list()), $m) ? $m[1] : null; } diff --git a/tracy-2.9.x/src/Tracy/Bar/assets/bar.js b/tracy-2.9.x/src/Tracy/Bar/assets/bar.js index 50f36902..acb6acba 100644 --- a/tracy-2.9.x/src/Tracy/Bar/assets/bar.js +++ b/tracy-2.9.x/src/Tracy/Bar/assets/bar.js @@ -152,10 +152,17 @@ class Panel } let doc = win.document; - doc.write('' - + '' - + '' - ); + doc.open(); + doc.close(); + let charset = doc.createElement('meta'); + charset.setAttribute('charset', 'utf-8'); + doc.head.appendChild(charset); + doc.body.id = 'tracy-debug'; + let s = doc.createElement('script'); + s.src = baseUrl + '_tracy_bar=js&XDEBUG_SESSION_STOP=1'; + s.async = true; + s.addEventListener('load', function() { Tracy.Dumper.init(); }); + doc.head.appendChild(s); let meta = this.elem.parentElement.lastElementChild; doc.body.innerHTML = '' diff --git a/tracy-2.9.x/src/Tracy/Helpers.php b/tracy-2.9.x/src/Tracy/Helpers.php index a0f179fb..691f60a3 100644 --- a/tracy-2.9.x/src/Tracy/Helpers.php +++ b/tracy-2.9.x/src/Tracy/Helpers.php @@ -352,7 +352,7 @@ public static function isCli(): bool /** @internal */ public static function getNonce(): ?string { - return preg_match('#^Content-Security-Policy(?:-Report-Only)?:.*\sscript-src\s+(?:[^;]+\s)?\'nonce-([\w+/]+=*)\'#mi', implode("\n", headers_list()), $m) + return preg_match('#^Content-Security-Policy(?:-Report-Only)?:.*\s(?:script-src|script-src-elem)\s+(?:[^;]+\s)?\'nonce-([\w+/]+=*)\'#mi', implode("\n", headers_list()), $m) ? $m[1] : null; }