diff --git a/scripts/celerity_mapper.php b/scripts/celerity_mapper.php index fd24b0eaa4..93867c86ff 100755 --- a/scripts/celerity_mapper.php +++ b/scripts/celerity_mapper.php @@ -166,7 +166,6 @@ $package_spec = array( 'javelin-behavior-maniphest-subpriority-editor', ), 'darkconsole.pkg.js' => array( - 'javelin-behavior-dark-console-ajax', 'javelin-behavior-dark-console', 'javelin-behavior-error-log', ), diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index 6edcdb0feb..da47577120 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -42,6 +42,13 @@ celerity_register_resource_map(array( 'disk' => '/rsrc/image/credit_cards.png', 'type' => 'png', ), + '/rsrc/image/darkload.gif' => + array( + 'hash' => '3a52cb7145d6e70f461fed21273117f2', + 'uri' => '/res/3a52cb71/rsrc/image/darkload.gif', + 'disk' => '/rsrc/image/darkload.gif', + 'type' => 'gif', + ), '/rsrc/image/divot.png' => array( 'hash' => '3be267bd11ea375bf68e808893718e0e', @@ -598,7 +605,7 @@ celerity_register_resource_map(array( ), 'aphront-dark-console-css' => array( - 'uri' => '/res/1e1f78d4/rsrc/css/aphront/dark-console.css', + 'uri' => '/res/63841304/rsrc/css/aphront/dark-console.css', 'type' => 'css', 'requires' => array( @@ -1057,7 +1064,7 @@ celerity_register_resource_map(array( ), 'javelin-behavior-aphront-form-disable-on-submit' => array( - 'uri' => '/res/ca54e8b9/rsrc/js/application/core/behavior-form.js', + 'uri' => '/res/70fd43fd/rsrc/js/application/core/behavior-form.js', 'type' => 'js', 'requires' => array( @@ -1145,7 +1152,7 @@ celerity_register_resource_map(array( ), 'javelin-behavior-dark-console' => array( - 'uri' => '/res/aa6f8a71/rsrc/js/application/core/behavior-dark-console.js', + 'uri' => '/res/c3e8a3d8/rsrc/js/application/core/behavior-dark-console.js', 'type' => 'js', 'requires' => array( @@ -1155,21 +1162,9 @@ celerity_register_resource_map(array( 3 => 'javelin-dom', 4 => 'javelin-request', 5 => 'phabricator-keyboard-shortcut', - 6 => 'javelin-behavior-dark-console-ajax', ), 'disk' => '/rsrc/js/application/core/behavior-dark-console.js', ), - 'javelin-behavior-dark-console-ajax' => - array( - 'uri' => '/res/ac3ab63a/rsrc/js/application/core/behavior-dark-console-ajax.js', - 'type' => 'js', - 'requires' => - array( - 0 => 'javelin-behavior', - 1 => 'javelin-dom', - ), - 'disk' => '/rsrc/js/application/core/behavior-dark-console-ajax.js', - ), 'javelin-behavior-device' => array( 'uri' => '/res/a10b851b/rsrc/js/application/core/behavior-device.js', @@ -1493,7 +1488,7 @@ celerity_register_resource_map(array( ), 'javelin-behavior-lightbox-attachments' => array( - 'uri' => '/res/5efba371/rsrc/js/application/core/behavior-lightbox-attachments.js', + 'uri' => '/res/08f5e202/rsrc/js/application/core/behavior-lightbox-attachments.js', 'type' => 'js', 'requires' => array( @@ -1986,7 +1981,7 @@ celerity_register_resource_map(array( ), 'javelin-dom' => array( - 'uri' => '/res/2826c532/rsrc/js/javelin/lib/DOM.js', + 'uri' => '/res/459f3c08/rsrc/js/javelin/lib/DOM.js', 'type' => 'js', 'requires' => array( @@ -3393,7 +3388,7 @@ celerity_register_resource_map(array( 'uri' => '/res/pkg/023adc14/core.pkg.css', 'type' => 'css', ), - '66dca903' => + 'b3c1b6e7' => array( 'name' => 'core.pkg.js', 'symbols' => @@ -3432,19 +3427,18 @@ celerity_register_resource_map(array( 31 => 'javelin-behavior-global-drag-and-drop', 32 => 'javelin-behavior-phabricator-home-reveal-tiles', ), - 'uri' => '/res/pkg/66dca903/core.pkg.js', + 'uri' => '/res/pkg/b3c1b6e7/core.pkg.js', 'type' => 'js', ), - '8edbada5' => + '032118cf' => array( 'name' => 'darkconsole.pkg.js', 'symbols' => array( - 0 => 'javelin-behavior-dark-console-ajax', - 1 => 'javelin-behavior-dark-console', - 2 => 'javelin-behavior-error-log', + 0 => 'javelin-behavior-dark-console', + 1 => 'javelin-behavior-error-log', ), - 'uri' => '/res/pkg/8edbada5/darkconsole.pkg.js', + 'uri' => '/res/pkg/032118cf/darkconsole.pkg.js', 'type' => 'js', ), 'ec01d039' => @@ -3521,7 +3515,7 @@ celerity_register_resource_map(array( 'uri' => '/res/pkg/f96657b8/diffusion.pkg.js', 'type' => 'js', ), - 'fbeded59' => + '1c6f020b' => array( 'name' => 'javelin.pkg.js', 'symbols' => @@ -3546,7 +3540,7 @@ celerity_register_resource_map(array( 17 => 'javelin-typeahead-ondemand-source', 18 => 'javelin-tokenizer', ), - 'uri' => '/res/pkg/fbeded59/javelin.pkg.js', + 'uri' => '/res/pkg/1c6f020b/javelin.pkg.js', 'type' => 'js', ), 'e30a3fa8' => @@ -3608,18 +3602,17 @@ celerity_register_resource_map(array( 'diffusion-icons-css' => 'c8ce2d88', 'global-drag-and-drop-css' => '023adc14', 'inline-comment-summary-css' => 'ec01d039', - 'javelin-aphlict' => '66dca903', - 'javelin-behavior' => 'fbeded59', - 'javelin-behavior-aphlict-dropdown' => '66dca903', - 'javelin-behavior-aphlict-listen' => '66dca903', - 'javelin-behavior-aphront-basic-tokenizer' => '66dca903', + 'javelin-aphlict' => 'b3c1b6e7', + 'javelin-behavior' => '1c6f020b', + 'javelin-behavior-aphlict-dropdown' => 'b3c1b6e7', + 'javelin-behavior-aphlict-listen' => 'b3c1b6e7', + 'javelin-behavior-aphront-basic-tokenizer' => 'b3c1b6e7', 'javelin-behavior-aphront-drag-and-drop' => '310cd201', 'javelin-behavior-aphront-drag-and-drop-textarea' => '310cd201', - 'javelin-behavior-aphront-form-disable-on-submit' => '66dca903', + 'javelin-behavior-aphront-form-disable-on-submit' => 'b3c1b6e7', 'javelin-behavior-audit-preview' => 'f96657b8', - 'javelin-behavior-dark-console' => '8edbada5', - 'javelin-behavior-dark-console-ajax' => '8edbada5', - 'javelin-behavior-device' => '66dca903', + 'javelin-behavior-dark-console' => '032118cf', + 'javelin-behavior-device' => 'b3c1b6e7', 'javelin-behavior-differential-accept-with-errors' => '310cd201', 'javelin-behavior-differential-add-reviewers-and-ccs' => '310cd201', 'javelin-behavior-differential-comment-jump' => '310cd201', @@ -3634,84 +3627,84 @@ celerity_register_resource_map(array( 'javelin-behavior-differential-user-select' => '310cd201', 'javelin-behavior-diffusion-commit-graph' => 'f96657b8', 'javelin-behavior-diffusion-pull-lastmodified' => 'f96657b8', - 'javelin-behavior-error-log' => '8edbada5', - 'javelin-behavior-global-drag-and-drop' => '66dca903', - 'javelin-behavior-konami' => '66dca903', - 'javelin-behavior-lightbox-attachments' => '66dca903', + 'javelin-behavior-error-log' => '032118cf', + 'javelin-behavior-global-drag-and-drop' => 'b3c1b6e7', + 'javelin-behavior-konami' => 'b3c1b6e7', + 'javelin-behavior-lightbox-attachments' => 'b3c1b6e7', 'javelin-behavior-maniphest-batch-selector' => '7707de41', 'javelin-behavior-maniphest-subpriority-editor' => '7707de41', 'javelin-behavior-maniphest-transaction-controls' => '7707de41', 'javelin-behavior-maniphest-transaction-expand' => '7707de41', 'javelin-behavior-maniphest-transaction-preview' => '7707de41', - 'javelin-behavior-phabricator-active-nav' => '66dca903', - 'javelin-behavior-phabricator-autofocus' => '66dca903', - 'javelin-behavior-phabricator-home-reveal-tiles' => '66dca903', - 'javelin-behavior-phabricator-keyboard-shortcuts' => '66dca903', - 'javelin-behavior-phabricator-nav' => '66dca903', + 'javelin-behavior-phabricator-active-nav' => 'b3c1b6e7', + 'javelin-behavior-phabricator-autofocus' => 'b3c1b6e7', + 'javelin-behavior-phabricator-home-reveal-tiles' => 'b3c1b6e7', + 'javelin-behavior-phabricator-keyboard-shortcuts' => 'b3c1b6e7', + 'javelin-behavior-phabricator-nav' => 'b3c1b6e7', 'javelin-behavior-phabricator-object-selector' => '310cd201', - 'javelin-behavior-phabricator-oncopy' => '66dca903', - 'javelin-behavior-phabricator-remarkup-assist' => '66dca903', - 'javelin-behavior-phabricator-search-typeahead' => '66dca903', - 'javelin-behavior-phabricator-tooltips' => '66dca903', - 'javelin-behavior-phabricator-watch-anchor' => '66dca903', - 'javelin-behavior-refresh-csrf' => '66dca903', + 'javelin-behavior-phabricator-oncopy' => 'b3c1b6e7', + 'javelin-behavior-phabricator-remarkup-assist' => 'b3c1b6e7', + 'javelin-behavior-phabricator-search-typeahead' => 'b3c1b6e7', + 'javelin-behavior-phabricator-tooltips' => 'b3c1b6e7', + 'javelin-behavior-phabricator-watch-anchor' => 'b3c1b6e7', + 'javelin-behavior-refresh-csrf' => 'b3c1b6e7', 'javelin-behavior-repository-crossreference' => '310cd201', - 'javelin-behavior-toggle-class' => '66dca903', - 'javelin-behavior-workflow' => '66dca903', - 'javelin-dom' => 'fbeded59', - 'javelin-event' => 'fbeded59', - 'javelin-install' => 'fbeded59', - 'javelin-json' => 'fbeded59', - 'javelin-mask' => 'fbeded59', - 'javelin-request' => 'fbeded59', - 'javelin-resource' => 'fbeded59', - 'javelin-stratcom' => 'fbeded59', - 'javelin-tokenizer' => 'fbeded59', - 'javelin-typeahead' => 'fbeded59', - 'javelin-typeahead-normalizer' => 'fbeded59', - 'javelin-typeahead-ondemand-source' => 'fbeded59', - 'javelin-typeahead-preloaded-source' => 'fbeded59', - 'javelin-typeahead-source' => 'fbeded59', - 'javelin-uri' => 'fbeded59', - 'javelin-util' => 'fbeded59', - 'javelin-vector' => 'fbeded59', - 'javelin-workflow' => 'fbeded59', + 'javelin-behavior-toggle-class' => 'b3c1b6e7', + 'javelin-behavior-workflow' => 'b3c1b6e7', + 'javelin-dom' => '1c6f020b', + 'javelin-event' => '1c6f020b', + 'javelin-install' => '1c6f020b', + 'javelin-json' => '1c6f020b', + 'javelin-mask' => '1c6f020b', + 'javelin-request' => '1c6f020b', + 'javelin-resource' => '1c6f020b', + 'javelin-stratcom' => '1c6f020b', + 'javelin-tokenizer' => '1c6f020b', + 'javelin-typeahead' => '1c6f020b', + 'javelin-typeahead-normalizer' => '1c6f020b', + 'javelin-typeahead-ondemand-source' => '1c6f020b', + 'javelin-typeahead-preloaded-source' => '1c6f020b', + 'javelin-typeahead-source' => '1c6f020b', + 'javelin-uri' => '1c6f020b', + 'javelin-util' => '1c6f020b', + 'javelin-vector' => '1c6f020b', + 'javelin-workflow' => '1c6f020b', 'lightbox-attachment-css' => '023adc14', 'maniphest-task-summary-css' => 'e30a3fa8', 'maniphest-transaction-detail-css' => 'e30a3fa8', - 'phabricator-busy' => '66dca903', + 'phabricator-busy' => 'b3c1b6e7', 'phabricator-content-source-view-css' => 'ec01d039', 'phabricator-core-buttons-css' => '023adc14', 'phabricator-core-css' => '023adc14', 'phabricator-crumbs-view-css' => '023adc14', 'phabricator-directory-css' => '023adc14', 'phabricator-drag-and-drop-file-upload' => '310cd201', - 'phabricator-dropdown-menu' => '66dca903', - 'phabricator-file-upload' => '66dca903', + 'phabricator-dropdown-menu' => 'b3c1b6e7', + 'phabricator-file-upload' => 'b3c1b6e7', 'phabricator-filetree-view-css' => '023adc14', 'phabricator-flag-css' => '023adc14', 'phabricator-form-view-css' => '023adc14', 'phabricator-header-view-css' => '023adc14', 'phabricator-jump-nav' => '023adc14', - 'phabricator-keyboard-shortcut' => '66dca903', - 'phabricator-keyboard-shortcut-manager' => '66dca903', + 'phabricator-keyboard-shortcut' => 'b3c1b6e7', + 'phabricator-keyboard-shortcut-manager' => 'b3c1b6e7', 'phabricator-main-menu-view' => '023adc14', - 'phabricator-menu-item' => '66dca903', + 'phabricator-menu-item' => 'b3c1b6e7', 'phabricator-nav-view-css' => '023adc14', - 'phabricator-notification' => '66dca903', + 'phabricator-notification' => 'b3c1b6e7', 'phabricator-notification-css' => '023adc14', 'phabricator-notification-menu-css' => '023adc14', 'phabricator-object-item-list-view-css' => '023adc14', 'phabricator-object-selector-css' => 'ec01d039', - 'phabricator-paste-file-upload' => '66dca903', - 'phabricator-prefab' => '66dca903', + 'phabricator-paste-file-upload' => 'b3c1b6e7', + 'phabricator-prefab' => 'b3c1b6e7', 'phabricator-project-tag-css' => 'e30a3fa8', 'phabricator-remarkup-css' => '023adc14', 'phabricator-shaped-request' => '310cd201', 'phabricator-side-menu-view-css' => '023adc14', 'phabricator-standard-page-view' => '023adc14', - 'phabricator-textareautils' => '66dca903', - 'phabricator-tooltip' => '66dca903', + 'phabricator-textareautils' => 'b3c1b6e7', + 'phabricator-tooltip' => 'b3c1b6e7', 'phabricator-transaction-view-css' => '023adc14', 'phabricator-zindex-css' => '023adc14', 'sprite-apps-large-css' => '023adc14', diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 5090f256e3..c5474942ed 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -218,6 +218,7 @@ phutil_register_library_map(array( 'ConpherenceViewController' => 'applications/conpherence/controller/ConpherenceViewController.php', 'DarkConsoleController' => 'aphront/console/DarkConsoleController.php', 'DarkConsoleCore' => 'aphront/console/DarkConsoleCore.php', + 'DarkConsoleDataController' => 'aphront/console/DarkConsoleDataController.php', 'DarkConsoleErrorLogPlugin' => 'aphront/console/plugin/DarkConsoleErrorLogPlugin.php', 'DarkConsoleErrorLogPluginAPI' => 'aphront/console/plugin/errorlog/DarkConsoleErrorLogPluginAPI.php', 'DarkConsoleEventPlugin' => 'aphront/console/plugin/DarkConsoleEventPlugin.php', @@ -1693,6 +1694,7 @@ phutil_register_library_map(array( 'ConpherenceUpdateController' => 'ConpherenceController', 'ConpherenceViewController' => 'ConpherenceController', 'DarkConsoleController' => 'PhabricatorController', + 'DarkConsoleDataController' => 'PhabricatorController', 'DarkConsoleErrorLogPlugin' => 'DarkConsolePlugin', 'DarkConsoleEventPlugin' => 'DarkConsolePlugin', 'DarkConsoleEventPluginAPI' => 'PhutilEventListener', diff --git a/src/aphront/configuration/AphrontDefaultApplicationConfiguration.php b/src/aphront/configuration/AphrontDefaultApplicationConfiguration.php index 6d17506831..764df9f141 100644 --- a/src/aphront/configuration/AphrontDefaultApplicationConfiguration.php +++ b/src/aphront/configuration/AphrontDefaultApplicationConfiguration.php @@ -73,7 +73,10 @@ class AphrontDefaultApplicationConfiguration 'profile/(?P[^/]+)/' => 'PhabricatorXHProfProfileController', ), - '/~/' => 'DarkConsoleController', + '/~/' => array( + '' => 'DarkConsoleController', + 'data/(?P[^/]+)/' => 'DarkConsoleDataController', + ), '/search/' => array( '' => 'PhabricatorSearchController', diff --git a/src/aphront/console/DarkConsoleCore.php b/src/aphront/console/DarkConsoleCore.php index 2a60c24bd1..90c5ec3ca8 100644 --- a/src/aphront/console/DarkConsoleCore.php +++ b/src/aphront/console/DarkConsoleCore.php @@ -5,57 +5,32 @@ */ final class DarkConsoleCore { - const PLUGIN_ERRORLOG = 'ErrorLog'; - const PLUGIN_SERVICES = 'Services'; - const PLUGIN_EVENT = 'Event'; - const PLUGIN_XHPROF = 'XHProf'; - const PLUGIN_REQUEST = 'Request'; - - public static function getPlugins() { - return array( - self::PLUGIN_ERRORLOG, - self::PLUGIN_REQUEST, - self::PLUGIN_SERVICES, - self::PLUGIN_EVENT, - self::PLUGIN_XHPROF, - ); - } - private $plugins = array(); - private $settings; - private $coredata; - - public function getPlugin($plugin_name) { - return idx($this->plugins, $plugin_name); - } + const STORAGE_VERSION = 1; public function __construct() { - foreach (self::getPlugins() as $plugin_name) { - $plugin = self::newPlugin($plugin_name); - if ($plugin->isPermanent() || !isset($disabled[$plugin_name])) { - if ($plugin->shouldStartup()) { - $plugin->didStartup(); - $plugin->setConsoleCore($this); - $this->plugins[$plugin_name] = $plugin; - } + $symbols = id(new PhutilSymbolLoader()) + ->setType('class') + ->setAncestorClass('DarkConsolePlugin') + ->selectAndLoadSymbols(); + + foreach ($symbols as $symbol) { + $plugin = newv($symbol['name'], array()); + if (!$plugin->shouldStartup()) { + continue; } + $plugin->setConsoleCore($this); + $plugin->didStartup(); + $this->plugins[$symbol['name']] = $plugin; } } - public static function newPlugin($plugin) { - $class = 'DarkConsole'.$plugin.'Plugin'; - return newv($class, array()); - } - - public function getEnabledPlugins() { + public function getPlugins() { return $this->plugins; } - public function render(AphrontRequest $request) { - - $user = $request->getUser(); - - $plugins = $this->getEnabledPlugins(); + public function getKey(AphrontRequest $request) { + $plugins = $this->getPlugins(); foreach ($plugins as $plugin) { $plugin->setRequest($request); @@ -70,128 +45,58 @@ final class DarkConsoleCore { $plugin->setData($plugin->generateData()); } - $selected = $user->getConsoleTab(); - $visible = $user->getConsoleVisible(); + $plugins = msort($plugins, 'getOrderKey'); - if (!isset($plugins[$selected])) { - $selected = head_key($plugins); - } + $key = Filesystem::readRandomCharacters(24); $tabs = array(); - foreach ($plugins as $key => $plugin) { - $tabs[$key] = array( + $data = array(); + foreach ($plugins as $plugin) { + $class = get_class($plugin); + $tabs[] = array( + 'class' => $class, 'name' => $plugin->getName(), - 'panel' => $plugin->render(), + 'color' => $plugin->getColor(), ); + $data[$class] = $plugin->getData(); } - $tabs_markup = array(); - $panel_markup = array(); - foreach ($tabs as $key => $data) { - $is_selected = ($key == $selected); - if ($is_selected) { - $style = null; - $tabclass = 'dark-console-tab-selected'; - } else { - $style = 'display: none;'; - $tabclass = null; - } + $storage = array( + 'vers' => self::STORAGE_VERSION, + 'tabs' => $tabs, + 'data' => $data, + 'user' => $request->getUser() + ? $request->getUser()->getPHID() + : null, + ); - $tabs_markup[] = javelin_tag( - 'a', - array( - 'class' => "dark-console-tab {$tabclass}", - 'sigil' => 'dark-console-tab', - 'id' => 'dark-console-tab-'.$key, - ), - (string)$data['name']); + $cache = new PhabricatorKeyValueDatabaseCache(); + $cache = new PhutilKeyValueCacheProfiler($cache); + $cache->setProfiler(PhutilServiceProfiler::getInstance()); - $panel_markup[] = javelin_render_tag( - 'div', - array( - 'class' => 'dark-console-panel dark-console-panel-'.$key, - 'style' => $style, - 'sigil' => 'dark-console-panel', - ), - (string)$data['panel']); - } - - $console = javelin_render_tag( - 'table', + $cache->setKeys( array( - 'class' => 'dark-console', - 'sigil' => 'dark-console', - 'style' => $visible ? '' : 'display: none;', + 'darkconsole:'.$key => json_encode($storage), ), - ''. - ''. - implode("\n", $tabs_markup). - ''. - ''.implode("\n", $panel_markup).''. - ''); + $ttl = (60 * 60 * 6)); - if (!empty($_COOKIE['phsid'])) { - $console = str_replace( - $_COOKIE['phsid'], - phutil_escape_html(''), - $console); - } - - if ($request->isAjax()) { - - // for ajax this HTML gets updated on the client - $request_history = null; - - } else { - - $request_table_header = - '
'; - - $rows = array(); - - $table = new AphrontTableView($rows); - $table->setHeaders( - array( - 'Sequence', - 'Type', - 'URI', - )); - $table->setColumnClasses( - array( - '', - '', - 'wide', - )); - - $request_table = $request_table_header . $table->render(); - $request_history = javelin_render_tag( - 'table', - array( - 'class' => 'dark-console dark-console-request-log', - 'sigil' => 'dark-console-request-log', - 'style' => $visible ? '' : 'display: none;', - ), - ''. - ''. - phutil_tag( - 'a', - array( - 'class' => 'dark-console-tab dark-console-tab-selected', - ), - 'Request Log'). - ''. - ''. - javelin_render_tag( - 'div', - array( - 'class' => 'dark-console-panel dark-console-panel-RequestLog', - ), - $request_table). - ''. - ''); - } - - return "\n\n\n\n".$console.$request_history."\n\n\n\n"; + return $key; } + + public function render(AphrontRequest $request) { + $user = $request->getUser(); + $visible = $user ? $user->getConsoleVisible() : true; + + return javelin_tag( + 'div', + array( + 'id' => 'darkconsole', + 'class' => 'dark-console', + 'style' => $visible ? '' : 'display: none;', + 'data-console-key' => $this->getKey($request), + ), + ''); + } + } diff --git a/src/aphront/console/DarkConsoleDataController.php b/src/aphront/console/DarkConsoleDataController.php new file mode 100644 index 0000000000..e5f1cd9e84 --- /dev/null +++ b/src/aphront/console/DarkConsoleDataController.php @@ -0,0 +1,69 @@ +key = $data['key']; + } + + public function processRequest() { + $request = $this->getRequest(); + $user = $request->getUser(); + + $cache = new PhabricatorKeyValueDatabaseCache(); + $cache = new PhutilKeyValueCacheProfiler($cache); + $cache->setProfiler(PhutilServiceProfiler::getInstance()); + + $result = $cache->getKey('darkconsole:'.$this->key); + if (!$result) { + return new Aphront400Response(); + } + + $result = json_decode($result, true); + + if (!is_array($result)) { + return new Aphront400Response(); + } + + if ($result['vers'] != DarkConsoleCore::STORAGE_VERSION) { + return new Aphront400Response(); + } + + if ($result['user'] != $user->getPHID()) { + return new Aphront400Response(); + } + + $output = array(); + $output['tabs'] = $result['tabs']; + $output['panel'] = array(); + + foreach ($result['data'] as $class => $data) { + try { + $obj = newv($class, array()); + $obj->setData($data); + $obj->setRequest($request); + + $panel = $obj->renderPanel(); + + if (!empty($_COOKIE['phsid'])) { + $panel = str_replace( + $_COOKIE['phsid'], + '(session-key)', + $panel); + } + + $output['panel'][$class] = $panel; + } catch (Exception $ex) { + $output['panel'][$class] = 'error'; + } + } + + return id(new AphrontAjaxResponse())->setContent($output); + } + +} diff --git a/src/aphront/console/plugin/DarkConsoleErrorLogPlugin.php b/src/aphront/console/plugin/DarkConsoleErrorLogPlugin.php index fae5ff2cb1..98df6d113c 100644 --- a/src/aphront/console/plugin/DarkConsoleErrorLogPlugin.php +++ b/src/aphront/console/plugin/DarkConsoleErrorLogPlugin.php @@ -7,29 +7,32 @@ final class DarkConsoleErrorLogPlugin extends DarkConsolePlugin { public function getName() { $count = count($this->getData()); - if ($count) { - return - ' '. - "Error Log ({$count})"; + return pht('Error Log (%d)', $count); } - - return 'Error Log'; + return pht('Error Log'); } + public function getOrder() { + return 0; + } + + public function getColor() { + if (count($this->getData())) { + return '#ff0000'; + } + return null; + } public function getDescription() { - return 'Shows errors and warnings.'; + return pht('Shows errors and warnings.'); } - public function generateData() { return DarkConsoleErrorLogPluginAPI::getErrors(); } - - public function render() { - + public function renderPanel() { $data = $this->getData(); $rows = array(); diff --git a/src/aphront/console/plugin/DarkConsoleEventPlugin.php b/src/aphront/console/plugin/DarkConsoleEventPlugin.php index d4bd7dcc3b..8a855bb3f8 100644 --- a/src/aphront/console/plugin/DarkConsoleEventPlugin.php +++ b/src/aphront/console/plugin/DarkConsoleEventPlugin.php @@ -37,7 +37,7 @@ final class DarkConsoleEventPlugin extends DarkConsolePlugin { ); } - public function render() { + public function renderPanel() { $data = $this->getData(); $out = array(); diff --git a/src/aphront/console/plugin/DarkConsolePlugin.php b/src/aphront/console/plugin/DarkConsolePlugin.php index 2499cfbc7a..20a93199c0 100644 --- a/src/aphront/console/plugin/DarkConsolePlugin.php +++ b/src/aphront/console/plugin/DarkConsolePlugin.php @@ -11,12 +11,27 @@ abstract class DarkConsolePlugin { abstract public function getName(); abstract public function getDescription(); - abstract public function render(); + abstract public function renderPanel(); public function __construct() { } + public function getColor() { + return null; + } + + final public function getOrderKey() { + return sprintf( + '%09d%s', + (int)(999999999 * $this->getOrder()), + $this->getName()); + } + + public function getOrder() { + return 1.0; + } + public function setConsoleCore(DarkConsoleCore $core) { $this->core = $core; return $this; @@ -52,10 +67,6 @@ abstract class DarkConsolePlugin { return $this->getRequest()->getRequestURI(); } - public function isPermanent() { - return false; - } - public function shouldStartup() { return true; } diff --git a/src/aphront/console/plugin/DarkConsoleRequestPlugin.php b/src/aphront/console/plugin/DarkConsoleRequestPlugin.php index e252f41e8a..7a59df8493 100644 --- a/src/aphront/console/plugin/DarkConsoleRequestPlugin.php +++ b/src/aphront/console/plugin/DarkConsoleRequestPlugin.php @@ -20,8 +20,7 @@ final class DarkConsoleRequestPlugin extends DarkConsolePlugin { ); } - public function render() { - + public function renderPanel() { $data = $this->getData(); $sections = array( diff --git a/src/aphront/console/plugin/DarkConsoleServicesPlugin.php b/src/aphront/console/plugin/DarkConsoleServicesPlugin.php index 29e7c50d21..094abea57d 100644 --- a/src/aphront/console/plugin/DarkConsoleServicesPlugin.php +++ b/src/aphront/console/plugin/DarkConsoleServicesPlugin.php @@ -136,11 +136,16 @@ final class DarkConsoleServicesPlugin extends DarkConsolePlugin { 'start' => PhabricatorStartup::getStartTime(), 'end' => microtime(true), 'log' => $log, + 'analyzeURI' => (string)$this + ->getRequestURI() + ->alter('__analyze__', true), + 'didAnalyze' => isset($_REQUEST['__analyze__']), ); } - public function render() { + public function renderPanel() { $data = $this->getData(); + $log = $data['log']; $results = array(); @@ -149,8 +154,8 @@ final class DarkConsoleServicesPlugin extends DarkConsolePlugin { phutil_tag( 'a', array( - 'href' => $this->getRequestURI()->alter('__analyze__', true), - 'class' => isset($_REQUEST['__analyze__']) + 'href' => $data['analyzeURI'], + 'class' => $data['didAnalyze'] ? 'disabled button' : 'green button', ), diff --git a/src/aphront/console/plugin/DarkConsoleXHProfPlugin.php b/src/aphront/console/plugin/DarkConsoleXHProfPlugin.php index 9368cb7e66..4574056deb 100644 --- a/src/aphront/console/plugin/DarkConsoleXHProfPlugin.php +++ b/src/aphront/console/plugin/DarkConsoleXHProfPlugin.php @@ -8,28 +8,40 @@ final class DarkConsoleXHProfPlugin extends DarkConsolePlugin { protected $xhprofID; public function getName() { - $run = $this->getData(); - - if ($run) { - return ' XHProf'; - } - return 'XHProf'; } + public function getColor() { + $data = $this->getData(); + if ($data['xhprofID']) { + return '#ff00ff'; + } + return null; + } + public function getDescription() { return 'Provides detailed PHP profiling information through XHProf.'; } public function generateData() { - return $this->xhprofID; + return array( + 'xhprofID' => $this->xhprofID, + 'profileURI' => (string)$this + ->getRequestURI() + ->alter('__profile__', 'page'), + ); } public function getXHProfRunID() { return $this->xhprofID; } - public function render() { + public function renderPanel() { + $data = $this->getData(); + + $run = $data['xhprofID']; + $profile_uri = $data['profileURI']; + if (!DarkConsoleXHProfPluginAPI::isProfilerAvailable()) { $href = PhabricatorEnv::getDoclink('article/Installation_Guide.html'); $install_guide = phutil_tag( @@ -49,14 +61,12 @@ final class DarkConsoleXHProfPlugin extends DarkConsolePlugin { $result = array(); - $run = $this->getXHProfRunID(); - $header = '
'. phutil_tag( 'a', array( - 'href' => $this->getRequestURI()->alter('__profile__', 'page'), + 'href' => $profile_uri, 'class' => $run ? 'disabled button' : 'green button', diff --git a/src/aphront/response/AphrontAjaxResponse.php b/src/aphront/response/AphrontAjaxResponse.php index ff2dc2a9de..0b982000f7 100644 --- a/src/aphront/response/AphrontAjaxResponse.php +++ b/src/aphront/response/AphrontAjaxResponse.php @@ -38,10 +38,10 @@ final class AphrontAjaxResponse extends AphrontResponse { $console = $this->getConsole(); if ($console) { Javelin::initBehavior( - 'dark-console-ajax', + 'dark-console', array( - 'console' => $console->render($this->getRequest()), - 'uri' => (string) $this->getRequest()->getRequestURI(), + 'uri' => (string)$this->getRequest()->getRequestURI(), + 'key' => $console->getKey($this->getRequest()), )); } diff --git a/src/view/page/PhabricatorStandardPageView.php b/src/view/page/PhabricatorStandardPageView.php index 2e44c4af3d..e2db6f8d34 100644 --- a/src/view/page/PhabricatorStandardPageView.php +++ b/src/view/page/PhabricatorStandardPageView.php @@ -164,8 +164,9 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView { Javelin::initBehavior( 'dark-console', array( - 'uri' => '/~/', - 'request_uri' => $request ? (string) $request->getRequestURI() : '/', + 'uri' => $request ? (string)$request->getRequestURI() : '?', + 'selected' => $user ? $user->getConsoleTab() : null, + 'visible' => $user ? (int)$user->getConsoleVisible() : true, )); // Change this to initBehavior when there is some behavior to initialize @@ -225,13 +226,15 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView { } protected function willSendResponse($response) { + $request = $this->getRequest(); $response = parent::willSendResponse($response); - $console = $this->getRequest()->getApplicationConfiguration()->getConsole(); + $console = $request->getApplicationConfiguration()->getConsole(); + if ($console) { $response = str_replace( '', - $console->render($this->getRequest()), + $console->render($request), $response); } diff --git a/webroot/rsrc/css/aphront/dark-console.css b/webroot/rsrc/css/aphront/dark-console.css index ceb874ab4d..5be2bcfaa4 100644 --- a/webroot/rsrc/css/aphront/dark-console.css +++ b/webroot/rsrc/css/aphront/dark-console.css @@ -3,7 +3,7 @@ */ .dark-console { - background: #555555; + background: #444444; color: #eeeeee; width: 100%; font-family: "Verdana"; @@ -11,54 +11,101 @@ position: relative; } -.dark-console a:link { - color: inherit; +.dark-console-requests, +.dark-console-tabs { + position: absolute; + overflow-y: auto; + top: 0; + left: 0; + bottom: 0; + width: 15%; + padding: 8px 0; +} + +.dark-console-requests, +.dark-console-tabs, +.dark-console-panel, +.dark-console-load { + border-left: 1px solid #111111; + box-shadow: -2px 0px 2px rgba(0, 0, 0, 0.25); +} + +.dark-console-requests { + background: #222222; } .dark-console-tabs { - width: 180px; - background: #222222; - border-right: 1px solid #888888; - padding: 2.5em 0em; + background: #333333; + left: 15%; } +.dark-console-panel, +.dark-console-load { + position: relative; + min-height: 320px; +} -a.dark-console-tab { - padding: .75em 12px; - text-align: right; - background: #444444; - position: relative; - border: 1px solid #666666; +.dark-console-panel { + margin-left: 30%; + background: #444444; +} + +.dark-console-requests a.dark-console-request, +.dark-console-tabs a.dark-console-tab { + display: block; + padding: 6px; + overflow: hidden; + background: #444444; + margin: 3px 0; + color: #cccccc; + border-color: #666666; border-width: 1px 0; - border-right-color: #888888; - margin-bottom: 2px; - display: block; - color: #cccccc; + border-style: solid; } -a.dark-console-tab-selected { - margin-right: -1px; - padding-right: 13px; - background: #555555; - border-color: #888888; - border-right-color: #555555; - color: #eeeeee; +.dark-console-requests a.dark-selected, +.dark-console-tabs a.dark-selected { + background: #0066aa; } +.dark-console-requests a.dark-console-request:hover, +.dark-console-tabs a.dark-console-tab:hover { + background: #1188cc; +} + +.dark-console-tabs a.dark-console-tab { + text-align: right; +} + +.dark-console-load { + background-image: url(/rsrc/image/darkload.gif); + background-position: center center; + background-repeat: no-repeat; + background-color: #000; + margin-left: 15%; +} .dark-console .aphront-table-view { + font-size: 11px; background: #888888; color: #eeeeee; - margin: 1em 1%; - width: 98%; + width: 100%; border-color: #333333; + margin: 8px 0; } .dark-console .aphront-table-view th { + text-shadow: none; + font-family: "Verdana"; + font-size: 11px; background: #333333; color: #ffffff; } +.dark-console .aphront-table-view td { + font-size: 11px; +} + .dark-console .aphront-table-view td.header { background: #444444; color: #ffffff; @@ -77,13 +124,8 @@ a.dark-console-tab-selected { color: #dddddd; } -.dark-console-panel-ErrorLog { - max-height: 500px; - overflow: auto; -} - -.dark-console-panel-error-details { - display: none; +.dark-console-panel-core { + padding: 12px; } .explain-sev-1 { @@ -119,15 +161,11 @@ a.dark-console-tab-selected { } .dark-console-panel-header { - background: #606060; - border-bottom: 1px solid #505050; - padding: .25em 1em .25em 0; + padding: 8px 4px 0; } .dark-console-panel-header h1 { - padding: 1em; - font-size: 12px; - font-weight: normal; + font-size: 15px; } .dark-console-panel-header .button { @@ -168,4 +206,11 @@ a.dark-console-tab-selected { height: 2px; } +.dark-console-panel-ErrorLog { + max-height: 500px; + overflow: auto; +} +.dark-console-panel-error-details { + display: none; +} diff --git a/webroot/rsrc/image/darkload.gif b/webroot/rsrc/image/darkload.gif new file mode 100644 index 0000000000..1b8c1f28ad Binary files /dev/null and b/webroot/rsrc/image/darkload.gif differ diff --git a/webroot/rsrc/js/application/core/behavior-dark-console-ajax.js b/webroot/rsrc/js/application/core/behavior-dark-console-ajax.js deleted file mode 100644 index ecaa6053cc..0000000000 --- a/webroot/rsrc/js/application/core/behavior-dark-console-ajax.js +++ /dev/null @@ -1,49 +0,0 @@ -/** - * @provides javelin-behavior-dark-console-ajax - * @requires javelin-behavior - * javelin-dom - */ - -JX.behavior('dark-console-ajax', function(config) { - var requestLog = JX.DOM.find(document.body, - 'table', - 'dark-console-request-log'); - var requestTable = JX.DOM.find(requestLog, 'table'); - var requestRows = JX.DOM.scry(requestTable, 'tr'); - var requestNumber = requestRows.length - 1; // header don't count - var requestURI = config.uri; - var console = JX.$H(config.console); - var newRowType = 'ajax'; - - var newRowNumber = JX.$N( - 'a', - { - 'sigil' : 'request-log-number', - 'meta' : { 'console' : console } - }, - requestNumber - ); - var newRowURI = JX.$N( - 'a', - { - 'sigil' : 'request-log-uri', - 'meta' : { 'console' : console } - }, - requestURI - ); - - var newRow = JX.$N( - 'tr', - { - 'className' : requestNumber % 2 ? 'alt' : '' - }, - [ - JX.$N('td', {}, newRowNumber), - JX.$N('td', {}, newRowType), - JX.$N('td', {}, newRowURI) - ] - ); - - JX.DOM.appendContent(requestTable, newRow); - -}); diff --git a/webroot/rsrc/js/application/core/behavior-dark-console.js b/webroot/rsrc/js/application/core/behavior-dark-console.js index 3f31ac1545..0c72d0469c 100644 --- a/webroot/rsrc/js/application/core/behavior-dark-console.js +++ b/webroot/rsrc/js/application/core/behavior-dark-console.js @@ -6,55 +6,221 @@ * javelin-dom * javelin-request * phabricator-keyboard-shortcut - * javelin-behavior-dark-console-ajax */ -JX.behavior('dark-console', function(config) { - var selected_tab = null; +JX.behavior('dark-console', function(config, statics) { + var root = statics.root || setup_console(); - JX.Stratcom.listen( - 'click', - ['dark-console', 'dark-console-tab'], - function(e) { - var console = e.getNode('dark-console'); - var tabs = JX.DOM.scry(console, 'a', 'dark-console-tab'); - var panels = JX.DOM.scry(console, 'div', 'dark-console-panel'); - var target = e.getTarget(); - for (var ii = 0; ii < tabs.length; ii++) { - JX.DOM.alterClass( - tabs[ii], - 'dark-console-tab-selected', - tabs[ii] == target); - (tabs[ii] != target ? JX.DOM.hide : JX.DOM.show)(panels[ii]); + config.key = config.key || root.getAttribute('data-console-key'); + add_request(config); + + + // Do first-time setup. + function setup_console() { + statics.root = JX.$('darkconsole'); + statics.req = {all: {}, current: null}; + statics.tab = {all: {}, current: null}; + + statics.el = {}; + + statics.el.reqs = JX.$N('div', {className: 'dark-console-requests'}); + statics.root.appendChild(statics.el.reqs); + + statics.el.tabs = JX.$N('div', {className: 'dark-console-tabs'}); + statics.root.appendChild(statics.el.tabs); + + statics.el.panel = JX.$N('div', {className: 'dark-console-panel'}); + statics.root.appendChild(statics.el.panel); + + statics.el.load = JX.$N('div', {className: 'dark-console-load'}); + statics.root.appendChild(statics.el.load); + + statics.cache = {}; + + statics.visible = config.visible; + statics.selected = config.selected; + + return statics.root; + } + + + // Add a new request to the console (initial page load, or new Ajax response). + function add_request(config) { + + // Ignore DarkConsole data requests. + if (config.uri.match(new RegExp('^/~/data/'))) { + return; + } + + var attr = { + className: 'dark-console-request', + sigil: 'dark-console-request', + title: config.uri, + meta: config, + href: '#' + }; + + var link = JX.$N('a', attr, config.uri); + statics.el.reqs.appendChild(link); + statics.req.all[config.key] = link; + + if (!statics.req.current) { + select_request(config.key); + } + } + + + // Select a request (on load, or when the user clicks one). + function select_request(key) { + var req = statics.req; + + if (req.current) { + JX.DOM.alterClass(req.all[req.current], 'dark-selected', false); + } + statics.req.current = key; + JX.DOM.alterClass(req.all[req.current], 'dark-selected', true); + + if (statics.visible) { + JX.log('visible!'); + draw_request(key); + } + } + + // When the user clicks a request, select it. + JX.Stratcom.listen('click', 'dark-console-request', function(e) { + e.kill(); + select_request(e.getNodeData('dark-console-request').key); + }); + + + // After the user selects a request, draw its tabs. + function draw_request(key) { + var cache = statics.cache; + + if (cache[key]) { + render_request(key); + return; + } + + new JX.Request( + '/~/data/' + key + '/', + function(r) { + cache[key] = r; + if (statics.req.current == key) { + render_request(key); + } + }) + .send(); + + show_loading(); + } + + // Show the loading indicator. + function show_loading() { + JX.DOM.hide(statics.el.tabs); + JX.DOM.hide(statics.el.panel); + JX.DOM.show(statics.el.load); + } + + // Hide the loading indicator. + function hide_loading() { + JX.DOM.show(statics.el.tabs); + JX.DOM.show(statics.el.panel); + JX.DOM.hide(statics.el.load); + } + + function render_request(key) { + var data = statics.cache[key]; + + statics.tab.all = {}; + + var links = []; + var first = null; + for (var ii = 0; ii < data.tabs.length; ii++) { + var tab = data.tabs[ii]; + var attr = { + className: 'dark-console-tab', + sigil: 'dark-console-tab', + meta: tab, + href: '#' + }; + + var bullet = null; + if (tab.color) { + bullet = JX.$N('span', {style: {color: tab.color}}, "\u2022"); } - selected_tab = target.id.replace('dark-console-tab-', ''); + var link = JX.$N('a', attr, [bullet, ' ', tab.name]); + links.push(link); + statics.tab.all[tab['class']] = link; + first = first || tab['class']; + } - new JX.Request(config.uri, JX.bag) - .setData({ tab : selected_tab }) + JX.DOM.setContent(statics.el.tabs, links); + + if (statics.tab.current in statics.tab.all) { + select_tab(statics.tab.current); + } else if (statics.selected in statics.tab.all) { + select_tab(statics.selected); + } else { + select_tab(first); + } + + hide_loading(); + } + + function select_tab(tclass) { + var tabs = statics.tab; + + if (tabs.current) { + JX.DOM.alterClass(tabs.current, 'dark-selected', false); + } + tabs.current = tabs.all[tclass]; + JX.DOM.alterClass(tabs.current, 'dark-selected', true); + + if (tclass != statics.selected) { + // Save user preference. + new JX.Request('/~/', JX.bag) + .setData({ tab : tclass }) .send(); - }); + } + draw_panel(); + } + + // When the user clicks a tab, select it. + JX.Stratcom.listen('click', 'dark-console-tab', function(e) { + e.kill(); + select_tab(e.getNodeData('dark-console-tab')['class']); + }); + + function draw_panel() { + var data = statics.cache[statics.req.current]; + var tclass = JX.Stratcom.getData(statics.tab.current)['class']; + var html = data.panel[tclass]; + + var div = JX.$N('div', {className: 'dark-console-panel-core'}, JX.$H(html)); + JX.DOM.setContent(statics.el.panel, div); + } + + // Install keyboard shortcut. var desc = 'Toggle visibility of DarkConsole.'; new JX.KeyboardShortcut('`', desc) .setHandler(function(manager) { - var console = JX.DOM.find(document.body, 'table', 'dark-console'); - var requestLog = JX.DOM.find( - document.body, - 'table', - 'dark-console-request-log'); + statics.visible = !statics.visible; - config.visible = !config.visible; - if (config.visible) { - JX.DOM.show(console); - JX.DOM.show(requestLog); + if (statics.visible) { + JX.DOM.show(root); + if (statics.req.current) { + draw_request(statics.req.current); + } } else { - JX.DOM.hide(console); - JX.DOM.hide(requestLog); + JX.DOM.hide(root); } - new JX.Request(config.uri, JX.bag) - .setData({visible: config.visible ? 1 : 0}) + // Save user preference. + new JX.Request('/~/', JX.bag) + .setData({visible: statics.visible ? 1 : 0}) .send(); // Force resize listeners to take effect. @@ -62,89 +228,4 @@ JX.behavior('dark-console', function(config) { }) .register(); - var initRequestLog = function() { - var console = JX.DOM.find(document.body, - 'table', - 'dark-console'); - var requestLog = JX.DOM.find(document.body, - 'table', - 'dark-console-request-log'); - var requestTable = JX.DOM.find(requestLog, 'table'); - var rows = JX.DOM.scry(requestTable, 'tr'); - var tableHeader = rows[0]; - var newRowNumber = JX.$N( - 'a', - { - 'sigil' : 'request-log-number', - 'meta' : { 'console' : console } - }, - "0" - ); - var newRowURI = JX.$N( - 'a', - { - 'sigil' : 'request-log-uri', - 'meta' : { 'console' : console } - }, - config.request_uri - ); - - var newRow = JX.$N( - 'tr', - { - 'className' : 'highlight' - }, - [ - JX.$N('td', {}, newRowNumber), - JX.$N('td', {}, 'main'), - JX.$N('td', {}, newRowURI) - ] - ); - - JX.DOM.setContent(requestTable, [tableHeader, newRow]); - } - - initRequestLog(); - - var updateActiveRequest = function(e) { - var log = e.getNode('dark-console-request-log'); - var table = JX.DOM.find(log, 'table'); - var rows = JX.DOM.scry(table, 'tr'); - var targetRow = e.getTarget().parentNode.parentNode; - var data = JX.Stratcom.getData(e.getTarget()); - var newConsole = data.console; - for (var ii = 0; ii < rows.length; ii++) { - JX.DOM.alterClass( - rows[ii], - 'highlight', - rows[ii] == targetRow); - } - var console = JX.DOM.find(document.body, 'table', 'dark-console'); - JX.DOM.replace(console, newConsole); - if (selected_tab) { - console = JX.DOM.find(document.body, 'table', 'dark-console'); - var s_id = 'dark-console-tab-' + selected_tab; - var tabs = JX.DOM.scry(console, 'a', 'dark-console-tab'); - var panels = JX.DOM.scry(console, 'div', 'dark-console-panel'); - for (var ii = 0; ii < tabs.length; ii++) { - JX.DOM.alterClass( - tabs[ii], - 'dark-console-tab-selected', - tabs[ii].id == s_id); - (tabs[ii].id != s_id ? JX.DOM.hide : JX.DOM.show)(panels[ii]); - } - } - } - - JX.Stratcom.listen( - 'click', - ['dark-console-request-log', 'request-log-number'], - updateActiveRequest - ); - JX.Stratcom.listen( - 'click', - ['dark-console-request-log', 'request-log-uri'], - updateActiveRequest - ); - });