1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-22 06:42:42 +01:00

DarkConsole: fix rendering, move request log, load over ajax

Summary:
This accomplishes three major goals:

  # Fixes phutil_render_tag -> phutil_tag callsites in DarkConsole.
  # Moves the Ajax request log to a new panel on the left. This panel (and the tabs panel) get scrollbars when they get large, instead of making the page constantly scroll down.
  # Loads the panel content over ajax, instead of dumping it into the page body / ajax response body. I've been planning to do this for about 3 years, which is why the plugins are architected the way they are. This should make debugging easier by making response bodies not be 50%+ darkconsole stuff.

Additionally, load the plugins dynamically (the old method predates library maps and PhutilSymbolLoader).

Test Plan:
{F30675}

  - Switched between requests and tabs, reloaded page, saw same tab.
  - Used "analyze queries", "profile page", triggered errors.
  - Verified page does not load anything by default if dark console is closed with Charles.
  - Generally banged on it a bit.

Reviewers: vrana, btrahan, chad

Reviewed By: vrana

CC: aran

Maniphest Tasks: T2432

Differential Revision: https://secure.phabricator.com/D4692
This commit is contained in:
epriestley 2013-01-28 18:45:32 -08:00
parent 9a0ae6376f
commit 114ed6c7fe
18 changed files with 560 additions and 481 deletions

View file

@ -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',
),

View file

@ -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',

View file

@ -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',

View file

@ -73,7 +73,10 @@ class AphrontDefaultApplicationConfiguration
'profile/(?P<phid>[^/]+)/' => 'PhabricatorXHProfProfileController',
),
'/~/' => 'DarkConsoleController',
'/~/' => array(
'' => 'DarkConsoleController',
'data/(?P<key>[^/]+)/' => 'DarkConsoleDataController',
),
'/search/' => array(
'' => 'PhabricatorSearchController',

View file

@ -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),
),
'<tr>'.
'<th class="dark-console-tabs">'.
implode("\n", $tabs_markup).
'</th>'.
'<td>'.implode("\n", $panel_markup).'</td>'.
'</tr>');
$ttl = (60 * 60 * 6));
if (!empty($_COOKIE['phsid'])) {
$console = str_replace(
$_COOKIE['phsid'],
phutil_escape_html('<session-key>'),
$console);
}
if ($request->isAjax()) {
// for ajax this HTML gets updated on the client
$request_history = null;
} else {
$request_table_header =
'<div class="dark-console-panel-request-log-separator"></div>';
$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;',
),
'<tr>'.
'<th class="dark-console-tabs">'.
phutil_tag(
'a',
array(
'class' => 'dark-console-tab dark-console-tab-selected',
),
'Request Log').
'</th>'.
'<td>'.
javelin_render_tag(
'div',
array(
'class' => 'dark-console-panel dark-console-panel-RequestLog',
),
$request_table).
'</td>'.
'</tr>');
}
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),
),
'');
}
}

View file

@ -0,0 +1,69 @@
<?php
/**
* @group console
*/
final class DarkConsoleDataController extends PhabricatorController {
private $key;
public function willProcessRequest(array $data) {
$this->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);
}
}

View file

@ -7,29 +7,32 @@ final class DarkConsoleErrorLogPlugin extends DarkConsolePlugin {
public function getName() {
$count = count($this->getData());
if ($count) {
return
'<span style="color: #ff0000;">&bull;</span> '.
"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();

View file

@ -37,7 +37,7 @@ final class DarkConsoleEventPlugin extends DarkConsolePlugin {
);
}
public function render() {
public function renderPanel() {
$data = $this->getData();
$out = array();

View file

@ -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;
}

View file

@ -20,8 +20,7 @@ final class DarkConsoleRequestPlugin extends DarkConsolePlugin {
);
}
public function render() {
public function renderPanel() {
$data = $this->getData();
$sections = array(

View file

@ -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',
),

View file

@ -8,28 +8,40 @@ final class DarkConsoleXHProfPlugin extends DarkConsolePlugin {
protected $xhprofID;
public function getName() {
$run = $this->getData();
if ($run) {
return '<span style="color: #ff00ff;">&bull;</span> 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 =
'<div class="dark-console-panel-header">'.
phutil_tag(
'a',
array(
'href' => $this->getRequestURI()->alter('__profile__', 'page'),
'href' => $profile_uri,
'class' => $run
? 'disabled button'
: 'green button',

View file

@ -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()),
));
}

View file

@ -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(
'<darkconsole />',
$console->render($this->getRequest()),
$console->render($request),
$response);
}

View file

@ -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;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View file

@ -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);
});

View file

@ -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
);
});