Page MenuHomePhorge

No OneTemporary

diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php
index 48e6ef848e..54800c50f8 100644
--- a/src/__celerity_resource_map__.php
+++ b/src/__celerity_resource_map__.php
@@ -1,1323 +1,1323 @@
<?php
/**
* This file is automatically generated. Use 'celerity_mapper.php' to rebuild
* it.
* @generated
*/
celerity_register_resource_map(array(
'aphront-attached-file-view-css' =>
array(
'uri' => '/res/a6ca5487/rsrc/css/aphront/attached-file-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/aphront/attached-file-view.css',
),
'aphront-crumbs-view-css' =>
array(
'uri' => '/res/9009e6bd/rsrc/css/aphront/crumbs-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/aphront/crumbs-view.css',
),
'aphront-dark-console-css' =>
array(
- 'uri' => '/res/e7011594/rsrc/css/aphront/dark-console.css',
+ 'uri' => '/res/a7d1dbf1/rsrc/css/aphront/dark-console.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/aphront/dark-console.css',
),
'aphront-dialog-view-css' =>
array(
'uri' => '/res/61a58113/rsrc/css/aphront/dialog-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/aphront/dialog-view.css',
),
'aphront-error-view-css' =>
array(
'uri' => '/res/e4c5e4ed/rsrc/css/aphront/error-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/aphront/error-view.css',
),
'aphront-form-view-css' =>
array(
'uri' => '/res/8ee16aba/rsrc/css/aphront/form-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/aphront/form-view.css',
),
'aphront-headsup-action-list-view-css' =>
array(
'uri' => '/res/af3dff49/rsrc/css/aphront/headsup-action-list-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/aphront/headsup-action-list-view.css',
),
'aphront-list-filter-view-css' =>
array(
'uri' => '/res/0f5ddaba/rsrc/css/aphront/list-filter-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/aphront/list-filter-view.css',
),
'aphront-pager-view-css' =>
array(
'uri' => '/res/43fb79f0/rsrc/css/aphront/pager-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/aphront/pager-view.css',
),
'aphront-panel-view-css' =>
array(
'uri' => '/res/e0139b9c/rsrc/css/aphront/panel-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/aphront/panel-view.css',
),
'aphront-request-failure-view-css' =>
array(
'uri' => '/res/c9a43002/rsrc/css/aphront/request-failure-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/aphront/request-failure-view.css',
),
'aphront-side-nav-view-css' =>
array(
'uri' => '/res/f92966bd/rsrc/css/aphront/side-nav-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/aphront/side-nav-view.css',
),
'aphront-table-view-css' =>
array(
'uri' => '/res/910e83ec/rsrc/css/aphront/table-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/aphront/table-view.css',
),
'aphront-tokenizer-control-css' =>
array(
'uri' => '/res/f530af47/rsrc/css/aphront/tokenizer.css',
'type' => 'css',
'requires' =>
array(
0 => 'aphront-typeahead-control-css',
),
'disk' => '/rsrc/css/aphront/tokenizer.css',
),
'aphront-typeahead-control-css' =>
array(
'uri' => '/res/a05236a6/rsrc/css/aphront/typeahead.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/aphront/typeahead.css',
),
'differential-changeset-view-css' =>
array(
'uri' => '/res/d2923406/rsrc/css/application/differential/changeset-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/differential/changeset-view.css',
),
'differential-core-view-css' =>
array(
'uri' => '/res/438fe316/rsrc/css/application/differential/core.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/differential/core.css',
),
'differential-inline-comment-editor' =>
array(
'uri' => '/res/5e4f0aa4/rsrc/js/application/differential/DifferentialInlineCommentEditor.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-dom',
1 => 'javelin-workflow',
2 => 'javelin-util',
3 => 'javelin-stratcom',
4 => 'javelin-install',
),
'disk' => '/rsrc/js/application/differential/DifferentialInlineCommentEditor.js',
),
'differential-revision-add-comment-css' =>
array(
'uri' => '/res/849748d3/rsrc/css/application/differential/add-comment.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/differential/add-comment.css',
),
'differential-revision-comment-css' =>
array(
'uri' => '/res/9fb8013b/rsrc/css/application/differential/revision-comment.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/differential/revision-comment.css',
),
'differential-revision-comment-list-css' =>
array(
'uri' => '/res/3b31faa3/rsrc/css/application/differential/revision-comment-list.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/differential/revision-comment-list.css',
),
'differential-revision-detail-css' =>
array(
'uri' => '/res/015e5995/rsrc/css/application/differential/revision-detail.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/differential/revision-detail.css',
),
'differential-revision-history-css' =>
array(
'uri' => '/res/0d7d515d/rsrc/css/application/differential/revision-history.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/differential/revision-history.css',
),
'differential-table-of-contents-css' =>
array(
'uri' => '/res/d173445b/rsrc/css/application/differential/table-of-contents.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/differential/table-of-contents.css',
),
'diffusion-commit-view-css' =>
array(
'uri' => '/res/bc39d876/rsrc/css/application/diffusion/commit-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/diffusion/commit-view.css',
),
'diffusion-source-css' =>
array(
'uri' => '/res/db4566b6/rsrc/css/application/diffusion/diffusion-source.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/diffusion/diffusion-source.css',
),
'herald-css' =>
array(
'uri' => '/res/5051f3ab/rsrc/css/application/herald/herald.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/herald/herald.css',
),
'herald-rule-editor' =>
array(
'uri' => '/res/ba957508/rsrc/js/application/herald/HeraldRuleEditor.js',
'type' => 'js',
'requires' =>
array(
0 => 'multirow-row-manager',
1 => 'javelin-install',
2 => 'javelin-typeahead',
3 => 'javelin-util',
4 => 'javelin-dom',
5 => 'javelin-tokenizer',
6 => 'javelin-typeahead-preloaded-source',
7 => 'javelin-stratcom',
8 => 'javelin-json',
),
'disk' => '/rsrc/js/application/herald/HeraldRuleEditor.js',
),
'herald-test-css' =>
array(
'uri' => '/res/c0cd6bdb/rsrc/css/application/herald/herald-test.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/herald/herald-test.css',
),
'javelin-behavior' =>
array(
'uri' => '/res/b28adfa1/rsrc/js/javelin/lib/behavior.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-magical-init',
),
'disk' => '/rsrc/js/javelin/lib/behavior.js',
),
0 =>
array(
'uri' => '/res/1da00bfe/rsrc/js/javelin/lib/__tests__/URI.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-uri',
1 => 'javelin-php-serializer',
),
'disk' => '/rsrc/js/javelin/lib/__tests__/URI.js',
),
'javelin-behavior-aphront-basic-tokenizer' =>
array(
'uri' => '/res/5e183bd5/rsrc/js/application/core/behavior-tokenizer.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-typeahead',
2 => 'javelin-tokenizer',
3 => 'javelin-typeahead-preloaded-source',
4 => 'javelin-dom',
5 => 'javelin-stratcom',
),
'disk' => '/rsrc/js/application/core/behavior-tokenizer.js',
),
'javelin-behavior-aphront-drag-and-drop' =>
array(
'uri' => '/res/170115f4/rsrc/js/application/core/behavior-drag-and-drop.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-dom',
2 => 'javelin-util',
3 => 'phabricator-drag-and-drop-file-upload',
),
'disk' => '/rsrc/js/application/core/behavior-drag-and-drop.js',
),
'javelin-behavior-aphront-form-disable-on-submit' =>
array(
'uri' => '/res/6c659ede/rsrc/js/application/core/behavior-form.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-stratcom',
2 => 'javelin-dom',
),
'disk' => '/rsrc/js/application/core/behavior-form.js',
),
'javelin-behavior-countdown-timer' =>
array(
'uri' => '/res/9eef8193/rsrc/js/application/countdown/timer.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-dom',
2 => 'javelin-util',
),
'disk' => '/rsrc/js/application/countdown/timer.js',
),
'javelin-behavior-dark-console' =>
array(
'uri' => '/res/c80156c4/rsrc/js/application/core/behavior-dark-console.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-stratcom',
2 => 'javelin-util',
3 => 'javelin-dom',
4 => 'javelin-request',
5 => 'phabricator-keyboard-shortcut',
),
'disk' => '/rsrc/js/application/core/behavior-dark-console.js',
),
'javelin-behavior-differential-accept-with-errors' =>
array(
'uri' => '/res/41c4685b/rsrc/js/application/differential/behavior-accept-with-errors.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-dom',
),
'disk' => '/rsrc/js/application/differential/behavior-accept-with-errors.js',
),
'javelin-behavior-differential-add-reviewers-and-ccs' =>
array(
'uri' => '/res/fc352745/rsrc/js/application/differential/behavior-add-reviewers-and-ccs.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-dom',
2 => 'javelin-tokenizer',
3 => 'javelin-typeahead',
4 => 'javelin-typeahead-preloaded-source',
),
'disk' => '/rsrc/js/application/differential/behavior-add-reviewers-and-ccs.js',
),
'javelin-behavior-differential-comment-jump' =>
array(
'uri' => '/res/be77fced/rsrc/js/application/differential/behavior-comment-jump.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-util',
2 => 'javelin-dom',
),
'disk' => '/rsrc/js/application/differential/behavior-comment-jump.js',
),
'javelin-behavior-differential-diff-radios' =>
array(
'uri' => '/res/004cb66f/rsrc/js/application/differential/behavior-diff-radios.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-stratcom',
2 => 'javelin-dom',
),
'disk' => '/rsrc/js/application/differential/behavior-diff-radios.js',
),
'javelin-behavior-differential-edit-inline-comments' =>
array(
'uri' => '/res/9d4ca5d7/rsrc/js/application/differential/behavior-edit-inline-comments.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-stratcom',
2 => 'javelin-dom',
3 => 'javelin-util',
4 => 'javelin-vector',
5 => 'differential-inline-comment-editor',
),
'disk' => '/rsrc/js/application/differential/behavior-edit-inline-comments.js',
),
'javelin-behavior-differential-feedback-preview' =>
array(
'uri' => '/res/ab8a7d60/rsrc/js/application/differential/behavior-comment-preview.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-stratcom',
2 => 'javelin-dom',
3 => 'javelin-request',
4 => 'javelin-util',
5 => 'phabricator-shaped-request',
),
'disk' => '/rsrc/js/application/differential/behavior-comment-preview.js',
),
'javelin-behavior-differential-keyboard-navigation' =>
array(
'uri' => '/res/3bdfaec7/rsrc/js/application/differential/behavior-keyboard-nav.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-dom',
2 => 'phabricator-keyboard-shortcut',
),
'disk' => '/rsrc/js/application/differential/behavior-keyboard-nav.js',
),
'javelin-behavior-differential-populate' =>
array(
'uri' => '/res/025171e1/rsrc/js/application/differential/behavior-populate.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-request',
2 => 'javelin-util',
3 => 'javelin-dom',
),
'disk' => '/rsrc/js/application/differential/behavior-populate.js',
),
'javelin-behavior-differential-show-all-comments' =>
array(
'uri' => '/res/bcc990f0/rsrc/js/application/differential/behavior-show-all-comments.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-stratcom',
2 => 'javelin-dom',
),
'disk' => '/rsrc/js/application/differential/behavior-show-all-comments.js',
),
'javelin-behavior-differential-show-more' =>
array(
'uri' => '/res/a766c717/rsrc/js/application/differential/behavior-show-more.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-dom',
2 => 'javelin-request',
3 => 'javelin-util',
4 => 'javelin-stratcom',
),
'disk' => '/rsrc/js/application/differential/behavior-show-more.js',
),
'javelin-behavior-diffusion-jump-to' =>
array(
'uri' => '/res/4b63e436/rsrc/js/application/diffusion/behavior-jump-to.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-util',
2 => 'javelin-vector',
3 => 'javelin-dom',
),
'disk' => '/rsrc/js/application/diffusion/behavior-jump-to.js',
),
'javelin-behavior-diffusion-pull-lastmodified' =>
array(
'uri' => '/res/29fe2790/rsrc/js/application/diffusion/behavior-pull-lastmodified.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-dom',
2 => 'javelin-util',
3 => 'javelin-request',
),
'disk' => '/rsrc/js/application/diffusion/behavior-pull-lastmodified.js',
),
'javelin-behavior-error-log' =>
array(
'uri' => '/res/a5cb42a5/rsrc/js/application/core/behavior-error-log.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-dom',
),
'disk' => '/rsrc/js/application/core/behavior-error-log.js',
),
'javelin-behavior-herald-rule-editor' =>
array(
'uri' => '/res/77a0c945/rsrc/js/application/herald/herald-rule-editor.js',
'type' => 'js',
'requires' =>
array(
0 => 'herald-rule-editor',
1 => 'javelin-behavior',
),
'disk' => '/rsrc/js/application/herald/herald-rule-editor.js',
),
'javelin-behavior-maniphest-project-create' =>
array(
'uri' => '/res/85a0eaf9/rsrc/js/application/maniphest/behavior-project-create.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-dom',
2 => 'javelin-stratcom',
3 => 'javelin-workflow',
),
'disk' => '/rsrc/js/application/maniphest/behavior-project-create.js',
),
'javelin-behavior-maniphest-transaction-controls' =>
array(
'uri' => '/res/94a2a395/rsrc/js/application/maniphest/behavior-transaction-controls.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-dom',
2 => 'javelin-tokenizer',
3 => 'javelin-typeahead',
4 => 'javelin-typeahead-preloaded-source',
),
'disk' => '/rsrc/js/application/maniphest/behavior-transaction-controls.js',
),
'javelin-behavior-maniphest-transaction-expand' =>
array(
'uri' => '/res/966410de/rsrc/js/application/maniphest/behavior-transaction-expand.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-dom',
2 => 'javelin-workflow',
3 => 'javelin-stratcom',
),
'disk' => '/rsrc/js/application/maniphest/behavior-transaction-expand.js',
),
'javelin-behavior-maniphest-transaction-preview' =>
array(
'uri' => '/res/44e86555/rsrc/js/application/maniphest/behavior-transaction-preview.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-dom',
2 => 'javelin-util',
3 => 'phabricator-shaped-request',
),
'disk' => '/rsrc/js/application/maniphest/behavior-transaction-preview.js',
),
'javelin-behavior-owners-path-editor' =>
array(
'uri' => '/res/9cf78ffc/rsrc/js/application/owners/owners-path-editor.js',
'type' => 'js',
'requires' =>
array(
0 => 'owners-path-editor',
1 => 'javelin-behavior',
),
'disk' => '/rsrc/js/application/owners/owners-path-editor.js',
),
'javelin-behavior-phabricator-keyboard-shortcuts' =>
array(
'uri' => '/res/94b009e2/rsrc/js/application/core/behavior-keyboard-shortcuts.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-workflow',
2 => 'javelin-json',
3 => 'phabricator-keyboard-shortcut',
),
'disk' => '/rsrc/js/application/core/behavior-keyboard-shortcuts.js',
),
'javelin-behavior-phabricator-object-selector' =>
array(
'uri' => '/res/34f9a11e/rsrc/js/application/core/behavior-object-selector.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-dom',
2 => 'javelin-request',
3 => 'javelin-util',
4 => 'javelin-stratcom',
),
'disk' => '/rsrc/js/application/core/behavior-object-selector.js',
),
'javelin-behavior-phabricator-watch-anchor' =>
array(
'uri' => '/res/bb6fa5b2/rsrc/js/application/core/behavior-watch-anchor.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-stratcom',
2 => 'javelin-util',
3 => 'javelin-dom',
),
'disk' => '/rsrc/js/application/core/behavior-watch-anchor.js',
),
'javelin-behavior-workflow' =>
array(
'uri' => '/res/079f49c3/rsrc/js/application/core/behavior-workflow.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-stratcom',
2 => 'javelin-workflow',
),
'disk' => '/rsrc/js/application/core/behavior-workflow.js',
),
'javelin-dom' =>
array(
'uri' => '/res/34e7f2b5/rsrc/js/javelin/lib/DOM.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-magical-init',
1 => 'javelin-install',
2 => 'javelin-util',
3 => 'javelin-vector',
4 => 'javelin-stratcom',
),
'disk' => '/rsrc/js/javelin/lib/DOM.js',
),
'javelin-event' =>
array(
'uri' => '/res/25c7c9e8/rsrc/js/javelin/core/Event.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-install',
),
'disk' => '/rsrc/js/javelin/core/Event.js',
),
'javelin-install' =>
array(
'uri' => '/res/b6692f42/rsrc/js/javelin/core/install.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-util',
1 => 'javelin-magical-init',
),
'disk' => '/rsrc/js/javelin/core/install.js',
),
'javelin-json' =>
array(
'uri' => '/res/f23dbfbd/rsrc/js/javelin/lib/JSON.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-install',
1 => 'javelin-util',
),
'disk' => '/rsrc/js/javelin/lib/JSON.js',
),
'javelin-magical-init' =>
array(
'uri' => '/res/6a069362/rsrc/js/javelin/core/init.js',
'type' => 'js',
'requires' =>
array(
),
'disk' => '/rsrc/js/javelin/core/init.js',
),
'javelin-mask' =>
array(
'uri' => '/res/28e3bd9c/rsrc/js/javelin/lib/Mask.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-install',
1 => 'javelin-vector',
2 => 'javelin-dom',
),
'disk' => '/rsrc/js/javelin/lib/Mask.js',
),
'javelin-request' =>
array(
'uri' => '/res/786bfe6f/rsrc/js/javelin/lib/Request.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-install',
1 => 'javelin-stratcom',
2 => 'javelin-util',
3 => 'javelin-behavior',
4 => 'javelin-json',
),
'disk' => '/rsrc/js/javelin/lib/Request.js',
),
'javelin-stratcom' =>
array(
'uri' => '/res/d6cf4631/rsrc/js/javelin/core/Stratcom.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-install',
1 => 'javelin-event',
2 => 'javelin-util',
3 => 'javelin-magical-init',
),
'disk' => '/rsrc/js/javelin/core/Stratcom.js',
),
'javelin-tokenizer' =>
array(
'uri' => '/res/c2dbba74/rsrc/js/javelin/lib/control/tokenizer/Tokenizer.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-dom',
1 => 'javelin-util',
2 => 'javelin-stratcom',
3 => 'javelin-install',
),
'disk' => '/rsrc/js/javelin/lib/control/tokenizer/Tokenizer.js',
),
'javelin-typeahead' =>
array(
'uri' => '/res/354d32f1/rsrc/js/javelin/lib/control/typeahead/Typeahead.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-install',
1 => 'javelin-dom',
2 => 'javelin-vector',
3 => 'javelin-util',
),
'disk' => '/rsrc/js/javelin/lib/control/typeahead/Typeahead.js',
),
'javelin-typeahead-composite-source' =>
array(
'uri' => '/res/9873d21c/rsrc/js/javelin/lib/control/typeahead/source/TypeaheadCompositeSource.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-install',
1 => 'javelin-typeahead-source',
2 => 'javelin-util',
),
'disk' => '/rsrc/js/javelin/lib/control/typeahead/source/TypeaheadCompositeSource.js',
),
'javelin-typeahead-normalizer' =>
array(
'uri' => '/res/a5d60e3c/rsrc/js/javelin/lib/control/typeahead/normalizer/TypeaheadNormalizer.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-install',
),
'disk' => '/rsrc/js/javelin/lib/control/typeahead/normalizer/TypeaheadNormalizer.js',
),
'javelin-typeahead-ondemand-source' =>
array(
'uri' => '/res/b2e527e4/rsrc/js/javelin/lib/control/typeahead/source/TypeaheadOnDemandSource.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-install',
1 => 'javelin-util',
2 => 'javelin-stratcom',
3 => 'javelin-request',
4 => 'javelin-typeahead-source',
),
'disk' => '/rsrc/js/javelin/lib/control/typeahead/source/TypeaheadOnDemandSource.js',
),
'javelin-typeahead-preloaded-source' =>
array(
'uri' => '/res/f3c1e7c7/rsrc/js/javelin/lib/control/typeahead/source/TypeaheadPreloadedSource.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-install',
1 => 'javelin-util',
2 => 'javelin-stratcom',
3 => 'javelin-request',
4 => 'javelin-typeahead-source',
),
'disk' => '/rsrc/js/javelin/lib/control/typeahead/source/TypeaheadPreloadedSource.js',
),
'javelin-typeahead-source' =>
array(
'uri' => '/res/8a3aa262/rsrc/js/javelin/lib/control/typeahead/source/TypeaheadSource.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-install',
1 => 'javelin-util',
2 => 'javelin-dom',
3 => 'javelin-typeahead-normalizer',
),
'disk' => '/rsrc/js/javelin/lib/control/typeahead/source/TypeaheadSource.js',
),
'javelin-uri' =>
array(
'uri' => '/res/1c0ead43/rsrc/js/javelin/lib/URI.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-install',
1 => 'javelin-util',
2 => 'javelin-stratcom',
),
'disk' => '/rsrc/js/javelin/lib/URI.js',
),
'javelin-util' =>
array(
'uri' => '/res/725cc90d/rsrc/js/javelin/core/util.js',
'type' => 'js',
'requires' =>
array(
),
'disk' => '/rsrc/js/javelin/core/util.js',
),
'javelin-vector' =>
array(
'uri' => '/res/50535cb8/rsrc/js/javelin/lib/Vector.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-install',
1 => 'javelin-event',
),
'disk' => '/rsrc/js/javelin/lib/Vector.js',
),
'javelin-workflow' =>
array(
'uri' => '/res/91a04640/rsrc/js/javelin/lib/Workflow.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-stratcom',
1 => 'javelin-request',
2 => 'javelin-dom',
3 => 'javelin-vector',
4 => 'javelin-install',
5 => 'javelin-util',
6 => 'javelin-mask',
7 => 'javelin-uri',
),
'disk' => '/rsrc/js/javelin/lib/Workflow.js',
),
'mainphest-task-detail-css' =>
array(
'uri' => '/res/dbefc148/rsrc/css/application/maniphest/task-detail.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/maniphest/task-detail.css',
),
'maniphest-task-summary-css' =>
array(
'uri' => '/res/14cb4b5d/rsrc/css/application/maniphest/task-summary.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/maniphest/task-summary.css',
),
'maniphest-transaction-detail-css' =>
array(
'uri' => '/res/149fccab/rsrc/css/application/maniphest/transaction-detail.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/maniphest/transaction-detail.css',
),
'multirow-row-manager' =>
array(
'uri' => '/res/0a9b3dee/rsrc/js/application/core/MultirowRowManager.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-install',
1 => 'javelin-stratcom',
2 => 'javelin-dom',
3 => 'javelin-util',
),
'disk' => '/rsrc/js/application/core/MultirowRowManager.js',
),
'owners-path-editor' =>
array(
'uri' => '/res/e6c51eb6/rsrc/js/application/owners/OwnersPathEditor.js',
'type' => 'js',
'requires' =>
array(
0 => 'multirow-row-manager',
1 => 'javelin-install',
2 => 'path-typeahead',
3 => 'javelin-dom',
4 => 'javelin-util',
),
'disk' => '/rsrc/js/application/owners/OwnersPathEditor.js',
),
'owners-path-editor-css' =>
array(
'uri' => '/res/9bc5332c/rsrc/css/application/owners/owners-path-editor.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/owners/owners-path-editor.css',
),
'path-typeahead' =>
array(
'uri' => '/res/1343345d/rsrc/js/application/herald/PathTypeahead.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-install',
1 => 'javelin-typeahead',
2 => 'javelin-dom',
3 => 'javelin-request',
4 => 'javelin-typeahead-ondemand-source',
5 => 'javelin-util',
),
'disk' => '/rsrc/js/application/herald/PathTypeahead.js',
),
'phabricator-core-buttons-css' =>
array(
'uri' => '/res/3059cf79/rsrc/css/core/buttons.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/core/buttons.css',
),
'phabricator-core-css' =>
array(
'uri' => '/res/bd20d04b/rsrc/css/core/core.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/core/core.css',
),
'phabricator-countdown-css' =>
array(
'uri' => '/res/0f646281/rsrc/css/application/countdown/timer.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/countdown/timer.css',
),
'phabricator-directory-css' =>
array(
'uri' => '/res/a3d307c5/rsrc/css/application/directory/phabricator-directory.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/directory/phabricator-directory.css',
),
'phabricator-drag-and-drop-file-upload' =>
array(
'uri' => '/res/63a06ad9/rsrc/js/application/core/DragAndDropFileUpload.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-install',
1 => 'javelin-util',
2 => 'javelin-request',
3 => 'javelin-dom',
4 => 'javelin-uri',
),
'disk' => '/rsrc/js/application/core/DragAndDropFileUpload.js',
),
'phabricator-feed-css' =>
array(
'uri' => '/res/32e5879b/rsrc/css/application/feed/feed.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/feed/feed.css',
),
'phabricator-keyboard-shortcut' =>
array(
'uri' => '/res/beed38cd/rsrc/js/application/core/KeyboardShortcut.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-install',
1 => 'javelin-util',
2 => 'phabricator-keyboard-shortcut-manager',
),
'disk' => '/rsrc/js/application/core/KeyboardShortcut.js',
),
'phabricator-keyboard-shortcut-manager' =>
array(
'uri' => '/res/04767571/rsrc/js/application/core/KeyboardShortcutManager.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-install',
1 => 'javelin-util',
2 => 'javelin-stratcom',
3 => 'javelin-dom',
4 => 'javelin-vector',
),
'disk' => '/rsrc/js/application/core/KeyboardShortcutManager.js',
),
'phabricator-object-selector-css' =>
array(
'uri' => '/res/608461d2/rsrc/css/application/objectselector/object-selector.css',
'type' => 'css',
'requires' =>
array(
0 => 'aphront-dialog-view-css',
),
'disk' => '/rsrc/css/application/objectselector/object-selector.css',
),
'phabricator-profile-css' =>
array(
'uri' => '/res/ebe1ac2f/rsrc/css/application/profile/profile-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/profile/profile-view.css',
),
'phabricator-remarkup-css' =>
array(
'uri' => '/res/7ca89111/rsrc/css/core/remarkup.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/core/remarkup.css',
),
'phabricator-search-results-css' =>
array(
'uri' => '/res/f8a86e27/rsrc/css/application/search/search-results.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/search/search-results.css',
),
'phabricator-shaped-request' =>
array(
'uri' => '/res/d7ba774e/rsrc/js/application/core/ShapedRequest.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-install',
1 => 'javelin-util',
2 => 'javelin-request',
),
'disk' => '/rsrc/js/application/core/ShapedRequest.js',
),
'phabricator-slowvote-css' =>
array(
'uri' => '/res/94d20443/rsrc/css/application/slowvote/slowvote.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/slowvote/slowvote.css',
),
'phabricator-standard-page-view' =>
array(
'uri' => '/res/e8238633/rsrc/css/application/base/standard-page-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/base/standard-page-view.css',
),
'phabricator-ui-example-css' =>
array(
'uri' => '/res/0cef078b/rsrc/css/application/uiexample/example.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/uiexample/example.css',
),
'syntax-highlighting-css' =>
array(
'uri' => '/res/e5cc3d88/rsrc/css/core/syntax.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/core/syntax.css',
),
), array (
'packages' =>
array (
'03ef179e' =>
array (
'name' => 'diffusion.pkg.css',
'symbols' =>
array (
0 => 'diffusion-commit-view-css',
),
'uri' => '/res/pkg/03ef179e/diffusion.pkg.css',
'type' => 'css',
),
'25f94e94' =>
array (
'name' => 'typeahead.pkg.js',
'symbols' =>
array (
0 => 'javelin-typeahead',
1 => 'javelin-typeahead-normalizer',
2 => 'javelin-typeahead-source',
3 => 'javelin-typeahead-preloaded-source',
4 => 'javelin-typeahead-ondemand-source',
5 => 'javelin-tokenizer',
6 => 'javelin-behavior-aphront-basic-tokenizer',
),
'uri' => '/res/pkg/25f94e94/typeahead.pkg.js',
'type' => 'js',
),
'307df223' =>
array (
'name' => 'javelin.pkg.js',
'symbols' =>
array (
0 => 'javelin-util',
1 => 'javelin-install',
2 => 'javelin-event',
3 => 'javelin-stratcom',
4 => 'javelin-behavior',
5 => 'javelin-request',
6 => 'javelin-vector',
7 => 'javelin-dom',
8 => 'javelin-json',
9 => 'javelin-uri',
),
'uri' => '/res/pkg/307df223/javelin.pkg.js',
'type' => 'js',
),
'95b66c1a' =>
array (
'name' => 'differential.pkg.css',
'symbols' =>
array (
0 => 'differential-core-view-css',
1 => 'differential-changeset-view-css',
2 => 'differential-revision-detail-css',
3 => 'differential-revision-history-css',
4 => 'differential-table-of-contents-css',
5 => 'differential-revision-comment-css',
6 => 'differential-revision-add-comment-css',
7 => 'differential-revision-comment-list-css',
),
'uri' => '/res/pkg/95b66c1a/differential.pkg.css',
'type' => 'css',
),
'a06761eb' =>
array (
'name' => 'core.pkg.css',
'symbols' =>
array (
0 => 'phabricator-core-css',
1 => 'phabricator-core-buttons-css',
2 => 'phabricator-standard-page-view',
3 => 'aphront-dialog-view-css',
4 => 'aphront-form-view-css',
5 => 'aphront-panel-view-css',
6 => 'aphront-side-nav-view-css',
7 => 'aphront-table-view-css',
8 => 'aphront-crumbs-view-css',
9 => 'aphront-tokenizer-control-css',
10 => 'aphront-typeahead-control-css',
11 => 'aphront-list-filter-view-css',
12 => 'phabricator-directory-css',
13 => 'phabricator-remarkup-css',
14 => 'syntax-highlighting-css',
),
'uri' => '/res/pkg/a06761eb/core.pkg.css',
'type' => 'css',
),
'd0713563' =>
array (
'name' => 'workflow.pkg.js',
'symbols' =>
array (
0 => 'javelin-mask',
1 => 'javelin-workflow',
2 => 'javelin-behavior-workflow',
3 => 'javelin-behavior-aphront-form-disable-on-submit',
4 => 'phabricator-keyboard-shortcut-manager',
5 => 'phabricator-keyboard-shortcut',
6 => 'javelin-behavior-phabricator-keyboard-shortcuts',
),
'uri' => '/res/pkg/d0713563/workflow.pkg.js',
'type' => 'js',
),
'da416e1c' =>
array (
'name' => 'differential.pkg.js',
'symbols' =>
array (
0 => 'javelin-behavior-differential-feedback-preview',
1 => 'javelin-behavior-differential-edit-inline-comments',
2 => 'javelin-behavior-differential-populate',
3 => 'javelin-behavior-differential-show-more',
4 => 'javelin-behavior-differential-diff-radios',
),
'uri' => '/res/pkg/da416e1c/differential.pkg.js',
'type' => 'js',
),
),
'reverse' =>
array (
'aphront-crumbs-view-css' => 'a06761eb',
'aphront-dialog-view-css' => 'a06761eb',
'aphront-form-view-css' => 'a06761eb',
'aphront-list-filter-view-css' => 'a06761eb',
'aphront-panel-view-css' => 'a06761eb',
'aphront-side-nav-view-css' => 'a06761eb',
'aphront-table-view-css' => 'a06761eb',
'aphront-tokenizer-control-css' => 'a06761eb',
'aphront-typeahead-control-css' => 'a06761eb',
'differential-changeset-view-css' => '95b66c1a',
'differential-core-view-css' => '95b66c1a',
'differential-revision-add-comment-css' => '95b66c1a',
'differential-revision-comment-css' => '95b66c1a',
'differential-revision-comment-list-css' => '95b66c1a',
'differential-revision-detail-css' => '95b66c1a',
'differential-revision-history-css' => '95b66c1a',
'differential-table-of-contents-css' => '95b66c1a',
'diffusion-commit-view-css' => '03ef179e',
'javelin-behavior' => '307df223',
'javelin-behavior-aphront-basic-tokenizer' => '25f94e94',
'javelin-behavior-aphront-form-disable-on-submit' => 'd0713563',
'javelin-behavior-differential-diff-radios' => 'da416e1c',
'javelin-behavior-differential-edit-inline-comments' => 'da416e1c',
'javelin-behavior-differential-feedback-preview' => 'da416e1c',
'javelin-behavior-differential-populate' => 'da416e1c',
'javelin-behavior-differential-show-more' => 'da416e1c',
'javelin-behavior-phabricator-keyboard-shortcuts' => 'd0713563',
'javelin-behavior-workflow' => 'd0713563',
'javelin-dom' => '307df223',
'javelin-event' => '307df223',
'javelin-install' => '307df223',
'javelin-json' => '307df223',
'javelin-mask' => 'd0713563',
'javelin-request' => '307df223',
'javelin-stratcom' => '307df223',
'javelin-tokenizer' => '25f94e94',
'javelin-typeahead' => '25f94e94',
'javelin-typeahead-normalizer' => '25f94e94',
'javelin-typeahead-ondemand-source' => '25f94e94',
'javelin-typeahead-preloaded-source' => '25f94e94',
'javelin-typeahead-source' => '25f94e94',
'javelin-uri' => '307df223',
'javelin-util' => '307df223',
'javelin-vector' => '307df223',
'javelin-workflow' => 'd0713563',
'phabricator-core-buttons-css' => 'a06761eb',
'phabricator-core-css' => 'a06761eb',
'phabricator-directory-css' => 'a06761eb',
'phabricator-keyboard-shortcut' => 'd0713563',
'phabricator-keyboard-shortcut-manager' => 'd0713563',
'phabricator-remarkup-css' => 'a06761eb',
'phabricator-standard-page-view' => 'a06761eb',
'syntax-highlighting-css' => 'a06761eb',
),
));
diff --git a/src/aphront/console/plugin/base/DarkConsolePlugin.php b/src/aphront/console/plugin/base/DarkConsolePlugin.php
index 7350979925..5fe185d105 100644
--- a/src/aphront/console/plugin/base/DarkConsolePlugin.php
+++ b/src/aphront/console/plugin/base/DarkConsolePlugin.php
@@ -1,88 +1,92 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
abstract class DarkConsolePlugin {
private $data;
private $request;
private $core;
abstract public function getName();
abstract public function getDescription();
abstract public function render();
public function __construct() {
}
public function setConsoleCore(DarkConsoleCore $core) {
$this->core = $core;
return $this;
}
public function getConsoleCore() {
return $this->core;
}
public function generateData() {
return null;
}
public function setData($data) {
$this->data = $data;
return $this;
}
public function getData() {
return $this->data;
}
public function setRequest($request) {
$this->request = $request;
return $this;
}
public function getRequest() {
return $this->request;
}
+ public function getRequestURI() {
+ return $this->getRequest()->getRequestURI();
+ }
+
public function isPermanent() {
return false;
}
public function shouldStartup() {
return true;
}
public function didStartup() {
return null;
}
public function willShutdown() {
return null;
}
public function didShutdown() {
return null;
}
public function processRequest() {
return null;
}
}
diff --git a/src/aphront/console/plugin/services/DarkConsoleServicesPlugin.php b/src/aphront/console/plugin/services/DarkConsoleServicesPlugin.php
index dc053dfa6e..5cda555c6f 100644
--- a/src/aphront/console/plugin/services/DarkConsoleServicesPlugin.php
+++ b/src/aphront/console/plugin/services/DarkConsoleServicesPlugin.php
@@ -1,95 +1,276 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class DarkConsoleServicesPlugin extends DarkConsolePlugin {
protected $observations;
public function getName() {
return 'Services';
}
public function getDescription() {
return 'Information about services.';
}
public function generateData() {
+ $log = PhutilServiceProfiler::getInstance()->getServiceCallLog();
+ foreach ($log as $key => $entry) {
+ $config = $entry['config'];
+ unset($log[$key]['config']);
+
+ if (empty($_REQUEST['__analyze__'])) {
+ $log[$key]['explain'] = array(
+ 'sev' => 7,
+ 'size' => null,
+ 'reason' => 'Disabled',
+ );
+ // Query analysis is disabled for this request, so don't do any of it.
+ continue;
+ }
+
+ if ($entry['type'] != 'query') {
+ continue;
+ }
+
+ // For each SELECT query, go issue an EXPLAIN on it so we can flag stuff
+ // causing table scans, etc.
+ if (preg_match('/^\s*SELECT\b/i', $entry['query'])) {
+ $conn = new AphrontMySQLDatabaseConnection($entry['config']);
+ try {
+ $explain = queryfx_all(
+ $conn,
+ 'EXPLAIN %Q',
+ $entry['query']);
+
+ $badness = 0;
+ $size = 1;
+ $reason = null;
+
+ foreach ($explain as $table) {
+ $size *= (int)$table['rows'];
+
+ switch ($table['type']) {
+ case 'index':
+ $cur_badness = 1;
+ $cur_reason = 'Index';
+ break;
+ case 'const':
+ $cur_badness = 1;
+ $cur_reason = 'Const';
+ break;
+ case 'eq_ref';
+ $cur_badness = 2;
+ $cur_reason = 'EqRef';
+ break;
+ case 'range':
+ $cur_badness = 3;
+ $cur_reason = 'Range';
+ break;
+ case 'ref':
+ $cur_badness = 3;
+ $cur_reason = 'Ref';
+ break;
+ case 'ALL':
+ if (preg_match('/Using where/', $table['Extra'])) {
+ if ($table['rows'] < 256 && !empty($table['possible_keys'])) {
+ $cur_badness = 2;
+ $cur_reason = 'Small Table Scan';
+ } else {
+ $cur_badness = 6;
+ $cur_reason = 'TABLE SCAN!';
+ }
+ } else {
+ $cur_badness = 3;
+ $cur_reason = 'Whole Table';
+ }
+ break;
+ default:
+ if (preg_match('/No tables used/i', $table['Extra'])) {
+ $cur_badness = 1;
+ $cur_reason = 'No Tables';
+ } else if (preg_match('/Impossible/i', $table['Extra'])) {
+ $cur_badness = 1;
+ $cur_reason = 'Empty';
+ } else {
+ $cur_badness = 4;
+ $cur_reason = "Can't Analyze";
+ }
+ break;
+ }
+
+ if ($cur_badness > $badness) {
+ $badness = $cur_badness;
+ $reason = $cur_reason;
+ }
+ }
+
+ $log[$key]['explain'] = array(
+ 'sev' => $badness,
+ 'size' => $size,
+ 'reason' => $reason,
+ );
+ } catch (Exception $ex) {
+ $log[$key]['explain'] = array(
+ 'sev' => 5,
+ 'size' => null,
+ 'reason' => $ex->getMessage(),
+ );
+ }
+ }
+ }
+
return array(
'start' => $GLOBALS['__start__'],
- 'log' => PhutilServiceProfiler::getInstance()->getServiceCallLog(),
+ 'end' => microtime(true),
+ 'log' => $log,
);
}
public function render() {
$data = $this->getData();
$log = $data['log'];
+ $results = array();
+
+ $results[] =
+ '<div class="dark-console-panel-header">'.
+ phutil_render_tag(
+ 'a',
+ array(
+ 'href' => $this->getRequestURI()->alter('__analyze__', true),
+ 'class' => isset($_REQUEST['__analyze__'])
+ ? 'disabled button'
+ : 'green button',
+ ),
+ 'Analyze Query Plans').
+ '<h1>Calls to External Services</h1>'.
+ '<div style="clear: both;"></div>'.
+ '</div>';
+
+ $page_total = $data['end'] - $data['start'];
+ $totals = array();
+ $counts = array();
+
+ foreach ($log as $row) {
+ $totals[$row['type']] += $row['duration'];
+ $counts[$row['type']]++;
+ }
+ $totals['All'] = array_sum($totals);
+ $counts['All'] = array_sum($counts);
+
+ $table = new AphrontTableView();
+ $summary = array();
+ foreach ($totals as $type => $total) {
+ $summary[] = array(
+ $type,
+ number_format($counts[$type]),
+ number_format((int)(1000000 * $totals[$type])).' us',
+ sprintf('%.1f%%', 100 * $totals[$type] / $page_total),
+ );
+ }
+ $summary_table = new AphrontTableView($summary);
+ $summary_table->setColumnClasses(
+ array(
+ '',
+ 'n',
+ 'n',
+ 'wide',
+ ));
+ $summary_table->setHeaders(
+ array(
+ 'Type',
+ 'Count',
+ 'Total Cost',
+ 'Page Weight',
+ ));
+
+ $results[] = $summary_table->render();
$rows = array();
foreach ($log as $row) {
+ $analysis = null;
+
switch ($row['type']) {
case 'query':
$info = $row['query'];
+ $info = wordwrap($info, 128, "\n", true);
+
+ if (!empty($row['explain'])) {
+ $analysis = phutil_escape_html($row['explain']['reason']);
+ $analysis = phutil_render_tag(
+ 'span',
+ array(
+ 'class' => 'explain-sev-'.$row['explain']['sev'],
+ ),
+ $analysis);
+ }
+
$info = phutil_escape_html($info);
break;
case 'connect':
$info = $row['host'].':'.$row['database'];
$info = phutil_escape_html($info);
break;
case 'exec':
$info = $row['command'];
$info = phutil_escape_html($info);
break;
case 'conduit':
$info = $row['method'];
$info = phutil_escape_html($info);
break;
default:
$info = '-';
break;
}
$rows[] = array(
phutil_escape_html($row['type']),
'+'.number_format(1000 * ($row['begin'] - $data['start'])).' ms',
number_format(1000000 * $row['duration']).' us',
$info,
+ $analysis,
);
}
$table = new AphrontTableView($rows);
$table->setColumnClasses(
array(
null,
'n',
'n',
- 'wide wrap',
+ 'wide',
+ '',
));
$table->setHeaders(
array(
'Event',
'Start',
'Duration',
'Details',
+ 'Analysis',
));
- return $table->render();
+ $results[] = $table->render();
+
+ return implode("\n", $results);
}
}
diff --git a/src/aphront/console/plugin/services/__init__.php b/src/aphront/console/plugin/services/__init__.php
index 00234db119..a89b976384 100644
--- a/src/aphront/console/plugin/services/__init__.php
+++ b/src/aphront/console/plugin/services/__init__.php
@@ -1,16 +1,18 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'aphront/console/plugin/base');
+phutil_require_module('phabricator', 'storage/connection/mysql');
+phutil_require_module('phabricator', 'storage/queryfx');
phutil_require_module('phabricator', 'view/control/table');
phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'serviceprofiler');
phutil_require_source('DarkConsoleServicesPlugin.php');
diff --git a/src/aphront/console/plugin/xhprof/DarkConsoleXHProfPlugin.php b/src/aphront/console/plugin/xhprof/DarkConsoleXHProfPlugin.php
index c12181493b..31550e98a2 100644
--- a/src/aphront/console/plugin/xhprof/DarkConsoleXHProfPlugin.php
+++ b/src/aphront/console/plugin/xhprof/DarkConsoleXHProfPlugin.php
@@ -1,135 +1,157 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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 getDescription() {
return 'Provides detailed PHP profiling information through XHProf.';
}
public function generateData() {
return $this->xhprofID;
}
public function getXHProfRunID() {
return $this->xhprofID;
}
public function render() {
if (!DarkConsoleXHProfPluginAPI::isProfilerAvailable()) {
+ $href = PhabricatorEnv::getDoclink('article/Installation_Guide.html');
+ $install_guide = phutil_render_tag(
+ 'a',
+ array(
+ 'href' => $href,
+ 'class' => 'bright-link',
+ ),
+ 'Installation Guide');
return
- '<p>The "xhprof" PHP extension is not available. Install xhprof '.
- 'to enable the XHProf plugin.';
+ '<div class="dark-console-no-content">'.
+ 'The "xhprof" PHP extension is not available. Install xhprof '.
+ 'to enable the XHProf console plugin. You can find instructions in '.
+ 'the '.$install_guide.'.'.
+ '</div>';
}
+ $result = array();
+
$run = $this->getXHProfRunID();
- if ($run) {
- return '<a href="/xhprof/profile/'.$run.'/">View Run</a>';
- } else {
- $hidden = array();
- $data = array('__profile__' => 'page') + $_GET;
- foreach ($data as $k => $v) {
- $hidden[] = phutil_render_tag(
- 'input',
+ $header =
+ '<div class="dark-console-panel-header">'.
+ phutil_render_tag(
+ 'a',
array(
- 'type' => 'hidden',
- 'name' => $k,
- 'value' => $v,
- ));
- }
- $hidden = implode("\n", $hidden);
-
+ 'href' => $this->getRequestURI()->alter('__profile__', 'page'),
+ 'class' => $run
+ ? 'disabled button'
+ : 'green button',
+ ),
+ 'Profile Page').
+ '<h1>XHProf Profiler</h1>'.
+ '</div>';
+ $result[] = $header;
- return
- '<form method="get">'.
- $hidden.
- '<button>Enable XHProf</button>'.
- '</form>';
+ if ($run) {
+ $result[] =
+ '<a href="/xhprof/profile/'.$run.'/" '.
+ 'class="bright-link" '.
+ 'style="float: right; margin: 1em 2em 0 0;'.
+ 'font-weight: bold;" '.
+ 'target="_blank">Profile Permalink</a>'.
+ '<iframe src="/xhprof/profile/'.$run.'/?frame=true"></iframe>';
+ } else {
+ $result[] =
+ '<div class="dark-console-no-content">'.
+ 'Profiling was not enabled for this page. Use the button above '.
+ 'to enable it.'.
+ '</div>';
}
+
+ return implode("\n", $result);
}
public function willShutdown() {
if (isset($_REQUEST['__profile__']) &&
$_REQUEST['__profile__'] != 'all') {
$this->xhprofID = DarkConsoleXHProfPluginAPI::stopProfiler();
}
}
}
/*
public function render() {
$run = $this->getData();
if ($run) {
$uri = 'http://www.intern.facebook.com/intern/phprof/?run='.$run;
return
<x:frag>
<h1>XHProf Results</h1>
<div class="XHProfPlugin">
<a href={$uri} target="_blank" class="XHProfPlugin">Permalink</a>
<iframe src={$uri} width="100%" height="600" />
</div>
</x:frag>;
}
$uri = URI::getRequestURI();
return
<x:frag>
<h1>XHProf</h1>
<form action={$uri} method="get" class="EnableFeature">
<fieldset>
<legend>Enable Profiling</legend>
<p>Profiling was not enabled for this request. Click the button
below to rerun the request with profiling enabled.</p>
<button type="submit" name="_profile_" value="all"
style="margin: 2px 1em; width: 75%;">
Profile Page (With Includes)
</button>
<button type="submit" name="_profile_" value="exec"
style="margin: 2px 1em; width: 75%;">
Profile Page (No Includes)
</button>
</fieldset>
</form>
</x:frag>;
}
public function didShutdown() {
if ($this->xhprofData) {
require_module_lazy('profiling/phprof');
$this->xhprofID = phprof_save_run($this->xhprofData);
}
}
}
*/
diff --git a/src/aphront/console/plugin/xhprof/__init__.php b/src/aphront/console/plugin/xhprof/__init__.php
index 32fd5d88d9..1e2cbd9cf3 100644
--- a/src/aphront/console/plugin/xhprof/__init__.php
+++ b/src/aphront/console/plugin/xhprof/__init__.php
@@ -1,15 +1,16 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'aphront/console/plugin/base');
phutil_require_module('phabricator', 'aphront/console/plugin/xhprof/api');
+phutil_require_module('phabricator', 'infrastructure/env');
phutil_require_module('phutil', 'markup');
phutil_require_source('DarkConsoleXHProfPlugin.php');
diff --git a/src/applications/files/controller/list/PhabricatorFileListController.php b/src/applications/files/controller/list/PhabricatorFileListController.php
index f78af9fd2c..85befebfcd 100644
--- a/src/applications/files/controller/list/PhabricatorFileListController.php
+++ b/src/applications/files/controller/list/PhabricatorFileListController.php
@@ -1,121 +1,122 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class PhabricatorFileListController extends PhabricatorFileController {
public function processRequest() {
$request = $this->getRequest();
+ $author = null;
$author_username = $request->getStr('author');
if ($author_username) {
$author = id(new PhabricatorUser())->loadOneWhere(
'userName = %s',
$author_username);
if (!$author) {
return id(new Aphront404Response());
}
}
$pager = new AphrontPagerView();
$pager->setOffset($request->getInt('page'));
if ($author) {
$files = id(new PhabricatorFile())->loadAllWhere(
'authorPHID = %s ORDER BY id DESC LIMIT %d, %d',
$author->getPHID(),
$pager->getOffset(),
$pager->getPageSize() + 1);
} else {
$files = id(new PhabricatorFile())->loadAllWhere(
'1 = 1 ORDER BY id DESC LIMIT %d, %d',
$pager->getOffset(),
$pager->getPageSize() + 1);
}
$files = $pager->sliceResults($files);
$pager->setURI($request->getRequestURI(), 'page');
$rows = array();
foreach ($files as $file) {
if ($file->isViewableInBrowser()) {
$view_button = phutil_render_tag(
'a',
array(
'class' => 'small button grey',
'href' => '/file/view/'.$file->getPHID().'/',
),
'View');
} else {
$view_button = null;
}
$rows[] = array(
phutil_escape_html($file->getPHID()),
phutil_escape_html($file->getName()),
phutil_escape_html($file->getByteSize()),
phutil_render_tag(
'a',
array(
'class' => 'small button grey',
'href' => '/file/info/'.$file->getPHID().'/',
),
'Info'),
$view_button,
phutil_render_tag(
'a',
array(
'class' => 'small button grey',
'href' => '/file/download/'.$file->getPHID().'/',
),
'Download'),
);
}
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
'PHID',
'Name',
'Size',
'',
'',
'',
));
$table->setColumnClasses(
array(
null,
'wide',
null,
'action',
'action',
'action',
));
$panel = new AphrontPanelView();
$panel->appendChild($table);
$panel->setHeader('Files');
$panel->setCreateButton('Upload File', '/file/upload/');
$panel->appendChild($pager);
return $this->buildStandardPageResponse($panel, array(
'title' => 'Files',
'tab' => 'files',
));
}
}
diff --git a/src/applications/xhprof/controller/base/PhabricatorXHProfController.php b/src/applications/xhprof/controller/base/PhabricatorXHProfController.php
index fff91acb0d..b6e3b73d48 100644
--- a/src/applications/xhprof/controller/base/PhabricatorXHProfController.php
+++ b/src/applications/xhprof/controller/base/PhabricatorXHProfController.php
@@ -1,34 +1,42 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
abstract class PhabricatorXHProfController extends PhabricatorController {
public function buildStandardPageResponse($view, array $data) {
$page = $this->buildStandardPageView();
$page->setApplicationName('XHProf');
$page->setBaseURI('/xhprof/');
$page->setTitle(idx($data, 'title'));
$page->setGlyph("\xE2\x98\x84");
$page->appendChild($view);
$response = new AphrontWebpageResponse();
+
+ if (isset($data['frame'])) {
+ $response->setFrameable(true);
+ $page->setFrameable(true);
+ $page->setShowChrome(false);
+ $page->setDisableConsole(true);
+ }
+
return $response->setContent($page->render());
}
}
diff --git a/src/applications/xhprof/controller/profile/PhabricatorXHProfProfileController.php b/src/applications/xhprof/controller/profile/PhabricatorXHProfProfileController.php
index 1eca5b23be..cf0b954d7d 100644
--- a/src/applications/xhprof/controller/profile/PhabricatorXHProfProfileController.php
+++ b/src/applications/xhprof/controller/profile/PhabricatorXHProfProfileController.php
@@ -1,63 +1,64 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class PhabricatorXHProfProfileController
extends PhabricatorXHProfController {
private $phid;
public function willProcessRequest(array $data) {
$this->phid = $data['phid'];
}
public function processRequest() {
$file = id(new PhabricatorFile())->loadOneWhere(
'phid = %s',
$this->phid);
if (!$file) {
return new Aphront404Response();
}
$data = $file->loadFileData();
$data = unserialize($data);
if (!$data) {
throw new Exception("Failed to unserialize XHProf profile!");
}
$request = $this->getRequest();
$symbol = $request->getStr('symbol');
if ($symbol) {
$view = new PhabricatorXHProfProfileSymbolView();
$view->setProfileData($data);
$view->setSymbol($symbol);
} else {
$view = new PhabricatorXHProfProfileTopLevelView();
$view->setProfileData($data);
$view->setLimit(100);
}
return $this->buildStandardPageResponse(
$view,
array(
'title' => 'Profile',
+ 'frame' => $request->getBool('frame'),
));
}
}
diff --git a/src/storage/connection/mysql/AphrontMySQLDatabaseConnection.php b/src/storage/connection/mysql/AphrontMySQLDatabaseConnection.php
index 93e120233e..4f5a21bc01 100644
--- a/src/storage/connection/mysql/AphrontMySQLDatabaseConnection.php
+++ b/src/storage/connection/mysql/AphrontMySQLDatabaseConnection.php
@@ -1,270 +1,271 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @group storage
*/
class AphrontMySQLDatabaseConnection extends AphrontDatabaseConnection {
private $config;
private $connection;
private static $connectionCache = array();
public function __construct(array $configuration) {
$this->configuration = $configuration;
}
public function escapeString($string) {
$this->requireConnection();
return mysql_real_escape_string($string, $this->connection);
}
public function escapeColumnName($name) {
return '`'.str_replace('`', '\\`', $name).'`';
}
public function escapeMultilineComment($comment) {
// These can either terminate a comment, confuse the hell out of the parser,
// make MySQL execute the comment as a query, or, in the case of semicolon,
// are quasi-dangerous because the semicolon could turn a broken query into
// a working query plus an ignored query.
static $map = array(
'--' => '(DOUBLEDASH)',
'*/' => '(STARSLASH)',
'//' => '(SLASHSLASH)',
'#' => '(HASH)',
'!' => '(BANG)',
';' => '(SEMICOLON)',
);
$comment = str_replace(
array_keys($map),
array_values($map),
$comment);
// For good measure, kill anything else that isn't a nice printable
// character.
$comment = preg_replace('/[^\x20-\x7F]+/', ' ', $comment);
return '/* '.$comment.' */';
}
public function escapeStringForLikeClause($value) {
$value = $this->escapeString($value);
// Ideally the query shouldn't be modified after safely escaping it,
// but we need to escape _ and % within LIKE terms.
$value = str_replace(
// Even though we've already escaped, we need to replace \ with \\
// because MYSQL unescapes twice inside a LIKE clause. See note
// at mysql.com. However, if the \ is being used to escape a single
// quote ('), then the \ should not be escaped. Thus, after all \
// are replaced with \\, we need to revert instances of \\' back to
// \'.
array('\\', '\\\\\'', '_', '%'),
array('\\\\', '\\\'', '\_', '\%'),
$value);
return $value;
}
private function getConfiguration($key, $default = null) {
return idx($this->configuration, $key, $default);
}
private function closeConnection() {
if ($this->connection) {
$this->connection = null;
$key = $this->getConnectionCacheKey();
unset(self::$connectionCache[$key]);
}
}
private function getConnectionCacheKey() {
$user = $this->getConfiguration('user');
$host = $this->getConfiguration('host');
$database = $this->getConfiguration('database');
return "{$user}:{$host}:{$database}";
}
private function establishConnection() {
$this->closeConnection();
$user = $this->getConfiguration('user');
$host = $this->getConfiguration('host');
$database = $this->getConfiguration('database');
$key = $this->getConnectionCacheKey();
if (isset(self::$connectionCache[$key])) {
$this->connection = self::$connectionCache[$key];
return;
}
$start = microtime(true);
if (!function_exists('mysql_connect')) {
// We have to '@' the actual call since it can spew all sorts of silly
// noise, but it will also silence fatals caused by not having MySQL
// installed, which has bitten me on three separate occasions. Make sure
// such failures are explicit and loud.
throw new Exception(
"About to call mysql_connect(), but the PHP MySQL extension is not ".
"available!");
}
$profiler = PhutilServiceProfiler::getInstance();
$call_id = $profiler->beginServiceCall(
array(
'type' => 'connect',
'host' => $host,
'database' => $database,
));
try {
$conn = @mysql_connect(
$host,
$user,
$this->getConfiguration('pass'),
$new_link = true,
$flags = 0);
if (!$conn) {
$errno = mysql_errno();
$error = mysql_error();
throw new AphrontQueryConnectionException(
"Attempt to connect to {$user}@{$host} failed with error #{$errno}: ".
"{$error}.");
}
if ($database !== null) {
$ret = @mysql_select_db($database, $conn);
if (!$ret) {
$this->throwQueryException($conn);
}
}
$profiler->endServiceCall($call_id, array());
} catch (Exception $ex) {
$profiler->endServiceCall($call_id, array());
throw $ex;
}
self::$connectionCache[$key] = $conn;
$this->connection = $conn;
}
public function getInsertID() {
return mysql_insert_id($this->requireConnection());
}
public function getAffectedRows() {
return mysql_affected_rows($this->requireConnection());
}
public function getTransactionKey() {
return (int)$this->requireConnection();
}
private function requireConnection() {
if (!$this->connection) {
$this->establishConnection();
}
return $this->connection;
}
public function selectAllResults() {
$result = array();
$res = $this->lastResult;
if ($res == null) {
throw new Exception('No query result to fetch from!');
}
while (($row = mysql_fetch_assoc($res)) !== false) {
$result[] = $row;
}
return $result;
}
public function executeRawQuery($raw_query) {
$this->lastResult = null;
$retries = 3;
while ($retries--) {
try {
$this->requireConnection();
$start = microtime(true);
$profiler = PhutilServiceProfiler::getInstance();
$call_id = $profiler->beginServiceCall(
array(
- 'type' => 'query',
- 'query' => $raw_query,
+ 'type' => 'query',
+ 'config' => $this->configuration,
+ 'query' => $raw_query,
));
$result = @mysql_query($raw_query, $this->connection);
$profiler->endServiceCall($call_id, array());
if ($result) {
$this->lastResult = $result;
break;
}
$this->throwQueryException($this->connection);
} catch (AphrontQueryConnectionLostException $ex) {
if (!$retries) {
throw $ex;
}
if ($this->isInsideTransaction()) {
throw $ex;
}
$this->closeConnection();
}
}
}
private function throwQueryException($connection) {
$errno = mysql_errno($connection);
$error = mysql_error($connection);
switch ($errno) {
case 2013: // Connection Dropped
case 2006: // Gone Away
throw new AphrontQueryConnectionLostException("#{$errno}: {$error}");
case 1213: // Deadlock
case 1205: // Lock wait timeout exceeded
throw new AphrontQueryRecoverableException("#{$errno}: {$error}");
case 1062: // Duplicate Key
// NOTE: In some versions of MySQL we get a key name back here, but
// older versions just give us a key index ("key 2") so it's not
// portable to parse the key out of the error and attach it to the
// exception.
throw new AphrontQueryDuplicateKeyException("{$errno}: {$error}");
case 1044: // Access denied to database
case 1045: // Access denied (auth)
case 1142: // Access denied to table
case 1143: // Access denied to column
throw new AphrontQueryAccessDeniedException("#{$errno}: {$error}");
default:
// TODO: 1064 is syntax error, and quite terrible in production.
throw new AphrontQueryException("#{$errno}: {$error}");
}
}
}
diff --git a/src/view/page/standard/PhabricatorStandardPageView.php b/src/view/page/standard/PhabricatorStandardPageView.php
index e5dec0e373..b5abe90c11 100644
--- a/src/view/page/standard/PhabricatorStandardPageView.php
+++ b/src/view/page/standard/PhabricatorStandardPageView.php
@@ -1,348 +1,372 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class PhabricatorStandardPageView extends AphrontPageView {
private $baseURI;
private $applicationName;
private $tabs = array();
private $selectedTab;
private $glyph;
private $bodyContent;
private $request;
private $isAdminInterface;
private $showChrome = true;
+ private $isFrameable = false;
+ private $disableConsole;
public function setIsAdminInterface($is_admin_interface) {
$this->isAdminInterface = $is_admin_interface;
return $this;
}
public function getIsAdminInterface() {
return $this->isAdminInterface;
}
public function setRequest($request) {
$this->request = $request;
return $this;
}
public function getRequest() {
return $this->request;
}
public function setApplicationName($application_name) {
$this->applicationName = $application_name;
return $this;
}
+ public function setFrameable($frameable) {
+ $this->isFrameable = $frameable;
+ return $this;
+ }
+
+ public function setDisableConsole($disable) {
+ $this->disableConsole = $disable;
+ return $this;
+ }
+
public function getApplicationName() {
return $this->applicationName;
}
public function setBaseURI($base_uri) {
$this->baseURI = $base_uri;
return $this;
}
public function getBaseURI() {
return $this->baseURI;
}
public function setTabs(array $tabs, $selected_tab) {
$this->tabs = $tabs;
$this->selectedTab = $selected_tab;
return $this;
}
public function setShowChrome($show_chrome) {
$this->showChrome = $show_chrome;
return $this;
}
public function getShowChrome() {
return $this->showChrome;
}
public function getTitle() {
$use_glyph = true;
$request = $this->getRequest();
if ($request) {
$user = $request->getUser();
if ($user && $user->loadPreferences()->getPreference(
PhabricatorUserPreferences::PREFERENCE_TITLES) !== 'glyph') {
$use_glyph = false;
}
}
return ($use_glyph ?
$this->getGlyph() : '['.$this->getApplicationName().']').
' '.parent::getTitle();
}
protected function willRenderPage() {
if (!$this->getRequest()) {
throw new Exception(
"You must set the Request to render a PhabricatorStandardPageView.");
}
- $console = $this->getRequest()->getApplicationConfiguration()->getConsole();
+ $console = $this->getConsole();
require_celerity_resource('phabricator-core-css');
require_celerity_resource('phabricator-core-buttons-css');
require_celerity_resource('phabricator-standard-page-view');
Javelin::initBehavior('workflow', array());
Javelin::initBehavior(
'phabricator-keyboard-shortcuts',
array(
'helpURI' => '/help/keyboardshortcut/',
));
if ($console) {
require_celerity_resource('aphront-dark-console-css');
Javelin::initBehavior(
'dark-console',
array(
'uri' => '/~/',
));
// Change this to initBehavior when there is some behavior to initialize
require_celerity_resource('javelin-behavior-error-log');
}
$this->bodyContent = $this->renderChildren();
}
protected function getHead() {
+
+ $framebust = null;
+ if (!$this->isFrameable) {
+ $framebust = '(top != self) && top.location.replace(self.location.href);';
+ }
+
$response = CelerityAPI::getStaticResourceResponse();
$head =
'<script type="text/javascript">'.
- '(top != self) && top.location.replace(self.location.href);'.
+ $framebust.
'window.__DEV__=1;'.
'</script>'.
$response->renderResourcesOfType('css').
$response->renderSingleResource('javelin-magical-init');
$request = $this->getRequest();
if ($request) {
$user = $request->getUser();
if ($user) {
$monospaced = $user->loadPreferences()->getPreference(
PhabricatorUserPreferences::PREFERENCE_MONOSPACED
);
if (strlen($monospaced)) {
$head .=
'<style type="text/css">'.
'.PhabricatorMonospaced { font: '.
$monospaced.
' !important; }'.
'</style>';
}
}
}
return $head;
}
public function setGlyph($glyph) {
$this->glyph = $glyph;
return $this;
}
public function getGlyph() {
return $this->glyph;
}
protected function willSendResponse($response) {
$console = $this->getRequest()->getApplicationConfiguration()->getConsole();
if ($console) {
$response = str_replace(
'<darkconsole />',
$console->render($this->getRequest()),
$response);
}
return $response;
}
protected function getBody() {
- $console = $this->getRequest()->getApplicationConfiguration()->getConsole();
+ $console = $this->getConsole();
$tabs = array();
foreach ($this->tabs as $name => $tab) {
$tab_markup = phutil_render_tag(
'a',
array(
'href' => idx($tab, 'href'),
),
phutil_escape_html(idx($tab, 'name')));
$tab_markup = phutil_render_tag(
'td',
array(
'class' => ($name == $this->selectedTab)
? 'phabricator-selected-tab'
: null,
),
$tab_markup);
$tabs[] = $tab_markup;
}
$tabs = implode('', $tabs);
$login_stuff = null;
$request = $this->getRequest();
$user = null;
if ($request) {
$user = $request->getUser();
// NOTE: user may not be set here if we caught an exception early
// in the execution workflow.
if ($user && $user->getPHID()) {
$login_stuff =
phutil_render_tag(
'a',
array(
'href' => '/p/'.$user->getUsername().'/',
),
phutil_escape_html($user->getUsername())).
' &middot; '.
'<a href="/settings/">Settings</a>'.
' &middot; '.
phabricator_render_form(
$user,
array(
'action' => '/search/',
'method' => 'post',
'style' => 'display: inline',
),
'<input type="text" name="query" />'.
'<button>Search</button>');
}
}
$foot_links = array();
$version = PhabricatorEnv::getEnvConfig('phabricator.version');
$foot_links[] = phutil_escape_html('Phabricator '.$version);
if (PhabricatorEnv::getEnvConfig('darkconsole.enabled') &&
!PhabricatorEnv::getEnvConfig('darkconsole.always-on')) {
if ($console) {
$link = javelin_render_tag(
'a',
array(
'href' => '/~/',
'sigil' => 'workflow',
),
'Disable DarkConsole');
} else {
$link = javelin_render_tag(
'a',
array(
'href' => '/~/',
'sigil' => 'workflow',
),
'Enable DarkConsole');
}
$foot_links[] = $link;
}
if ($user && $user->getPHID()) {
// This ends up very early in tab order at the top of the page and there's
// a bunch of junk up there anyway, just shove it down here.
$foot_links[] = phabricator_render_form(
$user,
array(
'action' => '/logout/',
'method' => 'post',
'style' => 'display: inline',
),
'<button class="link">Logout</button>');
}
$foot_links = implode(' &middot; ', $foot_links);
$admin_class = null;
if ($this->getIsAdminInterface()) {
$admin_class = 'phabricator-admin-page-view';
}
$header_chrome = null;
$footer_chrome = null;
if ($this->getShowChrome()) {
$header_chrome =
'<table class="phabricator-standard-header">'.
'<tr>'.
'<td class="phabricator-logo"><a href="/"> </a></td>'.
'<td>'.
'<table class="phabricator-primary-navigation">'.
'<tr>'.
'<th>'.
phutil_render_tag(
'a',
array(
'href' => $this->getBaseURI(),
'class' => 'phabricator-head-appname',
),
phutil_escape_html($this->getApplicationName())).
'</th>'.
$tabs.
'</tr>'.
'</table>'.
'</td>'.
'<td class="phabricator-login-details">'.
$login_stuff.
'</td>'.
'</tr>'.
'</table>';
$footer_chrome =
'<div class="phabricator-page-foot">'.
$foot_links.
'</div>';
}
return
($console ? '<darkconsole />' : null).
'<div class="phabricator-standard-page '.$admin_class.'">'.
$header_chrome.
$this->bodyContent.
'<div style="clear: both;"></div>'.
'</div>'.
$footer_chrome;
}
protected function getTail() {
$response = CelerityAPI::getStaticResourceResponse();
return
$response->renderResourcesOfType('js').
$response->renderHTMLFooter();
}
protected function getBodyClasses() {
$classes = array();
if (!$this->getShowChrome()) {
$classes[] = 'phabricator-chromeless-page';
}
return implode(' ', $classes);
}
+ private function getConsole() {
+ if ($this->disableConsole) {
+ return null;
+ }
+ return $this->getRequest()->getApplicationConfiguration()->getConsole();
+ }
}
diff --git a/webroot/rsrc/css/aphront/dark-console.css b/webroot/rsrc/css/aphront/dark-console.css
index 7fe3d766b5..6c7cda8baf 100644
--- a/webroot/rsrc/css/aphront/dark-console.css
+++ b/webroot/rsrc/css/aphront/dark-console.css
@@ -1,89 +1,154 @@
/**
* @provides aphront-dark-console-css
*/
.dark-console {
background: #555555;
color: #eeeeee;
width: 100%;
font-family: "Verdana";
font-size: 11px;
border-bottom: 2px solid #000000;
position: relative;
z-index: 1;
}
.dark-console-tabs {
width: 180px;
background: #222222;
border-right: 1px solid #888888;
padding: 2.5em 0em;
}
a.dark-console-tab {
padding: .75em 12px;
text-align: right;
background: #444444;
position: relative;
border: 1px solid #666666;
border-width: 1px 0;
border-right-color: #888888;
margin-bottom: 2px;
display: block;
color: #cccccc;
}
a.dark-console-tab-selected {
margin-right: -1px;
padding-right: 13px;
background: #555555;
border-color: #888888;
border-right-color: #555555;
color: #eeeeee;
}
.dark-console .aphront-table-view {
background: #888888;
color: #eeeeee;
margin: 1em 1%;
width: 98%;
border-color: #333333;
}
.dark-console .aphront-table-view th {
background: #333333;
color: #ffffff;
}
.dark-console .aphront-table-view td.header {
background: #444444;
color: #ffffff;
min-width: 200px;
}
.dark-console .aphront-table-view tr.alt {
background: #666666;
}
.dark-console .aphront-table-view tr.no-data td {
color: #dddddd;
}
.dark-console-panel-ErrorLog {
max-height: 500px;
overflow: auto;
}
.dark-console-panel-error-details {
display: none;
}
.dark-console-panel-error-separator {
background-color: #e8e8e8;
border-bottom: 1px solid #b7b7b7;
border-top: 1px solid #b7b7b7;
height: 2px;
}
+.explain-sev-1 {
+ color: #33ff33;
+}
+
+.explain-sev-2 {
+ color: #99ff33;
+}
+
+.explain-sev-3 {
+ color: #ccff33;
+}
+
+.explain-sev-4 {
+ color: #ffff33;
+}
+
+.explain-sev-5 {
+ color: #ffcc33;
+}
+
+.explain-sev-6 {
+ color: #ffffff;
+ font-weight: bold;
+ background: #aa0000;
+ padding: 0 1em;
+ border: 2px solid #ffff00;
+}
+
+.explain-sev-7 {
+ color: #aaaaaa;
+}
+
+.dark-console-panel-header {
+ background: #606060;
+ border-bottom: 1px solid #505050;
+ padding: .25em 1em .25em 0;
+}
+
+.dark-console-panel-header h1 {
+ padding: 1em;
+ font-size: 12px;
+ font-weight: normal;
+}
+
+.dark-console-panel-header .button {
+ margin-top: .5em;
+ float: right;
+}
+
+.dark-console-panel a.bright-link {
+ color: #00cfff;
+ font-weight: bold;
+}
+
+.dark-console iframe {
+ width: 98%;
+ margin: .5em 1%;
+ height: 450px;
+ border: 0;
+}
+
+.dark-console-no-content {
+ padding: 1.5em 2em;
+ font-style: italic;
+}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Jan 19, 13:01 (3 w, 3 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1124854
Default Alt Text
(85 KB)

Event Timeline