diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index 041ae521fc..1da3289989 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -1,415 +1,415 @@ array( 'uri' => '/res/ac3fc983/rsrc/css/aphront/dark-console.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/aphront/dark-console.css', ), 'aphront-dialog-view-css' => array( 'uri' => '/res/a05107ae/rsrc/css/aphront/dialog-view.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/aphront/dialog-view.css', ), 'aphront-form-view-css' => array( 'uri' => '/res/785ac1c6/rsrc/css/aphront/form-view.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/aphront/form-view.css', ), 'aphront-panel-view-css' => array( 'uri' => '/res/63672373/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/97b8337a/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/0fc0545c/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/de3a1e4c/rsrc/css/aphront/table-view.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/aphront/table-view.css', ), 'aphront-tokenizer-control-css' => array( 'uri' => '/res/a3d23074/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/928df9f0/rsrc/css/aphront/typeahead.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/aphront/typeahead.css', ), 'phabricator-standard-page-view' => array( 'uri' => '/res/fb02fb0e/rsrc/css/application/base/standard-page-view.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/application/base/standard-page-view.css', ), 'differential-revision-add-comment-css' => array( - 'uri' => '/res/d7f8719e/rsrc/css/application/differential/add-comment.css', + 'uri' => '/res/9be761de/rsrc/css/application/differential/add-comment.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/application/differential/add-comment.css', ), 'differential-changeset-view-css' => array( 'uri' => '/res/4e0295a9/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/525d1a12/rsrc/css/application/differential/core.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/application/differential/core.css', ), 'differential-revision-comment-list-css' => array( 'uri' => '/res/a1c117db/rsrc/css/application/differential/revision-comment-list.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/application/differential/revision-comment-list.css', ), 'differential-revision-comment-css' => array( - 'uri' => '/res/368bd612/rsrc/css/application/differential/revision-comment.css', + 'uri' => '/res/274eb3f1/rsrc/css/application/differential/revision-comment.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/application/differential/revision-comment.css', ), 'differential-revision-detail-css' => array( 'uri' => '/res/230a67c6/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/755f3da3/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/a4a7b2b5/rsrc/css/application/differential/table-of-contents.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/application/differential/table-of-contents.css', ), 'phabricator-directory-css' => array( 'uri' => '/res/6a000601/rsrc/css/application/directory/phabricator-directory.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/application/directory/phabricator-directory.css', ), 'phabricator-core-buttons-css' => array( 'uri' => '/res/6e348ba4/rsrc/css/core/buttons.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/core/buttons.css', ), 'phabricator-core-css' => array( 'uri' => '/res/39ce37c2/rsrc/css/core/core.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/core/core.css', ), 'phabricator-core-dialog-css' => array( 'uri' => '/res/f66cec41/rsrc/css/core/dialog.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/core/dialog.css', ), 'phabricator-remarkup-css' => array( 'uri' => '/res/786989c3/rsrc/css/core/remarkup.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/core/remarkup.css', ), 'syntax-highlighting-css' => array( 'uri' => '/res/fb673ece/rsrc/css/core/syntax.css', 'type' => 'css', 'requires' => array( ), 'disk' => '/rsrc/css/core/syntax.css', ), 'javelin-behavior-dark-console' => array( - 'uri' => '/res/453503f4/rsrc/js/application/core/behavior-dark-console.js', + 'uri' => '/res/35907b6e/rsrc/js/application/core/behavior-dark-console.js', 'type' => 'js', 'requires' => array( ), 'disk' => '/rsrc/js/application/core/behavior-dark-console.js', ), 'javelin-behavior-aphront-basic-tokenizer' => array( 'uri' => '/res/8317d761/rsrc/js/application/core/behavior-tokenizer.js', 'type' => 'js', 'requires' => array( 0 => 'javelin-lib-dev', ), 'disk' => '/rsrc/js/application/core/behavior-tokenizer.js', ), 'javelin-behavior-differential-feedback-preview' => array( 'uri' => '/res/8695d8b8/rsrc/js/application/differential/behavior-comment-preview.js', 'type' => 'js', 'requires' => array( 0 => 'javelin-lib-dev', ), 'disk' => '/rsrc/js/application/differential/behavior-comment-preview.js', ), 'javelin-behavior-differential-edit-inline-comments' => array( 'uri' => '/res/74747b2e/rsrc/js/application/differential/behavior-edit-inline-comments.js', 'type' => 'js', 'requires' => array( 0 => 'javelin-lib-dev', ), 'disk' => '/rsrc/js/application/differential/behavior-edit-inline-comments.js', ), 'javelin-behavior-differential-populate' => array( 'uri' => '/res/f7efbf62/rsrc/js/application/differential/behavior-populate.js', 'type' => 'js', 'requires' => array( 0 => 'javelin-lib-dev', ), 'disk' => '/rsrc/js/application/differential/behavior-populate.js', ), 'javelin-behavior-differential-show-more' => array( 'uri' => '/res/ea998002/rsrc/js/application/differential/behavior-show-more.js', 'type' => 'js', 'requires' => array( 0 => 'javelin-lib-dev', ), 'disk' => '/rsrc/js/application/differential/behavior-show-more.js', ), 'javelin-magical-init' => array( 'uri' => '/res/76614f84/rsrc/js/javelin/init.dev.js', 'type' => 'js', 'requires' => array( ), 'disk' => '/rsrc/js/javelin/init.dev.js', ), 'javelin-init-prod' => array( 'uri' => '/res/1267c868/rsrc/js/javelin/init.min.js', 'type' => 'js', 'requires' => array( ), 'disk' => '/rsrc/js/javelin/init.min.js', ), 'javelin-lib-dev' => array( 'uri' => '/res/a0e7a5e9/rsrc/js/javelin/javelin.dev.js', 'type' => 'js', 'requires' => array( ), 'disk' => '/rsrc/js/javelin/javelin.dev.js', ), 'javelin-lib-prod' => array( 'uri' => '/res/2f2b3b2e/rsrc/js/javelin/javelin.min.js', 'type' => 'js', 'requires' => array( ), 'disk' => '/rsrc/js/javelin/javelin.min.js', ), 'javelin-typeahead-dev' => array( 'uri' => '/res/6de6ae59/rsrc/js/javelin/typeahead.dev.js', 'type' => 'js', 'requires' => array( ), 'disk' => '/rsrc/js/javelin/typeahead.dev.js', ), 'javelin-typeahead-prod' => array( 'uri' => '/res/69d5fad1/rsrc/js/javelin/typeahead.min.js', 'type' => 'js', 'requires' => array( ), 'disk' => '/rsrc/js/javelin/typeahead.min.js', ), 'javelin-workflow-dev' => array( 'uri' => '/res/7e690e16/rsrc/js/javelin/workflow.dev.js', 'type' => 'js', 'requires' => array( ), 'disk' => '/rsrc/js/javelin/workflow.dev.js', ), 'javelin-workflow-prod' => array( 'uri' => '/res/b758e0a0/rsrc/js/javelin/workflow.min.js', 'type' => 'js', 'requires' => array( ), 'disk' => '/rsrc/js/javelin/workflow.min.js', ), ), array ( 'packages' => array ( 'c5efa388' => 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-tokenizer-control-css', 9 => 'aphront-typeahead-control-css', 10 => 'phabricator-directory-css', ), 'uri' => '/res/pkg/c5efa388/core.pkg.css', 'type' => 'css', ), '9d9c881c' => 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', ), 'uri' => '/res/pkg/9d9c881c/differential.pkg.css', 'type' => 'css', ), ), 'reverse' => array ( 'phabricator-core-css' => 'c5efa388', 'phabricator-core-buttons-css' => 'c5efa388', 'phabricator-standard-page-view' => 'c5efa388', 'aphront-dialog-view-css' => 'c5efa388', 'aphront-form-view-css' => 'c5efa388', 'aphront-panel-view-css' => 'c5efa388', 'aphront-side-nav-view-css' => 'c5efa388', 'aphront-table-view-css' => 'c5efa388', 'aphront-tokenizer-control-css' => 'c5efa388', 'aphront-typeahead-control-css' => 'c5efa388', 'phabricator-directory-css' => 'c5efa388', 'differential-core-view-css' => '9d9c881c', 'differential-changeset-view-css' => '9d9c881c', 'differential-revision-detail-css' => '9d9c881c', 'differential-revision-history-css' => '9d9c881c', 'differential-table-of-contents-css' => '9d9c881c', ), )); diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 4d7055953b..0287bcc21c 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1,374 +1,376 @@ array( 'Aphront400Response' => 'aphront/response/400', 'Aphront404Response' => 'aphront/response/404', 'AphrontAjaxResponse' => 'aphront/response/ajax', 'AphrontApplicationConfiguration' => 'aphront/applicationconfiguration', 'AphrontController' => 'aphront/controller', 'AphrontDatabaseConnection' => 'storage/connection/base', 'AphrontDefaultApplicationConfiguration' => 'aphront/default/configuration', 'AphrontDefaultApplicationController' => 'aphront/default/controller', 'AphrontDialogResponse' => 'aphront/response/dialog', 'AphrontDialogView' => 'view/dialog', 'AphrontErrorView' => 'view/form/error', 'AphrontException' => 'aphront/exception/base', 'AphrontFileResponse' => 'aphront/response/file', 'AphrontFormCheckboxControl' => 'view/form/control/checkbox', 'AphrontFormControl' => 'view/form/control/base', 'AphrontFormFileControl' => 'view/form/control/file', 'AphrontFormMarkupControl' => 'view/form/control/markup', 'AphrontFormPasswordControl' => 'view/form/control/password', 'AphrontFormRecaptchaControl' => 'view/form/control/recaptcha', 'AphrontFormSelectControl' => 'view/form/control/select', 'AphrontFormStaticControl' => 'view/form/control/static', 'AphrontFormSubmitControl' => 'view/form/control/submit', 'AphrontFormTextAreaControl' => 'view/form/control/textarea', 'AphrontFormTextControl' => 'view/form/control/text', 'AphrontFormTokenizerControl' => 'view/form/control/tokenizer', 'AphrontFormView' => 'view/form/base', 'AphrontMySQLDatabaseConnection' => 'storage/connection/mysql', 'AphrontNullView' => 'view/null', 'AphrontPageView' => 'view/page/base', 'AphrontPanelView' => 'view/layout/panel', 'AphrontQueryConnectionException' => 'storage/exception/connection', 'AphrontQueryConnectionLostException' => 'storage/exception/connectionlost', 'AphrontQueryCountException' => 'storage/exception/count', 'AphrontQueryDuplicateKeyException' => 'storage/exception/duplicatekey', 'AphrontQueryException' => 'storage/exception/base', 'AphrontQueryObjectMissingException' => 'storage/exception/objectmissing', 'AphrontQueryParameterException' => 'storage/exception/parameter', 'AphrontQueryRecoverableException' => 'storage/exception/recoverable', 'AphrontRedirectException' => 'aphront/exception/redirect', 'AphrontRedirectResponse' => 'aphront/response/redirect', 'AphrontRequest' => 'aphront/request', 'AphrontRequestFailureView' => 'view/page/failure', 'AphrontResponse' => 'aphront/response/base', 'AphrontSideNavView' => 'view/layout/sidenav', 'AphrontTableView' => 'view/control/table', 'AphrontURIMapper' => 'aphront/mapper', 'AphrontView' => 'view/base', 'AphrontWebpageResponse' => 'aphront/response/webpage', 'CelerityAPI' => 'infrastructure/celerity/api', 'CelerityResourceController' => 'infrastructure/celerity/controller', 'CelerityResourceMap' => 'infrastructure/celerity/map', 'CelerityStaticResourceResponse' => 'infrastructure/celerity/response', 'ConduitAPIMethod' => 'applications/conduit/method/base', 'ConduitAPIRequest' => 'applications/conduit/protocol/request', 'ConduitAPI_conduit_connect_Method' => 'applications/conduit/method/conduit/connect', 'ConduitAPI_differential_creatediff_Method' => 'applications/conduit/method/differential/creatediff', 'ConduitAPI_differential_setdiffproperty_Method' => 'applications/conduit/method/differential/setdiffproperty', 'ConduitAPI_file_upload_Method' => 'applications/conduit/method/file/upload', 'ConduitAPI_user_find_Method' => 'applications/conduit/method/user/find', 'ConduitException' => 'applications/conduit/protocol/exception', 'DarkConsole' => 'aphront/console/api', 'DarkConsoleController' => 'aphront/console/controller', 'DarkConsoleCore' => 'aphront/console/core', 'DarkConsoleErrorLogPlugin' => 'aphront/console/plugin/errorlog', + 'DarkConsoleErrorLogPluginAPI' => 'aphront/console/plugin/errorlog/api', 'DarkConsolePlugin' => 'aphront/console/plugin/base', 'DarkConsoleRequestPlugin' => 'aphront/console/plugin/request', 'DarkConsoleServicesPlugin' => 'aphront/console/plugin/services', + 'DarkConsoleServicesPluginAPI' => 'aphront/console/plugin/services/api', 'DarkConsoleXHProfPlugin' => 'aphront/console/plugin/xhprof', 'DarkConsoleXHProfPluginAPI' => 'aphront/console/plugin/xhprof/api', 'DifferentialAction' => 'applications/differential/constants/action', 'DifferentialAddCommentView' => 'applications/differential/view/addcomment', 'DifferentialCCWelcomeMail' => 'applications/differential/mail/ccwelcome', 'DifferentialChangeType' => 'applications/differential/constants/changetype', 'DifferentialChangeset' => 'applications/differential/storage/changeset', 'DifferentialChangesetDetailView' => 'applications/differential/view/changesetdetailview', 'DifferentialChangesetListView' => 'applications/differential/view/changesetlistview', 'DifferentialChangesetParser' => 'applications/differential/parser/changeset', 'DifferentialChangesetViewController' => 'applications/differential/controller/changesetview', 'DifferentialComment' => 'applications/differential/storage/comment', 'DifferentialCommentEditor' => 'applications/differential/editor/comment', 'DifferentialCommentMail' => 'applications/differential/mail/comment', 'DifferentialCommentPreviewController' => 'applications/differential/controller/commentpreview', 'DifferentialCommentSaveController' => 'applications/differential/controller/commentsave', 'DifferentialController' => 'applications/differential/controller/base', 'DifferentialDAO' => 'applications/differential/storage/base', 'DifferentialDiff' => 'applications/differential/storage/diff', 'DifferentialDiffContentMail' => 'applications/differential/mail/diffcontent', 'DifferentialDiffProperty' => 'applications/differential/storage/diffproperty', 'DifferentialDiffTableOfContentsView' => 'applications/differential/view/difftableofcontents', 'DifferentialDiffViewController' => 'applications/differential/controller/diffview', 'DifferentialHunk' => 'applications/differential/storage/hunk', 'DifferentialInlineComment' => 'applications/differential/storage/inlinecomment', 'DifferentialInlineCommentEditController' => 'applications/differential/controller/inlinecommentedit', 'DifferentialInlineCommentPreviewController' => 'applications/differential/controller/inlinecommentpreview', 'DifferentialInlineCommentView' => 'applications/differential/view/inlinecomment', 'DifferentialLintStatus' => 'applications/differential/constants/lintstatus', 'DifferentialMail' => 'applications/differential/mail/base', 'DifferentialMarkupEngineFactory' => 'applications/differential/parser/markup', 'DifferentialNewDiffMail' => 'applications/differential/mail/newdiff', 'DifferentialReviewRequestMail' => 'applications/differential/mail/reviewrequest', 'DifferentialRevision' => 'applications/differential/storage/revision', 'DifferentialRevisionCommentListView' => 'applications/differential/view/revisioncommentlist', 'DifferentialRevisionCommentView' => 'applications/differential/view/revisioncomment', 'DifferentialRevisionControlSystem' => 'applications/differential/constants/revisioncontrolsystem', 'DifferentialRevisionDetailView' => 'applications/differential/view/revisiondetail', 'DifferentialRevisionEditController' => 'applications/differential/controller/revisionedit', 'DifferentialRevisionEditor' => 'applications/differential/editor/revision', 'DifferentialRevisionListController' => 'applications/differential/controller/revisionlist', 'DifferentialRevisionListData' => 'applications/differential/data/revisionlist', 'DifferentialRevisionStatus' => 'applications/differential/constants/revisionstatus', 'DifferentialRevisionUpdateHistoryView' => 'applications/differential/view/revisionupdatehistory', 'DifferentialRevisionViewController' => 'applications/differential/controller/revisionview', 'DifferentialUnitStatus' => 'applications/differential/constants/unitstatus', 'Javelin' => 'infrastructure/javelin/api', 'LiskDAO' => 'storage/lisk/dao', 'Phabricator404Controller' => 'applications/base/controller/404', 'PhabricatorAuthController' => 'applications/auth/controller/base', 'PhabricatorConduitAPIController' => 'applications/conduit/controller/api', 'PhabricatorConduitConnectionLog' => 'applications/conduit/storage/connectionlog', 'PhabricatorConduitConsoleController' => 'applications/conduit/controller/console', 'PhabricatorConduitController' => 'applications/conduit/controller/base', 'PhabricatorConduitDAO' => 'applications/conduit/storage/base', 'PhabricatorConduitLogController' => 'applications/conduit/controller/log', 'PhabricatorConduitMethodCallLog' => 'applications/conduit/storage/methodcalllog', 'PhabricatorController' => 'applications/base/controller/base', 'PhabricatorDirectoryCategory' => 'applications/directory/storage/category', 'PhabricatorDirectoryCategoryDeleteController' => 'applications/directory/controller/categorydelete', 'PhabricatorDirectoryCategoryEditController' => 'applications/directory/controller/categoryedit', 'PhabricatorDirectoryCategoryListController' => 'applications/directory/controller/categorylist', 'PhabricatorDirectoryController' => 'applications/directory/controller/base', 'PhabricatorDirectoryDAO' => 'applications/directory/storage/base', 'PhabricatorDirectoryItem' => 'applications/directory/storage/item', 'PhabricatorDirectoryItemDeleteController' => 'applications/directory/controller/itemdelete', 'PhabricatorDirectoryItemEditController' => 'applications/directory/controller/itemedit', 'PhabricatorDirectoryItemListController' => 'applications/directory/controller/itemlist', 'PhabricatorDirectoryMainController' => 'applications/directory/controller/main', 'PhabricatorEmailLoginController' => 'applications/auth/controller/email', 'PhabricatorEmailTokenController' => 'applications/auth/controller/emailtoken', 'PhabricatorEnv' => 'infrastructure/env', 'PhabricatorFacebookAuthController' => 'applications/auth/controller/facebookauth', 'PhabricatorFacebookAuthDiagnosticsController' => 'applications/auth/controller/facebookauth/diagnostics', 'PhabricatorFile' => 'applications/files/storage/file', 'PhabricatorFileController' => 'applications/files/controller/base', 'PhabricatorFileDAO' => 'applications/files/storage/base', 'PhabricatorFileListController' => 'applications/files/controller/list', 'PhabricatorFileStorageBlob' => 'applications/files/storage/storageblob', 'PhabricatorFileURI' => 'applications/files/uri', 'PhabricatorFileUploadController' => 'applications/files/controller/upload', 'PhabricatorFileViewController' => 'applications/files/controller/view', 'PhabricatorLiskDAO' => 'applications/base/storage/lisk', 'PhabricatorLoginController' => 'applications/auth/controller/login', 'PhabricatorLogoutController' => 'applications/auth/controller/logout', 'PhabricatorMailImplementationAdapter' => 'applications/metamta/adapter/base', 'PhabricatorMailImplementationPHPMailerLiteAdapter' => 'applications/metamta/adapter/phpmailerlite', 'PhabricatorMetaMTAController' => 'applications/metamta/controller/base', 'PhabricatorMetaMTADAO' => 'applications/metamta/storage/base', 'PhabricatorMetaMTAListController' => 'applications/metamta/controller/list', 'PhabricatorMetaMTAMail' => 'applications/metamta/storage/mail', 'PhabricatorMetaMTAMailingList' => 'applications/metamta/storage/mailinglist', 'PhabricatorMetaMTAMailingListEditController' => 'applications/metamta/controller/mailinglistedit', 'PhabricatorMetaMTAMailingListsController' => 'applications/metamta/controller/mailinglists', 'PhabricatorMetaMTASendController' => 'applications/metamta/controller/send', 'PhabricatorMetaMTAViewController' => 'applications/metamta/controller/view', 'PhabricatorObjectHandle' => 'applications/phid/handle', 'PhabricatorObjectHandleData' => 'applications/phid/handle/data', 'PhabricatorPHID' => 'applications/phid/storage/phid', 'PhabricatorPHIDAllocateController' => 'applications/phid/controller/allocate', 'PhabricatorPHIDController' => 'applications/phid/controller/base', 'PhabricatorPHIDDAO' => 'applications/phid/storage/base', 'PhabricatorPHIDListController' => 'applications/phid/controller/list', 'PhabricatorPHIDLookupController' => 'applications/phid/controller/lookup', 'PhabricatorPHIDType' => 'applications/phid/storage/type', 'PhabricatorPHIDTypeEditController' => 'applications/phid/controller/typeedit', 'PhabricatorPHIDTypeListController' => 'applications/phid/controller/typelist', 'PhabricatorPeopleController' => 'applications/people/controller/base', 'PhabricatorPeopleEditController' => 'applications/people/controller/edit', 'PhabricatorPeopleListController' => 'applications/people/controller/list', 'PhabricatorPeopleProfileController' => 'applications/people/controller/profile', 'PhabricatorStandardPageView' => 'view/page/standard', 'PhabricatorTypeaheadCommonDatasourceController' => 'applications/typeahead/controller/common', 'PhabricatorTypeaheadDatasourceController' => 'applications/typeahead/controller/base', 'PhabricatorUser' => 'applications/people/storage/user', 'PhabricatorUserDAO' => 'applications/people/storage/base', 'PhabricatorXHProfController' => 'applications/xhprof/controller/base', 'PhabricatorXHProfProfileController' => 'applications/xhprof/controller/profile', 'PhabricatorXHProfProfileSymbolView' => 'applications/xhprof/view/symbol', 'PhabricatorXHProfProfileTopLevelView' => 'applications/xhprof/view/toplevel', ), 'function' => array( '_qsprintf_check_scalar_type' => 'storage/qsprintf', '_qsprintf_check_type' => 'storage/qsprintf', 'celerity_generate_unique_node_id' => 'infrastructure/celerity/api', 'celerity_register_resource_map' => 'infrastructure/celerity/map', 'javelin_render_tag' => 'infrastructure/javelin/markup', 'phabricator_format_relative_time' => 'view/utils', 'phabricator_format_timestamp' => 'view/utils', 'phabricator_format_units_generic' => 'view/utils', 'qsprintf' => 'storage/qsprintf', 'queryfx' => 'storage/queryfx', 'queryfx_all' => 'storage/queryfx', 'queryfx_one' => 'storage/queryfx', 'require_celerity_resource' => 'infrastructure/celerity/api', 'vqsprintf' => 'storage/qsprintf', 'vqueryfx' => 'storage/queryfx', 'vqueryfx_all' => 'storage/queryfx', 'xsprintf_query' => 'storage/qsprintf', ), 'requires_class' => array( 'Aphront400Response' => 'AphrontResponse', 'Aphront404Response' => 'AphrontResponse', 'AphrontAjaxResponse' => 'AphrontResponse', 'AphrontDefaultApplicationConfiguration' => 'AphrontApplicationConfiguration', 'AphrontDefaultApplicationController' => 'AphrontController', 'AphrontDialogResponse' => 'AphrontResponse', 'AphrontDialogView' => 'AphrontView', 'AphrontErrorView' => 'AphrontView', 'AphrontFileResponse' => 'AphrontResponse', 'AphrontFormCheckboxControl' => 'AphrontFormControl', 'AphrontFormControl' => 'AphrontView', 'AphrontFormFileControl' => 'AphrontFormControl', 'AphrontFormMarkupControl' => 'AphrontFormControl', 'AphrontFormPasswordControl' => 'AphrontFormControl', 'AphrontFormRecaptchaControl' => 'AphrontFormControl', 'AphrontFormSelectControl' => 'AphrontFormControl', 'AphrontFormStaticControl' => 'AphrontFormControl', 'AphrontFormSubmitControl' => 'AphrontFormControl', 'AphrontFormTextAreaControl' => 'AphrontFormControl', 'AphrontFormTextControl' => 'AphrontFormControl', 'AphrontFormTokenizerControl' => 'AphrontFormControl', 'AphrontFormView' => 'AphrontView', 'AphrontMySQLDatabaseConnection' => 'AphrontDatabaseConnection', 'AphrontNullView' => 'AphrontView', 'AphrontPageView' => 'AphrontView', 'AphrontPanelView' => 'AphrontView', 'AphrontQueryConnectionException' => 'AphrontQueryException', 'AphrontQueryConnectionLostException' => 'AphrontQueryRecoverableException', 'AphrontQueryCountException' => 'AphrontQueryException', 'AphrontQueryDuplicateKeyException' => 'AphrontQueryException', 'AphrontQueryObjectMissingException' => 'AphrontQueryException', 'AphrontQueryParameterException' => 'AphrontQueryException', 'AphrontQueryRecoverableException' => 'AphrontQueryException', 'AphrontRedirectException' => 'AphrontException', 'AphrontRedirectResponse' => 'AphrontResponse', 'AphrontRequestFailureView' => 'AphrontView', 'AphrontSideNavView' => 'AphrontView', 'AphrontTableView' => 'AphrontView', 'AphrontWebpageResponse' => 'AphrontResponse', 'CelerityResourceController' => 'AphrontController', 'ConduitAPI_conduit_connect_Method' => 'ConduitAPIMethod', 'ConduitAPI_differential_creatediff_Method' => 'ConduitAPIMethod', 'ConduitAPI_differential_setdiffproperty_Method' => 'ConduitAPIMethod', 'ConduitAPI_file_upload_Method' => 'ConduitAPIMethod', 'ConduitAPI_user_find_Method' => 'ConduitAPIMethod', - 'DarkConsoleController' => 'AliteController', + 'DarkConsoleController' => 'PhabricatorController', 'DarkConsoleErrorLogPlugin' => 'DarkConsolePlugin', 'DarkConsoleRequestPlugin' => 'DarkConsolePlugin', 'DarkConsoleServicesPlugin' => 'DarkConsolePlugin', 'DarkConsoleXHProfPlugin' => 'DarkConsolePlugin', 'DifferentialAddCommentView' => 'AphrontView', 'DifferentialCCWelcomeMail' => 'DifferentialReviewRequestMail', 'DifferentialChangeset' => 'DifferentialDAO', 'DifferentialChangesetDetailView' => 'AphrontView', 'DifferentialChangesetListView' => 'AphrontView', 'DifferentialChangesetViewController' => 'DifferentialController', 'DifferentialComment' => 'DifferentialDAO', 'DifferentialCommentMail' => 'DifferentialMail', 'DifferentialCommentPreviewController' => 'DifferentialController', 'DifferentialCommentSaveController' => 'DifferentialController', 'DifferentialController' => 'PhabricatorController', 'DifferentialDAO' => 'PhabricatorLiskDAO', 'DifferentialDiff' => 'DifferentialDAO', 'DifferentialDiffContentMail' => 'DifferentialMail', 'DifferentialDiffProperty' => 'DifferentialDAO', 'DifferentialDiffTableOfContentsView' => 'AphrontView', 'DifferentialDiffViewController' => 'DifferentialController', 'DifferentialHunk' => 'DifferentialDAO', 'DifferentialInlineComment' => 'DifferentialDAO', 'DifferentialInlineCommentEditController' => 'DifferentialController', 'DifferentialInlineCommentPreviewController' => 'DifferentialController', 'DifferentialInlineCommentView' => 'AphrontView', 'DifferentialNewDiffMail' => 'DifferentialReviewRequestMail', 'DifferentialReviewRequestMail' => 'DifferentialMail', 'DifferentialRevision' => 'DifferentialDAO', 'DifferentialRevisionCommentListView' => 'AphrontView', 'DifferentialRevisionCommentView' => 'AphrontView', 'DifferentialRevisionDetailView' => 'AphrontView', 'DifferentialRevisionEditController' => 'DifferentialController', 'DifferentialRevisionListController' => 'DifferentialController', 'DifferentialRevisionUpdateHistoryView' => 'AphrontView', 'DifferentialRevisionViewController' => 'DifferentialController', 'Phabricator404Controller' => 'PhabricatorController', 'PhabricatorAuthController' => 'PhabricatorController', 'PhabricatorConduitAPIController' => 'PhabricatorConduitController', 'PhabricatorConduitConnectionLog' => 'PhabricatorConduitDAO', 'PhabricatorConduitConsoleController' => 'PhabricatorConduitController', 'PhabricatorConduitController' => 'PhabricatorController', 'PhabricatorConduitDAO' => 'PhabricatorLiskDAO', 'PhabricatorConduitLogController' => 'PhabricatorConduitController', 'PhabricatorConduitMethodCallLog' => 'PhabricatorConduitDAO', 'PhabricatorController' => 'AphrontController', 'PhabricatorDirectoryCategory' => 'PhabricatorDirectoryDAO', 'PhabricatorDirectoryCategoryDeleteController' => 'PhabricatorDirectoryController', 'PhabricatorDirectoryCategoryEditController' => 'PhabricatorDirectoryController', 'PhabricatorDirectoryCategoryListController' => 'PhabricatorDirectoryController', 'PhabricatorDirectoryController' => 'PhabricatorController', 'PhabricatorDirectoryDAO' => 'PhabricatorLiskDAO', 'PhabricatorDirectoryItem' => 'PhabricatorDirectoryDAO', 'PhabricatorDirectoryItemDeleteController' => 'PhabricatorDirectoryController', 'PhabricatorDirectoryItemEditController' => 'PhabricatorDirectoryController', 'PhabricatorDirectoryItemListController' => 'PhabricatorDirectoryController', 'PhabricatorDirectoryMainController' => 'PhabricatorDirectoryController', 'PhabricatorEmailLoginController' => 'PhabricatorAuthController', 'PhabricatorEmailTokenController' => 'PhabricatorAuthController', 'PhabricatorFacebookAuthController' => 'PhabricatorAuthController', 'PhabricatorFacebookAuthDiagnosticsController' => 'PhabricatorAuthController', 'PhabricatorFile' => 'PhabricatorFileDAO', 'PhabricatorFileController' => 'PhabricatorController', 'PhabricatorFileDAO' => 'PhabricatorLiskDAO', 'PhabricatorFileListController' => 'PhabricatorFileController', 'PhabricatorFileStorageBlob' => 'PhabricatorFileDAO', 'PhabricatorFileUploadController' => 'PhabricatorFileController', 'PhabricatorFileViewController' => 'PhabricatorFileController', 'PhabricatorLiskDAO' => 'LiskDAO', 'PhabricatorLoginController' => 'PhabricatorAuthController', 'PhabricatorLogoutController' => 'PhabricatorAuthController', 'PhabricatorMailImplementationPHPMailerLiteAdapter' => 'PhabricatorMailImplementationAdapter', 'PhabricatorMetaMTAController' => 'PhabricatorController', 'PhabricatorMetaMTADAO' => 'PhabricatorLiskDAO', 'PhabricatorMetaMTAListController' => 'PhabricatorMetaMTAController', 'PhabricatorMetaMTAMail' => 'PhabricatorMetaMTADAO', 'PhabricatorMetaMTAMailingList' => 'PhabricatorMetaMTADAO', 'PhabricatorMetaMTAMailingListEditController' => 'PhabricatorMetaMTAController', 'PhabricatorMetaMTAMailingListsController' => 'PhabricatorMetaMTAController', 'PhabricatorMetaMTASendController' => 'PhabricatorMetaMTAController', 'PhabricatorMetaMTAViewController' => 'PhabricatorMetaMTAController', 'PhabricatorPHID' => 'PhabricatorPHIDDAO', 'PhabricatorPHIDAllocateController' => 'PhabricatorPHIDController', 'PhabricatorPHIDController' => 'PhabricatorController', 'PhabricatorPHIDDAO' => 'PhabricatorLiskDAO', 'PhabricatorPHIDListController' => 'PhabricatorPHIDController', 'PhabricatorPHIDLookupController' => 'PhabricatorPHIDController', 'PhabricatorPHIDType' => 'PhabricatorPHIDDAO', 'PhabricatorPHIDTypeEditController' => 'PhabricatorPHIDController', 'PhabricatorPHIDTypeListController' => 'PhabricatorPHIDController', 'PhabricatorPeopleController' => 'PhabricatorController', 'PhabricatorPeopleEditController' => 'PhabricatorPeopleController', 'PhabricatorPeopleListController' => 'PhabricatorPeopleController', 'PhabricatorPeopleProfileController' => 'PhabricatorPeopleController', 'PhabricatorStandardPageView' => 'AphrontPageView', 'PhabricatorTypeaheadCommonDatasourceController' => 'PhabricatorTypeaheadDatasourceController', 'PhabricatorTypeaheadDatasourceController' => 'PhabricatorController', 'PhabricatorUser' => 'PhabricatorUserDAO', 'PhabricatorUserDAO' => 'PhabricatorLiskDAO', 'PhabricatorXHProfController' => 'PhabricatorController', 'PhabricatorXHProfProfileController' => 'PhabricatorXHProfController', 'PhabricatorXHProfProfileSymbolView' => 'AphrontView', 'PhabricatorXHProfProfileTopLevelView' => 'AphrontView', ), 'requires_interface' => array( ), )); diff --git a/src/aphront/console/controller/DarkConsoleController.php b/src/aphront/console/controller/DarkConsoleController.php index 32939daf61..8ca4128c7e 100755 --- a/src/aphront/console/controller/DarkConsoleController.php +++ b/src/aphront/console/controller/DarkConsoleController.php @@ -1,100 +1,44 @@ getRequest(); + $user = $request->getUser(); - validate_parameter_list( - $params, - array( - ), - $ops = array( - 'tab' => true, - 'toggle' => true, - 'visible' => true, - 'plugin' => true, - - 'etc' => true, - )); - - foreach (array_keys($ops) as $op) { - if (isset($params[$op])) { - $this->op = $op; - break; - } + $visible = $request->getStr('visible'); + if (strlen($visible)) { + $user->setConsoleVisible((int)$visible); + $user->save(); + return new AphrontAjaxResponse(); } - $this->data = $params; - } - - public function getShortControllerName() { - return 'DarkConsole'; - } - - public function shouldPreflush() { - return false; - } - public function process() { - $request = $this->getRequest(); - - if (!$this->op) { - $this->op = 'toggle'; - } - - $coredata = $request->getCoreData(); - $console = $coredata->getConsole(); - - if ($request->isAsync()) { - $return = null; - } else { - $return = '/'; + $tab = $request->getStr('tab'); + if (strlen($tab)) { + $user->setConsoleTab($tab); + $user->save(); + return new AphrontAjaxResponse(); } - - switch ($this->op) { - case 'toggle': - $enabled = $coredata->didToggleDarkConsole(); - if ($enabled) { - if (!$console) { - $console = new DarkConsoleCore($coredata); - } - $console->setConsoleSetting( - DarkConsoleCore::SETTING_VISIBLE, - true); - } - break; - case 'tab': - $console->setConsoleSetting( - DarkConsoleCore::SETTING_TAB, - $request->getStr('tab')); - break; - case 'visible': - $console->setConsoleSetting( - DarkConsoleCore::SETTING_VISIBLE, - !$console->getConsoleSetting(DarkConsoleCore::SETTING_VISIBLE)); - break; - } -// return ; } } diff --git a/src/aphront/console/controller/__init__.php b/src/aphront/console/controller/__init__.php index bc1a89277c..e4df3df00d 100644 --- a/src/aphront/console/controller/__init__.php +++ b/src/aphront/console/controller/__init__.php @@ -1,12 +1,13 @@ getCoreData()->getViewerContext()->getUserID(), - self::APPLICATION_ID, - $key, - $value); - $guard->release(); - if (!$okay) { - throw new Exception('Failed to set preference setting.'); - } -*/ - } - - public function getConsoleSetting($key) { -// $viewer_id = $this->getCoreData()->getViewerContext()->getUserID(); -// return idx(idx($this->settings[$viewer_id], $key), 'value'); - return true; - } - public function getPlugin($plugin_name) { return idx($this->plugins, $plugin_name); } public function __construct() { - -/* - $this->settings = users_multiget_prefs_info( - array($coredata->getViewerContext()->getUserID()), - self::APPLICATION_ID); - - $disabled = $this->getConsoleSetting(self::SETTING_PLUGINS); - $disabled = array_flip(explode(',', $disabled)); -*/ 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; } } } } public static function newPlugin($plugin) { $class = 'DarkConsole'.$plugin.'Plugin'; PhutilSymbolLoader::loadClass($class); return newv($class, array()); } public function getEnabledPlugins() { return $this->plugins; } public function render(AphrontRequest $request) { + $user = $request->getUser(); + $plugins = $this->getEnabledPlugins(); foreach ($plugins as $plugin) { $plugin->setRequest($request); $plugin->willShutdown(); } foreach ($plugins as $plugin) { $plugin->didShutdown(); } foreach ($plugins as $plugin) { $plugin->setData($plugin->generateData()); } - $selected = 'XHProf';//true;//$this->getConsoleSetting(DarkConsoleCore::SETTING_TAB); - $visible = true;//$this->getConsoleSetting(DarkConsoleCore::SETTING_VISIBLE); + $selected = $user->getConsoleTab(); + $visible = $user->getConsoleVisible(); if (!isset($plugins[$selected])) { $selected = key($plugins); } $tabs = array(); foreach ($plugins as $key => $plugin) { $tabs[$key] = array( 'name' => $plugin->getName(), 'panel' => $plugin->render(), ); } $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; } $tabs_markup[] = javelin_render_tag( 'a', array( 'class' => "dark-console-tab {$tabclass}", 'sigil' => 'dark-console-tab', - 'meta' => array( - 'key' => $key, - ), + 'id' => 'dark-console-tab-'.$key, ), (string)$data['name']); $panel_markup[] = javelin_render_tag( 'div', array( 'class' => 'dark-console-panel', 'style' => $style, 'sigil' => 'dark-console-panel', - 'meta' => array( - 'key' => $key, - ), ), (string)$data['panel']); } $console = javelin_render_tag( 'table', array( 'class' => 'dark-console', 'sigil' => 'dark-console', - 'meta' => array( - 'visible' => true, - ), - 'style' => '', + 'style' => $visible ? '' : 'display: none;', ), ''. ''. implode("\n", $tabs_markup). ''. ''.implode("\n", $panel_markup).''. ''); - Javelin::initBehavior('dark-console'); + if (!empty($_COOKIE['phsid'])) { + $console = str_replace( + $_COOKIE['phsid'], + phutil_escape_html(''), + $console); + } return "\n\n\n\n".$console."\n\n\n\n"; } } diff --git a/src/aphront/console/core/__init__.php b/src/aphront/console/core/__init__.php index ccdf4c2167..8509770916 100644 --- a/src/aphront/console/core/__init__.php +++ b/src/aphront/console/core/__init__.php @@ -1,16 +1,16 @@ getData()); -/* if ($count) { return - - Error Log ({$count}) - ; + ' '. + "Error Log ({$count})"; } -*/ return 'Error Log'; } public function getDescription() { return 'Shows errors and warnings.'; } public function generateData() { -/* - $stub = tabconsole(); - if (!$stub) { - return array(); - } + return DarkConsoleErrorLogPluginAPI::getErrors(); + } + - $errors = $stub->getErrors(); + public function render() { + + $data = $this->getData(); - $data = array(); - foreach ($errors as $error) { - if (is_array($error)) { - list($err, $trace) = $error; - $trace = implode("\n", $trace); - } else { - $err = $error->getMessage(); - $trace = $error->getTraceAsString(); + $rows = array(); + foreach ($data as $row) { + switch ($row['event']) { + case 'error': + $file = $row['file']; + $line = $row['line']; + break; + case 'exception': + $file = $row['exception']->getFile(); + $line = $row['exception']->getLine(); + break; } - $data[] = array( - 'error' => $err, - 'trace' => $trace, + + + $rows[] = array( + basename($file).':'.$line, + $row['str'], ); } - return $data; -*/ - } - - public function render() { + $table = new AphrontTableView($rows); + $table->setColumnClasses( + array( + null, + 'wide wrap', + )); + $table->setHeaders( + array( + 'File', + 'Error', + )); + $table->setNoDataString('No errors.'); + + return $table->render(); + } +} - return '!!'; /* $data = $this->getData(); if (!$data) { return
No errors.
; } $markup = ; $alt = false; foreach ($data as $error) { $row = ; $text = $error['error']; $text = preg_replace('/\(in .* on line \d+\)$/', '', trim($text)); $trace = $error['trace']; $trace = explode("\n", $trace); if (!$trace) { $trace = array('unknown@0@unknown'); } foreach ($trace as $idx => $traceline) { list($file, $line, $where) = array_merge( explode('@', $traceline), array('?', '?', '?')); if ($where == 'DarkConsole->addError' || $where == 'debug_rlog') { unset($trace[$idx]); } } $row->appendChild(); foreach ($trace as $traceline) { list($file, $line, $where) = array_merge( explode('@', $traceline), array('?', '?', '?')); $row->appendChild(); $row->appendChild(); $markup->appendChild($row); $row = ; } $alt = !$alt; } return

Errors

{$markup}
; */ - } - -} diff --git a/src/aphront/console/plugin/errorlog/__init__.php b/src/aphront/console/plugin/errorlog/__init__.php index 0137999ed9..6b52c92dea 100644 --- a/src/aphront/console/plugin/errorlog/__init__.php +++ b/src/aphront/console/plugin/errorlog/__init__.php @@ -1,12 +1,14 @@ 'error', + 'num' => $num, + 'str' => $str, + 'file' => $file, + 'line' => $line, + 'cxt' => $cxt, + 'trace' => debug_backtrace(), + ); + error_log("{$file}:{$line} {$str}"); + } + + public static function handleException($ex) { + self::$errors[] = array( + 'event' => 'exception', + 'exception' => $ex, + ); + error_log($ex); + } + +} diff --git a/src/aphront/console/plugin/errorlog/api/__init__.php b/src/aphront/console/plugin/errorlog/api/__init__.php new file mode 100644 index 0000000000..f4f7b7ffb9 --- /dev/null +++ b/src/aphront/console/plugin/errorlog/api/__init__.php @@ -0,0 +1,10 @@ + $_REQUEST, 'Server' => $_SERVER, ); } public function render() { $data = $this->getData(); $sections = array( 'Basics' => array( 'Host' => $data['Server']['SERVER_ADDR'], 'Hostname' => gethostbyaddr($data['Server']['SERVER_ADDR']), + 'Machine' => php_uname('n'), ), ); $sections = array_merge($sections, $data); -/* - $out = ; + $out = array(); foreach ($sections as $header => $map) { - $list =
{$text}{$file}:{$line}{$where}()
; + $rows = array(); foreach ($map as $key => $value) { - if (!is_scalar($value)) { - $value = fb_json_encode($value); - } - $value = {$value}; - $list->appendChild( - ); + $rows[] = array( + phutil_escape_html($key), + phutil_escape_html($value), + ); } - $out->appendChild( - -

{$header}

- {$list} -
); + + $table = new AphrontTableView($rows); + $table->setHeaders( + array( + $header, + null, + )); + $table->setColumnClasses( + array( + 'header', + 'wide wrap', + )); + $out[] = $table->render(); } - return $out; -*/ - return "REQUEST"; + return implode("\n", $out); } } diff --git a/src/aphront/console/plugin/request/__init__.php b/src/aphront/console/plugin/request/__init__.php index 191e9cd6e0..93fa0510f0 100644 --- a/src/aphront/console/plugin/request/__init__.php +++ b/src/aphront/console/plugin/request/__init__.php @@ -1,12 +1,15 @@ observations = cacheobserver(); + public function generateData() { + return DarkConsoleServicesPluginAPI::getEvents(); } public function render() { - return '!'; + $data = $this->getData(); + + $rows = array(); + foreach ($data as $row) { + + switch ($row['event']) { + case DarkConsoleServicesPluginAPI::EVENT_QUERY: + $info = $row['query']; + $info = phutil_escape_html($info); + break; + case DarkConsoleServicesPluginAPI::EVENT_CONNECT: + $info = $row['host'].':'.$row['database']; + $info = phutil_escape_html($info); + + break; + default: + $info = '-'; + break; + } + + $rows[] = array( + $row['event'], + number_format(1000000 * ($row['end'] - $row['start'])).' us', + $info, + ); + } + + $table = new AphrontTableView($rows); + $table->setColumnClasses( + array( + null, + 'n', + 'wide wrap', + )); + $table->setHeaders( + array( + 'Event', + 'Duration', + 'Details', + )); + + return $table->render(); } } diff --git a/src/aphront/console/plugin/services/__init__.php b/src/aphront/console/plugin/services/__init__.php index aeab5c172b..3952c2ebd4 100644 --- a/src/aphront/console/plugin/services/__init__.php +++ b/src/aphront/console/plugin/services/__init__.php @@ -1,12 +1,16 @@ observations = cacheobserver(); + public static function getEvents() { + return self::$events; } - public function render() { - return '!'; - } } diff --git a/src/aphront/console/plugin/services/api/__init__.php b/src/aphront/console/plugin/services/api/__init__.php new file mode 100644 index 0000000000..f7324ef1ef --- /dev/null +++ b/src/aphront/console/plugin/services/api/__init__.php @@ -0,0 +1,10 @@ + array( '$' => 'RepositoryListController', 'new/$' => 'RepositoryEditController', 'edit/(?\d+)/$' => 'RepositoryEditController', 'delete/(?\d+)/$' => 'RepositoryDeleteController', ), '/' => array( '$' => 'PhabricatorDirectoryMainController', ), '/directory/' => array( 'item/$' => 'PhabricatorDirectoryItemListController', 'item/edit/(?:(?\d+)/)?$' => 'PhabricatorDirectoryItemEditController', 'item/delete/(?\d+)/' => 'PhabricatorDirectoryItemDeleteController', 'category/$' => 'PhabricatorDirectoryCategoryListController', 'category/edit/(?:(?\d+)/)?$' => 'PhabricatorDirectoryCategoryEditController', 'category/delete/(?\d+)/' => 'PhabricatorDirectoryCategoryDeleteController', ), '/file/' => array( '$' => 'PhabricatorFileListController', 'upload/$' => 'PhabricatorFileUploadController', '(?info)/(?[^/]+)/' => 'PhabricatorFileViewController', '(?view)/(?[^/]+)/' => 'PhabricatorFileViewController', '(?download)/(?[^/]+)/' => 'PhabricatorFileViewController', ), '/phid/' => array( '$' => 'PhabricatorPHIDLookupController', 'list/$' => 'PhabricatorPHIDListController', 'type/$' => 'PhabricatorPHIDTypeListController', 'type/edit/(?:(?\d+)/)?$' => 'PhabricatorPHIDTypeEditController', 'new/$' => 'PhabricatorPHIDAllocateController', ), '/people/' => array( '$' => 'PhabricatorPeopleListController', 'edit/(?:(?\w+)/)?$' => 'PhabricatorPeopleEditController', ), '/p/(?\w+)/$' => 'PhabricatorPeopleProfileController', '/conduit/' => array( '$' => 'PhabricatorConduitConsoleController', 'method/(?[^/]+)$' => 'PhabricatorConduitConsoleController', 'log/$' => 'PhabricatorConduitLogController', ), '/api/(?[^/]+)$' => 'PhabricatorConduitAPIController', '/D(?\d+)' => 'DifferentialRevisionViewController', '/differential/' => array( '$' => 'DifferentialRevisionListController', 'filter/(?\w+)/$' => 'DifferentialRevisionListController', 'diff/(?\d+)/$' => 'DifferentialDiffViewController', 'changeset/$' => 'DifferentialChangesetViewController', 'revision/edit/(?:(?\d+)/)?$' => 'DifferentialRevisionEditController', 'comment/' => array( 'preview/(?\d+)/$' => 'DifferentialCommentPreviewController', 'save/$' => 'DifferentialCommentSaveController', 'inline/' => array( 'preview/(?\d+)/$' => 'DifferentialInlineCommentPreviewController', 'edit/(?\d+)/$' => 'DifferentialInlineCommentEditController', ), ), ), '/res/' => array( '(?pkg/)?(?[a-f0-9]{8})/(?.+\.(?:css|js))$' => 'CelerityResourceController', ), '/typeahead/' => array( 'common/(?\w+)/$' => 'PhabricatorTypeaheadCommonDatasourceController', ), '/mail/' => array( '$' => 'PhabricatorMetaMTAListController', 'send/$' => 'PhabricatorMetaMTASendController', 'view/(?\d+)/$' => 'PhabricatorMetaMTAViewController', 'lists/$' => 'PhabricatorMetaMTAMailingListsController', 'lists/edit/(?:(?\d+)/)?$' => 'PhabricatorMetaMTAMailingListEditController', ), '/login/' => array( '$' => 'PhabricatorLoginController', 'email/$' => 'PhabricatorEmailLoginController', 'etoken/(?\w+)/$' => 'PhabricatorEmailTokenController', ), '/logout/$' => 'PhabricatorLogoutController', '/facebook-auth/' => array( '$' => 'PhabricatorFacebookAuthController', 'diagnose/$' => 'PhabricatorFacebookAuthDiagnosticsController', ), '/xhprof/' => array( 'profile/(?[^/]+)/$' => 'PhabricatorXHProfProfileController', ), + + '/~/' => 'DarkConsoleController', ); } public function buildRequest() { $request = new AphrontRequest($this->getHost(), $this->getPath()); $request->setRequestData($_GET + $_POST); $request->setApplicationConfiguration($this); return $request; } public function handleException(Exception $ex) { $class = phutil_escape_html(get_class($ex)); $message = phutil_escape_html($ex->getMessage()); $content = '
'. '

Unhandled Exception "'.$class.'": '.$message.'

'. ''.phutil_escape_html((string)$ex).''. '
'; $view = new PhabricatorStandardPageView(); $view->setRequest($this->getRequest()); $view->appendChild($content); $response = new AphrontWebpageResponse(); $response->setContent($view->render()); return $response; } public function willSendResponse(AphrontResponse $response) { $request = $this->getRequest(); if ($response instanceof AphrontDialogResponse) { if (!$request->isAjax()) { $view = new PhabricatorStandardPageView(); $view->appendChild( '
'. $response->buildResponseString(). '
'); $response = new AphrontWebpageResponse(); $response->setContent($view->render()); return $response; } else { return id(new AphrontAjaxResponse()) ->setContent(array( 'dialog' => $response->buildResponseString(), )); } } else if ($response instanceof Aphront404Response) { $failure = new AphrontRequestFailureView(); $failure->setHeader('404 Not Found'); $failure->appendChild( '

The page you requested was not found.

'); $view = new PhabricatorStandardPageView(); $view->setTitle('404 Not Found'); $view->setRequest($this->getRequest()); $view->appendChild($failure); $response = new AphrontWebpageResponse(); $response->setContent($view->render()); $response->setHTTPResponseCode(404); return $response; } return $response; } public function build404Controller() { return array(new Phabricator404Controller($this->getRequest()), array()); } } diff --git a/src/applications/base/controller/base/PhabricatorController.php b/src/applications/base/controller/base/PhabricatorController.php index 3ca0460a33..bee50ca2ac 100644 --- a/src/applications/base/controller/base/PhabricatorController.php +++ b/src/applications/base/controller/base/PhabricatorController.php @@ -1,69 +1,69 @@ getRequest(); $user = new PhabricatorUser(); $phusr = $request->getCookie('phusr'); $phsid = $request->getCookie('phsid'); if ($phusr && $phsid) { $info = queryfx_one( $user->establishConnection('r'), 'SELECT u.* FROM %T u JOIN %T s ON u.phid = s.userPHID AND s.type = %s AND s.sessionKey = %s', $user->getTableName(), 'phabricator_session', 'web', $phsid); if ($info) { $user->loadFromArray($info); } } $request->setUser($user); if ($this->shouldRequireLogin() && !$user->getPHID()) { throw new AphrontRedirectException('/login/'); } } public function buildStandardPageView() { $view = new PhabricatorStandardPageView(); $view->setRequest($this->getRequest()); return $view; } - public function buildStandardPageResponse($view) { + public function buildStandardPageResponse($view, array $data) { $page = $this->buildStandardPageView(); $page->appendChild($view); $response = new AphrontWebpageResponse(); $response->setContent($page->render()); return $response; } } diff --git a/src/applications/differential/parser/changeset/DifferentialChangesetParser.php b/src/applications/differential/parser/changeset/DifferentialChangesetParser.php index 7c13efa57d..4612c400e3 100644 --- a/src/applications/differential/parser/changeset/DifferentialChangesetParser.php +++ b/src/applications/differential/parser/changeset/DifferentialChangesetParser.php @@ -1,1333 +1,1334 @@ changeset = $changeset; $this->setFilename($changeset->getFilename()); $this->setChangesetID($changeset->getID()); return $this; } public function setWhitespaceMode($whitespace_mode) { $this->whitespaceMode = $whitespace_mode; return $this; } public function setOldChangesetID($old_changeset_id) { $this->oldChangesetID = $old_changeset_id; return $this; } public function setChangesetID($changeset_id) { $this->changesetID = $changeset_id; return $this; } public function getChangesetID() { return $this->changesetID; } public function setFilename($filename) { $this->filename = $filename; if (strpos($filename, '.', 1) !== false) { - $this->filetype = end(explode('.', $filename)); + $parts = explode('.', $filename); + $this->filetype = end($parts); } } public function setHandles(array $handles) { $this->handles = $handles; return $this; } public function setMarkupEngine(PhutilMarkupEngine $engine) { $this->markupEngine = $engine; return $this; } public function setUser(PhabricatorUser $user) { $this->user = $user; return $this; } public function parseHunk(DifferentialHunk $hunk) { $this->parsedHunk = true; $lines = $hunk->getChanges(); // Flatten UTF-8 into "\0". We don't support UTF-8 because the diffing // algorithms are byte-oriented (not character oriented) and everyone seems // to be in agreement that it's fairly reasonable not to allow UTF-8 in // source files. These bytes will later be replaced with a "?" glyph, but // in the meantime we replace them with "\0" since Pygments is happy to // deal with that. $lines = preg_replace('/[\x80-\xFF]/', "\0", $lines); $lines = str_replace( array("\t", "\r\n", "\r"), array(' ', "\n", "\n"), $lines); $lines = explode("\n", $lines); $types = array(); foreach ($lines as $line_index => $line) { $lines[$line_index] = $line; if (isset($line[0])) { $char = $line[0]; if ($char == ' ') { $types[$line_index] = null; } else if ($char == '\\' && $line_index > 0) { $types[$line_index] = $types[$line_index - 1]; } else { $types[$line_index] = $char; } } else { $types[$line_index] = null; } } $old_line = $hunk->getOldOffset(); $new_line = $hunk->getNewOffset(); $num_lines = count($lines); if ($old_line > 1) { $this->missingOld[$old_line] = true; } else if ($new_line > 1) { $this->missingNew[$new_line] = true; } for ($cursor = 0; $cursor < $num_lines; $cursor++) { $type = $types[$cursor]; $data = array( 'type' => $type, 'text' => (string)substr($lines[$cursor], 1), 'line' => $new_line, ); switch ($type) { case '+': $this->new[] = $data; ++$new_line; break; case '-': $data['line'] = $old_line; $this->old[] = $data; ++$old_line; break; default: $this->new[] = $data; $data['line'] = $old_line; $this->old[] = $data; ++$new_line; ++$old_line; break; } } } public function getDisplayLine($offset, $length) { $start = 1; for ($ii = $offset; $ii > 0; $ii--) { if ($this->new[$ii] && $this->new[$ii]['line']) { $start = $this->new[$ii]['line']; break; } } $end = $start; for ($ii = $offset + $length; $ii < count($this->new); $ii++) { if ($this->new[$ii] && $this->new[$ii]['line']) { $end = $this->new[$ii]['line']; break; } } return "{$start},{$end}"; } public function parseInlineComment(DifferentialInlineComment $comment) { $this->comments[] = $comment; return $this; } public function process() { $old = array(); $new = array(); $n = 0; $this->old = array_reverse($this->old); $this->new = array_reverse($this->new); $whitelines = false; $changed = false; $skip_intra = array(); while (count($this->old) || count($this->new)) { $o_desc = array_pop($this->old); $n_desc = array_pop($this->new); $oend = end($this->old); if ($oend) { $o_next = $oend['type']; } else { $o_next = null; } $nend = end($this->new); if ($nend) { $n_next = $nend['type']; } else { $n_next = null; } if ($o_desc) { $o_type = $o_desc['type']; } else { $o_type = null; } if ($n_desc) { $n_type = $n_desc['type']; } else { $n_type = null; } if (($o_type != null) && ($n_type == null)) { $old[] = $o_desc; $new[] = null; if ($n_desc) { array_push($this->new, $n_desc); } $changed = true; continue; } if (($n_type != null) && ($o_type == null)) { $old[] = null; $new[] = $n_desc; if ($o_desc) { array_push($this->old, $o_desc); } $changed = true; continue; } if ($this->whitespaceMode != self::WHITESPACE_SHOW_ALL) { $similar = false; switch ($this->whitespaceMode) { case self::WHITESPACE_IGNORE_TRAILING: if (rtrim($o_desc['text']) == rtrim($n_desc['text'])) { $similar = true; } break; } if ($similar) { $o_desc['type'] = null; $n_desc['type'] = null; $skip_intra[count($old)] = true; $whitelines = true; } else { $changed = true; } } else { $changed = true; } $old[] = $o_desc; $new[] = $n_desc; } $this->old = $old; $this->new = $new; if ($this->subparser && false) { // TODO: This is bugged // Use the subparser's side-by-side line information -- notably, the // change types -- but replace all the line text with ours. This lets us // render whitespace-only changes without marking them as different. $old = $this->subparser->old; $new = $this->subparser->new; $old_text = ipull($this->old, 'text', 'line'); $new_text = ipull($this->new, 'text', 'line'); foreach ($old as $k => $desc) { if (empty($desc)) { continue; } $old[$k]['text'] = idx($old_text, $desc['line']); } foreach ($new as $k => $desc) { if (empty($desc)) { continue; } $new[$k]['text'] = idx($new_text, $desc['line']); } $this->old = $old; $this->new = $new; } $min_length = min(count($this->old), count($this->new)); for ($ii = 0; $ii < $min_length; $ii++) { if ($this->old[$ii] || $this->new[$ii]) { if (isset($this->old[$ii]['text'])) { $otext = $this->old[$ii]['text']; } else { $otext = ''; } if (isset($this->new[$ii]['text'])) { $ntext = $this->new[$ii]['text']; } else { $ntext = ''; } if ($otext != $ntext && empty($skip_intra[$ii])) { $this->intra[$ii] = ArcanistDiffUtils::generateIntralineDiff( $otext, $ntext); } } } $lines_context = self::LINES_CONTEXT; $max_length = max(count($this->old), count($this->new)); $old = $this->old; $new = $this->new; $visible = false; $last = 0; for ($cursor = -$lines_context; $cursor < $max_length; $cursor++) { $offset = $cursor + $lines_context; if ((isset($old[$offset]) && $old[$offset]['type']) || (isset($new[$offset]) && $new[$offset]['type'])) { $visible = true; $last = $offset; } else if ($cursor > $last + $lines_context) { $visible = false; } if ($visible && $cursor > 0) { $this->visible[$cursor] = 1; } } $old_corpus = ipull($this->old, 'text'); $old_corpus_block = implode("\n", $old_corpus); $new_corpus = ipull($this->new, 'text'); $new_corpus_block = implode("\n", $new_corpus); if ($this->noHighlight) { $this->oldRender = explode("\n", phutil_escape_html($old_corpus_block)); $this->newRender = explode("\n", phutil_escape_html($new_corpus_block)); } else { $this->oldRender = $this->sourceHighlight($this->old, $old_corpus_block); $this->newRender = $this->sourceHighlight($this->new, $new_corpus_block); } $this->applyIntraline( $this->oldRender, ipull($this->intra, 0), $old_corpus); $this->applyIntraline( $this->newRender, ipull($this->intra, 1), $new_corpus); $this->tokenHighlight($this->oldRender); $this->tokenHighlight($this->newRender); $unchanged = false; if ($this->subparser && false) { $unchanged = $this->subparser->isUnchanged(); $whitelines = $this->subparser->isWhitespaceOnly(); } else if (!$changed) { $filetype = $this->changeset->getFileType(); if ($filetype == DifferentialChangeType::FILE_TEXT || $filetype == DifferentialChangeType::FILE_SYMLINK) { $unchanged = true; } } $generated = (strpos($new_corpus_block, '@'.'generated') !== false); $this->specialAttributes = array( self::ATTR_GENERATED => $generated, self::ATTR_UNCHANGED => $unchanged, self::ATTR_DELETED => array_filter($this->old) && !array_filter($this->new), self::ATTR_WHITELINES => $whitelines ); } public function loadCache() { if (!$this->changesetID) { return false; } $data = null; $changeset = new DifferentialChangeset(); $conn_r = $changeset->establishConnection('r'); $data = queryfx_one( $conn_r, 'SELECT * FROM %T WHERE id = %d', $changeset->getTableName().'_parse_cache', $this->changesetID); if (!$data) { return false; } $data = json_decode($data['cache'], true); if (!is_array($data) || !$data) { return false; } foreach (self::getCacheableProperties() as $cache_key) { if (!array_key_exists($cache_key, $data)) { // If we're missing a cache key, assume we're looking at an old cache // and ignore it. return false; } } if ($data['cacheVersion'] !== self::CACHE_VERSION) { return false; } unset($data['cacheVersion'], $data['cacheHost']); $cache_prop = array_select_keys($data, self::getCacheableProperties()); foreach ($cache_prop as $cache_key => $v) { $this->$cache_key = $v; } return true; } protected static function getCacheableProperties() { return array( 'visible', 'new', 'old', 'intra', 'newRender', 'oldRender', 'specialAttributes', 'missingOld', 'missingNew', 'cacheVersion', 'cacheHost', ); } public function saveCache() { if (!$this->changesetID) { return false; } $cache = array(); foreach (self::getCacheableProperties() as $cache_key) { switch ($cache_key) { case 'cacheVersion': $cache[$cache_key] = self::CACHE_VERSION; break; case 'cacheHost': $cache[$cache_key] = php_uname('n'); break; default: $cache[$cache_key] = $this->$cache_key; break; } } $cache = json_encode($cache); try { $changeset = new DifferentialChangeset(); $conn_w = $changeset->establishConnection('w'); queryfx( $conn_w, 'INSERT INTO %T (id, cache) VALUES (%d, %s) ON DUPLICATE KEY UPDATE cache = VALUES(cache)', $changeset->getTableName().'_parse_cache', $this->changesetID, $cache); } catch (QueryException $ex) { // TODO: uhoh } } public function isGenerated() { return idx($this->specialAttributes, self::ATTR_GENERATED, false); } public function isDeleted() { return idx($this->specialAttributes, self::ATTR_DELETED, false); } public function isUnchanged() { return idx($this->specialAttributes, self::ATTR_UNCHANGED, false); } public function isWhitespaceOnly() { return idx($this->specialAttributes, self::ATTR_WHITELINES, false); } public function getLength() { return max(count($this->old), count($this->new)); } protected function applyIntraline(&$render, $intra, $corpus) { foreach ($render as $key => $text) { if (isset($intra[$key])) { $render[$key] = ArcanistDiffUtils::applyIntralineDiff( $text, $intra[$key]); } if (isset($corpus[$key]) && strlen($corpus[$key]) > 80) { $render[$key] = $this->lineWrap($render[$key]); } } } protected function lineWrap($l) { $c = 0; $len = strlen($l); $ins = array(); for ($ii = 0; $ii < $len; ++$ii) { if ($l[$ii] == '&') { do { ++$ii; } while ($l[$ii] != ';'); ++$c; } else if ($l[$ii] == '<') { do { ++$ii; } while ($l[$ii] != '>'); } else { ++$c; } if ($c == 80) { $ins[] = ($ii + 1); $c = 0; } } while (($pos = array_pop($ins))) { $l = substr_replace( $l, "\xE2\xAC\x85
", $pos, 0); } return $l; } protected function tokenHighlight(&$render) { foreach ($render as $key => $text) { $render[$key] = str_replace( "\0", ''."\xEF\xBF\xBD".'', $text); } } protected function sourceHighlight($data, $corpus) { $result = $this->highlightEngine->highlightSource( $this->filetype, $corpus); $result_lines = explode("\n", $result); foreach ($data as $key => $info) { if (!$info) { unset($result_lines[$key]); } } return $result_lines; } private function tryCacheStuff() { $whitespace_mode = $this->whitespaceMode; switch ($whitespace_mode) { case self::WHITESPACE_SHOW_ALL: case self::WHITESPACE_IGNORE_TRAILING: break; default: $whitespace_mode = self::WHITESPACE_IGNORE_ALL; break; } $skip_cache = ($whitespace_mode != self::WHITESPACE_IGNORE_ALL); $this->whitespaceMode = $whitespace_mode; $changeset = $this->changeset; if ($changeset->getFileType() == DifferentialChangeType::FILE_TEXT || $changeset->getFileType() == DifferentialChangeType::FILE_SYMLINK) { if ($skip_cache || !$this->loadCache()) { if ($this->whitespaceMode == self::WHITESPACE_IGNORE_ALL) { // Huge mess. Generate a "-bw" (ignore all whitespace changes) diff, // parse it out, and then play a shell game with the parsed format // in process() so we highlight only changed lines but render // whitespace differences. If we don't do this, we either fail to // render whitespace changes (which is incredibly confusing, // especially for python) or often produce a much larger set of // differences than necessary. $old_tmp = new TempFile(); $new_tmp = new TempFile(); Filesystem::writeFile($old_tmp, $changeset->makeOldFile()); Filesystem::writeFile($new_tmp, $changeset->makeNewFile()); list($err, $diff) = exec_manual( 'diff -bw -U9999 %s %s', $old_tmp, $new_tmp); if (!strlen($diff)) { // If there's no diff text, that means the files are identical // except for whitespace changes. Build a synthetic, changeless // diff. TODO: this is incredibly hacky. $entire_file = explode("\n", $changeset->makeOldFile()); foreach ($entire_file as $k => $line) { $entire_file[$k] = ' '.$line; } $len = count($entire_file); $entire_file = implode("\n", $entire_file); $diff = <<parseDiff($diff); $diff = DifferentialDiff::newFromRawChanges($changes); $alt_changeset = reset($diff->getChangesets()); $this->subparser = new DifferentialChangesetParser(); $this->subparser->setChangeset($alt_changeset); $this->subparser->setWhitespaceMode(self::WHITESPACE_IGNORE_TRAILING); } foreach ($changeset->getHunks() as $hunk) { $this->parseHunk($hunk); } $this->process(); if (!$skip_cache) { $this->saveCache(); } } } } public function render( $range_start = null, $range_len = null, $mask_force = array()) { $this->highlightEngine = new PhutilDefaultSyntaxHighlighterEngine(); $this->tryCacheStuff(); $changeset_id = $this->changesetID; $feedback_mask = array(); switch ($this->changeset->getFileType()) { case DifferentialChangeType::FILE_IMAGE: $old = null; $cur = null; $metadata = $this->changeset->getMetadata(); $data = idx($metadata, 'attachment-data'); $old_phid = idx($metadata, 'old:binary-phid'); $new_phid = idx($metadata, 'new:binary-phid'); if ($old_phid || $new_phid) { if ($old_phid) { $old_uri = PhabricatorFileURI::getViewURIForPHID($old_phid); $old = phutil_render_tag( 'img', array( 'src' => $old_uri, )); } if ($new_phid) { $new_uri = PhabricatorFileURI::getViewURIForPHID($new_phid); $cur = phutil_render_tag( 'img', array( 'src' => $new_uri, )); } } $output = $this->renderChangesetTable( $this->changeset, '
'. ''. ''. ''. ''. ''); return $output; case DifferentialChangeType::FILE_DIRECTORY: case DifferentialChangeType::FILE_BINARY: $output = $this->renderChangesetTable($this->changeset, null); return $output; } $shield = null; if ($range_start === null && $range_len === null) { if ($this->isGenerated()) { $shield = $this->renderShield( "This file contains generated code, which does not normally need ". "to be reviewed.", true); } else if ($this->isUnchanged()) { if ($this->isWhitespaceOnly()) { $shield = $this->renderShield( "This file was changed only by adding or removing trailing ". "whitespace.", false); } else { $shield = $this->renderShield( "The contents of this file were not changed.", false); } } else if ($this->isDeleted()) { $shield = $this->renderShield( "This file was completely deleted.", true); } else if ($this->changeset->getAffectedLineCount() > 2500) { $lines = number_format($this->changeset->getAffectedLineCount()); $shield = $this->renderShield( "This file has a very large number of changes ({$lines} lines).", true); } else if (preg_match('/\.sql3$/', $this->changeset->getFilename())) { $shield = $this->renderShield( ".sql3 files are hidden by default.", true); } } if ($shield) { return $this->renderChangesetTable($this->changeset, $shield); } $old_comments = array(); $new_comments = array(); $old_mask = array(); $new_mask = array(); $feedback_mask = array(); if ($this->comments) { foreach ($this->comments as $comment) { $start = max($comment->getLineNumber() - self::LINES_CONTEXT, 0); $end = $comment->getLineNumber() + $comment->getLineLength() + self::LINES_CONTEXT; $new = $this->isCommentInNewFile($comment); for ($ii = $start; $ii <= $end; $ii++) { if ($new) { $new_mask[$ii] = true; } else { $old_mask[$ii] = true; } } } foreach ($this->old as $ii => $old) { if (isset($old['line']) && isset($old_mask[$old['line']])) { $feedback_mask[$ii] = true; } } foreach ($this->new as $ii => $new) { if (isset($new['line']) && isset($new_mask[$new['line']])) { $feedback_mask[$ii] = true; } } $this->comments = msort($this->comments, 'getID'); foreach ($this->comments as $comment) { $final = $comment->getLineNumber() + $comment->getLineLength(); if ($this->isCommentInNewFile($comment)) { $new_comments[$final][] = $comment; } else { $old_comments[$final][] = $comment; } } } $html = $this->renderTextChange( $range_start, $range_len, $mask_force, $feedback_mask, $old_comments, $new_comments); return $this->renderChangesetTable($this->changeset, $html); } private function isCommentInNewFile(DifferentialInlineComment $comment) { if ($this->oldChangesetID) { return ($comment->getChangesetID() != $this->oldChangesetID); } else { return $comment->getIsNewFile(); } } protected function renderShield($message, $more) { $end = $this->getLength(); $changeset_id = $this->getChangesetID(); if ($more) { $more = ' '. javelin_render_tag( 'a', array( 'mustcapture' => true, 'sigil' => 'show-more', 'class' => 'complete', 'href' => '#', 'meta' => array( 'id' => $changeset_id, 'range' => "0-{$end}", ), ), 'Show File Contents'); } else { $more = null; } return javelin_render_tag( 'tr', array( 'sigil' => 'context-target', ), ''); } protected function renderTextChange( $range_start, $range_len, $mask_force, $feedback_mask, array $old_comments, array $new_comments) { $context_not_available = null; if ($this->missingOld || $this->missingNew) { $context_not_available = javelin_render_tag( 'tr', array( 'sigil' => 'context-target', ), ''); $context_not_available = $context_not_available; } $html = array(); $rows = max( count($this->old), count($this->new)); if ($range_start === null) { $range_start = 0; } if ($range_len === null) { $range_len = $rows; } $range_len = min($range_len, $rows - $range_start); $gaps = array(); $gap_start = 0; $in_gap = false; $mask = $this->visible + $mask_force + $feedback_mask; $mask[$range_start + $range_len] = true; for ($ii = $range_start; $ii <= $range_start + $range_len; $ii++) { if (isset($mask[$ii])) { if ($in_gap) { $gap_length = $ii - $gap_start; if ($gap_length <= self::LINES_CONTEXT) { for ($jj = $gap_start; $jj <= $gap_start + $gap_length; $jj++) { $mask[$jj] = true; } } else { $gaps[] = array($gap_start, $gap_length); } $in_gap = false; } } else { if (!$in_gap) { $gap_start = $ii; $in_gap = true; } } } $gaps = array_reverse($gaps); $changeset = $this->changesetID; for ($ii = $range_start; $ii < $range_start + $range_len; $ii++) { if (empty($mask[$ii])) { $gap = array_pop($gaps); $top = $gap[0]; $len = $gap[1]; $end = $top + $len - 20; $contents = array(); if ($len > 40) { $contents[] = javelin_render_tag( 'a', array( 'href' => '#', 'mustcapture' => true, 'sigil' => 'show-more', 'meta' => array( 'id' => $changeset, 'range' => "{$top}-{$len}/{$top}-20", ), ), "\xE2\x96\xB2 Show 20 Lines"); } $contents[] = javelin_render_tag( 'a', array( 'href' => '#', 'mustcapture' => true, 'sigil' => 'show-more', 'meta' => array( 'id' => $changeset, 'range' => "{$top}-{$len}/{$top}-{$len}", ), ), 'Show All '.$len.' Lines'); if ($len > 40) { $contents[] = javelin_render_tag( 'a', array( 'href' => '#', 'mustcapture' => true, 'sigil' => 'show-more', 'meta' => array( 'id' => $changeset, 'range' => "{$top}-{$len}/{$end}-20", ), ), "\xE2\x96\xBC Show 20 Lines"); }; $container = javelin_render_tag( 'tr', array( 'sigil' => 'context-target', ), ''); $html[] = $container; $ii += ($len - 1); continue; } if (isset($this->old[$ii])) { $o_num = $this->old[$ii]['line']; $o_text = $this->oldRender[$ii]; $o_attr = null; if ($this->old[$ii]['type']) { if (empty($this->new[$ii])) { $o_attr = ' class="old old-full"'; } else { $o_attr = ' class="old"'; } } } else { $o_num = null; $o_text = null; $o_attr = null; } if (isset($this->new[$ii])) { $n_num = $this->new[$ii]['line']; $n_text = $this->newRender[$ii]; $n_attr = null; if ($this->new[$ii]['type']) { if (empty($this->old[$ii])) { $n_attr = ' class="new new-full"'; } else { $n_attr = ' class="new"'; } } } else { $n_num = null; $n_text = null; $n_attr = null; } if (($o_num && !empty($this->missingOld[$o_num])) || ($n_num && !empty($this->missingNew[$n_num]))) { $html[] = $context_not_available; } if ($o_num) { $o_id = ' id="C'.$changeset.'OL'.$o_num.'"'; } else { $o_id = null; } if ($n_num) { $n_id = ' id="C'.$changeset.'NL'.$n_num.'"'; } else { $n_id = null; } // NOTE: The Javascript is sensitive to whitespace changes in this // block! $html[] = ''. ''.$o_num.''. ''.$o_text.''. ''.$n_num.''. ''.$n_text.''. ''; if ($context_not_available && ($ii == $rows - 1)) { $html[] = $context_not_available; } if ($o_num && isset($old_comments[$o_num])) { foreach ($old_comments[$o_num] as $comment) { $xhp = $this->renderInlineComment($comment); $html[] = ''; } } if ($n_num && isset($new_comments[$n_num])) { foreach ($new_comments[$n_num] as $comment) { $xhp = $this->renderInlineComment($comment); $html[] = ''; } } } return implode('', $html); } private function renderInlineComment(DifferentialInlineComment $comment) { $user = $this->user; $edit = $user && ($comment->getAuthorPHID() == $user->getPHID()) && (!$comment->getCommentID()); $on_right = $this->isCommentInNewFile($comment); return id(new DifferentialInlineCommentView()) ->setInlineComment($comment) ->setOnRight($on_right) ->setHandles($this->handles) ->setMarkupEngine($this->markupEngine) ->setEditable($edit) ->render(); } protected function renderPropertyChangeHeader($changeset) { $old = $changeset->getOldProperties(); $new = $changeset->getNewProperties(); if ($old === $new) { return null; } if ($changeset->getChangeType() == DifferentialChangeType::TYPE_ADD && $new == array('unix:filemode' => '100644')) { return null; } if ($changeset->getChangeType() == DifferentialChangeType::TYPE_DELETE && $old == array('unix:filemode' => '100644')) { return null; } return null; /* TODO $table =
{$key}{$value}
'. '
'. $old. '
'. '
'. '
'. $cur. '
'. '
'. phutil_escape_html($message). $more. ''. 'Context not available.'. ''. implode(' • ', $contents). '
'. $xhp. '
'. $xhp. '
; $table->appendChild( ); $keys = array_keys($old + $new); sort($keys); foreach ($keys as $key) { $oval = idx($old, $key); $nval = idx($new, $key); if ($oval !== $nval) { if ($oval === null) { $oval = null; } if ($nval === null) { $nval = null; } $table->appendChild( ); } } return $table; */ } protected function renderChangesetTable($changeset, $contents) { $props = $this->renderPropertyChangeHeader($this->changeset); $table = null; if ($contents) { $table = '
Property Changes Old Value New Value
{$key} {$oval} {$nval}
'. $contents. '
'; } if (!$table && !$props) { $notice = $this->renderChangeTypeHeader($this->changeset, true); } else { $notice = $this->renderChangeTypeHeader($this->changeset, false); } return implode( "\n", array( $notice, $props, $table, )); } protected function renderChangeTypeHeader($changeset, $force) { static $articles = array( DifferentialChangeType::FILE_IMAGE => 'an', ); static $files = array( DifferentialChangeType::FILE_TEXT => 'file', DifferentialChangeType::FILE_IMAGE => 'image', DifferentialChangeType::FILE_DIRECTORY => 'directory', DifferentialChangeType::FILE_BINARY => 'binary file', DifferentialChangeType::FILE_SYMLINK => 'symlink', ); static $changes = array( DifferentialChangeType::TYPE_ADD => 'added', DifferentialChangeType::TYPE_CHANGE => 'changed', DifferentialChangeType::TYPE_DELETE => 'deleted', DifferentialChangeType::TYPE_MOVE_HERE => 'moved from', DifferentialChangeType::TYPE_COPY_HERE => 'copied from', DifferentialChangeType::TYPE_MOVE_AWAY => 'moved to', DifferentialChangeType::TYPE_COPY_AWAY => 'copied to', DifferentialChangeType::TYPE_MULTICOPY => 'deleted after being copied to', ); $change = $changeset->getChangeType(); $file = $changeset->getFileType(); $message = null; if ($change == DifferentialChangeType::TYPE_CHANGE && $file == DifferentialChangeType::FILE_TEXT) { if ($force) { // We have to force something to render because there were no changes // of other kinds. $message = "This {$files[$file]} was not modified."; } else { // Default case of changes to a text file, no metadata. return null; } } else { $verb = idx($changes, $change, 'changed'); switch ($change) { default: $message = "This {$files[$file]} was {$verb}."; break; case DifferentialChangeType::TYPE_MOVE_HERE: case DifferentialChangeType::TYPE_COPY_HERE: $message = "This {$files[$file]} was {$verb} ". "{$changeset->getOldFile()}."; break; case DifferentialChangeType::TYPE_MOVE_AWAY: case DifferentialChangeType::TYPE_COPY_AWAY: case DifferentialChangeType::TYPE_MULTICOPY: $paths = $changeset->getAwayPaths(); if (count($paths) > 1) { $message = "This {$files[$file]} was {$verb}: ". "".implode(', ', $paths)."."; } else { $message = "This {$files[$file]} was {$verb} ". "".reset($paths)."."; } break; case DifferentialChangeType::TYPE_CHANGE: $message = "This is ".idx($articles, $file, 'a')." {$files[$file]}."; break; } } return '
'. $message. '
'; } public function renderForEmail() { $ret = ''; $min = min(count($this->old), count($this->new)); for ($i = 0; $i < $min; $i++) { $o = $this->old[$i]; $n = $this->new[$i]; if (!isset($this->visible[$i])) { continue; } if ($o['line'] && $n['line']) { // It is quite possible there are better ways to achieve this. For // example, "white-space: pre;" can do a better job, WERE IT NOT for // broken email clients like OWA which use newlines to do weird // wrapping. So dont give them newlines. if (isset($this->intra[$i])) { $ret .= sprintf( "- %s
", str_replace(" ", " ", phutil_escape_html($o['text'])) ); $ret .= sprintf( "+ %s
", str_replace(" ", " ", phutil_escape_html($n['text'])) ); } else { $ret .= sprintf("  %s
", str_replace(" ", " ", phutil_escape_html($n['text'])) ); } } else if ($o['line'] && !$n['line']) { $ret .= sprintf( "- %s
", str_replace(" ", " ", phutil_escape_html($o['text'])) ); } else { $ret .= sprintf( "+ %s
", str_replace(" ", " ", phutil_escape_html($n['text'])) ); } } return $ret; } } diff --git a/src/applications/people/storage/user/PhabricatorUser.php b/src/applications/people/storage/user/PhabricatorUser.php index ccf118a050..d9d8f5590f 100644 --- a/src/applications/people/storage/user/PhabricatorUser.php +++ b/src/applications/people/storage/user/PhabricatorUser.php @@ -1,151 +1,155 @@ profileImagePHID, PhabricatorEnv::getEnvConfig('user.default-profile-image-phid')); } public function getConfiguration() { return array( self::CONFIG_AUX_PHID => true, ) + parent::getConfiguration(); } public function generatePHID() { return PhabricatorPHID::generateNewPHID(self::PHID_TYPE); } public function setPassword($password) { $this->setPasswordSalt(md5(mt_rand())); $hash = $this->hashPassword($password); $this->setPasswordHash($hash); return $this; } public function comparePassword($password) { $password = $this->hashPassword($password); return ($password === $this->getPasswordHash()); } private function hashPassword($password) { $password = $this->getUsername(). $password. $this->getPHID(). $this->getPasswordSalt(); for ($ii = 0; $ii < 1000; $ii++) { $password = md5($password); } return $password; } const CSRF_CYCLE_FREQUENCY = 3600; const CSRF_TOKEN_LENGTH = 16; const EMAIL_CYCLE_FREQUENCY = 86400; const EMAIL_TOKEN_LENGTH = 24; public function getCSRFToken($offset = 0) { return $this->generateToken( time() + (self::CSRF_CYCLE_FREQUENCY * $offset), self::CSRF_CYCLE_FREQUENCY, PhabricatorEnv::getEnvConfig('phabricator.csrf-key'), self::CSRF_TOKEN_LENGTH); } public function validateCSRFToken($token) { for ($ii = -1; $ii <= 1; $ii++) { $valid = $this->getCSRFToken($ii); if ($token == $valid) { return true; } } return false; } private function generateToken($epoch, $frequency, $key, $len) { $time_block = floor($epoch / $frequency); $vec = $this->getPHID().$this->passwordHash.$key.$time_block; return substr(sha1($vec), 0, $len); } public function establishSession($session_type) { $conn_w = $this->establishConnection('w'); $urandom = fopen('/dev/urandom', 'r'); if (!$urandom) { throw new Exception("Failed to open /dev/urandom!"); } $entropy = fread($urandom, 20); if (strlen($entropy) != 20) { throw new Exception("Failed to read /dev/urandom!"); } $session_key = sha1($entropy); queryfx( $conn_w, 'INSERT INTO phabricator_session '. '(userPHID, type, sessionKey, sessionStart)'. ' VALUES '. '(%s, %s, %s, UNIX_TIMESTAMP()) '. 'ON DUPLICATE KEY UPDATE '. 'sessionKey = VALUES(sessionKey), '. 'sessionStart = VALUES(sessionStart)', $this->getPHID(), $session_type, $session_key); + $this->sessionKey = $session_key; + return $session_key; } public function generateEmailToken($offset = 0) { return $this->generateToken( time() + ($offset * self::EMAIL_CYCLE_FREQUENCY), self::EMAIL_CYCLE_FREQUENCY, PhabricatorEnv::getEnvConfig('phabricator.csrf-key').$this->getEmail(), self::EMAIL_TOKEN_LENGTH); } public function validateEmailToken($token) { for ($ii = -1; $ii <= 1; $ii++) { $valid = $this->generateEmailToken($ii); if ($token == $valid) { return true; } } return false; } } diff --git a/src/storage/connection/mysql/AphrontMySQLDatabaseConnection.php b/src/storage/connection/mysql/AphrontMySQLDatabaseConnection.php index 3a52dc770f..cf51cde524 100644 --- a/src/storage/connection/mysql/AphrontMySQLDatabaseConnection.php +++ b/src/storage/connection/mysql/AphrontMySQLDatabaseConnection.php @@ -1,203 +1,232 @@ configuration = $configuration; } public function escapeString($string) { - if (!$this->connection) { - $this->establishConnection(); - } + $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 establishConnection() { $this->connection = null; $user = $this->getConfiguration('user'); $host = $this->getConfiguration('host'); + $database = $this->getConfiguration('database'); + + $key = "{$user}:{$host}:{$database}"; + if (isset(self::$connectionCache[$key])) { + $this->connection = self::$connectionCache[$key]; + return; + } + + $start = microtime(true); $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}."); } - $ret = @mysql_select_db($this->getConfiguration('database'), $conn); + $ret = @mysql_select_db($database, $conn); if (!$ret) { $this->throwQueryException($conn); } + $end = microtime(true); + + DarkConsoleServicesPluginAPI::addEvent( + array( + 'event' => DarkConsoleServicesPluginAPI::EVENT_CONNECT, + 'host' => $host, + 'database' => $database, + 'start' => $start, + 'end' => $end, + )); + + 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 { - if (!$this->connection) { - $this->establishConnection(); - } + $this->requireConnection(); + $start = microtime(true); $result = mysql_query($raw_query, $this->connection); + $end = microtime(true); + + DarkConsoleServicesPluginAPI::addEvent( + array( + 'event' => DarkConsoleServicesPluginAPI::EVENT_QUERY, + 'query' => $raw_query, + 'start' => $start, + 'end' => $end, + )); if ($result) { $this->lastResult = $result; break; } $this->throwQueryException($this->connection); } catch (AphrontQueryConnectionLostException $ex) { if (!$retries) { throw $ex; } if ($this->isInsideTransaction()) { throw $ex; } $this->connection = null; } } } 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 $matches = null; $key = null; if (preg_match('/for key \'(.*)\'$/', $error, $matches)) { $key = $matches[1]; } throw new AphrontQueryDuplicateKeyException($key, "{$errno}: {$error}"); default: // TODO: 1064 is syntax error, and quite terrible in production. throw new AphrontQueryException("#{$errno}: {$error}"); } } } diff --git a/src/storage/connection/mysql/__init__.php b/src/storage/connection/mysql/__init__.php index a92470889b..05fd2062c7 100644 --- a/src/storage/connection/mysql/__init__.php +++ b/src/storage/connection/mysql/__init__.php @@ -1,19 +1,20 @@ ', where '' ". "is one of 'development', 'production', or a custom environment."); } $conf = phabricator_read_config_file($env); $conf['phabricator.env'] = $env; setup_aphront_basics(); phutil_require_module('phabricator', 'infrastructure/env'); PhabricatorEnv::setEnvConfig($conf); phutil_require_module('phabricator', 'aphront/console/plugin/xhprof/api'); DarkConsoleXHProfPluginAPI::hookProfiler(); +phutil_require_module('phabricator', 'aphront/console/plugin/errorlog/api'); +set_error_handler(array('DarkConsoleErrorLogPluginAPI', 'handleError')); +set_exception_handler(array('DarkConsoleErrorLogPluginAPI', 'handleException')); + $host = $_SERVER['HTTP_HOST']; $path = $_REQUEST['__path__']; // Based on the host and path, choose which application should serve the // request. The default is the Aphront demo, but you'll want to replace this // with whichever other applications you're running. switch ($host) { default: phutil_require_module('phutil', 'autoload'); phutil_autoload_class('AphrontDefaultApplicationConfiguration'); $application = new AphrontDefaultApplicationConfiguration(); break; } $application->setHost($host); $application->setPath($path); $application->willBuildRequest(); $request = $application->buildRequest(); $application->setRequest($request); list($controller, $uri_data) = $application->buildController(); try { $controller->willBeginExecution(); $controller->willProcessRequest($uri_data); $response = $controller->processRequest(); } catch (AphrontRedirectException $ex) { $response = id(new AphrontRedirectResponse()) ->setURI($ex->getURI()); } catch (Exception $ex) { $response = $application->handleException($ex); } $response = $application->willSendResponse($response); $response->setRequest($request); $response_string = $response->buildResponseString(); $code = $response->getHTTPResponseCode(); if ($code != 200) { header("HTTP/1.0 {$code}"); } $headers = $response->getCacheHeaders(); $headers = array_merge($headers, $response->getHeaders()); foreach ($headers as $header) { list($header, $value) = $header; header("{$header}: {$value}"); } if (isset($_REQUEST['__profile__']) && ($_REQUEST['__profile__'] == 'all')) { $profile = DarkConsoleXHProfPluginAPI::stopProfiler(); - $profile = + $profile = '
'. ''. '>>> View Profile <<<'. ''. '
'; if (strpos($response_string, '') !== false) { $response_string = str_replace( '', ''.$profile, $response_string); } else { echo $profile; } } echo $response_string; /** * @group aphront */ function setup_aphront_basics() { $aphront_root = dirname(dirname(__FILE__)); $libraries_root = dirname($aphront_root); ini_set('include_path', ini_get('include_path').':'.$libraries_root.'/'); @include_once 'libphutil/src/__phutil_library_init__.php'; if (!@constant('__LIBPHUTIL__')) { echo "ERROR: Unable to load libphutil. Update your PHP 'include_path' to ". "include the parent directory of libphutil/.\n"; exit(1); } if (!ini_get('date.timezone')) { date_default_timezone_set('America/Los_Angeles'); } phutil_load_library($libraries_root.'/arcanist/src'); phutil_load_library($aphront_root.'/src'); } function __autoload($class_name) { PhutilSymbolLoader::loadClass($class_name); } function phabricator_read_config_file($config) { $root = dirname(dirname(__FILE__)); $conf = include $root.'/conf/'.$config.'.conf.php'; if ($conf === false) { throw new Exception("Failed to read config file '{$config}'."); } return $conf; } diff --git a/webroot/rsrc/css/aphront/dark-console.css b/webroot/rsrc/css/aphront/dark-console.css index 9e6989698d..c1e63cb14d 100644 --- a/webroot/rsrc/css/aphront/dark-console.css +++ b/webroot/rsrc/css/aphront/dark-console.css @@ -1,46 +1,72 @@ /** * @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; +} diff --git a/webroot/rsrc/css/aphront/table-view.css b/webroot/rsrc/css/aphront/table-view.css index 18fbaec6d1..dedf8a44b2 100644 --- a/webroot/rsrc/css/aphront/table-view.css +++ b/webroot/rsrc/css/aphront/table-view.css @@ -1,78 +1,83 @@ /** * @provides aphront-table-view-css */ .aphront-table-view { width: 100%; border-collapse: collapse; background: #fdfdfd; border: 1px solid #003366; } .aphront-table-view tr.alt { background: #efefef; } .aphront-table-view th { font-size: 12px; font-weight: bold; padding: 4px 8px; background: #003366; color: white; white-space: nowrap; } .aphront-table-view td.header { padding: 4px 8px; background: #3b5998; color: white; white-space: nowrap; text-align: right; } .aphront-table-view td { vertical-align: top; padding: 4px 8px; font-size: 11px; white-space: nowrap; } .aphront-table-view td.action { padding-top: 1px; padding-bottom: 1px; } .aphront-table-view td.larger { font-size: 14px; } .aphront-table-view td.pri { font-weight: bold; } .aphront-table-view td.wide { white-space: normal; width: 100%; } .aphront-table-view td.right { text-align: right; } .aphront-table-view td.mono { font-family: "Monaco", monospace; font-size: 10px; } .aphront-table-view td.n { font-family: "Monaco", monospace; font-size: 10px; text-align: right; } +.aprhont-table-view td.wrap { + white-space: normal; +} + + .aphront-table-view tr.no-data td { padding: 1em; text-align: center; color: #888888; font-style: italic; } diff --git a/webroot/rsrc/js/application/core/behavior-dark-console.js b/webroot/rsrc/js/application/core/behavior-dark-console.js index afa6b9a2ba..810d0cf6fd 100644 --- a/webroot/rsrc/js/application/core/behavior-dark-console.js +++ b/webroot/rsrc/js/application/core/behavior-dark-console.js @@ -1,55 +1,54 @@ /** * @provides javelin-behavior-dark-console */ -JX.behavior('dark-console', function() { +JX.behavior('dark-console', function(config) { 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]); } -/* - new JX.Request(e.getNodeData('dark-console').uri, JX.bag) - .setData({tab: e.getNodeData('dark-console-tab').key}) + + new JX.Request(config.uri, JX.bag) + .setData({tab: target.id.replace('dark-console-tab-', '')}) .send(); -*/ }); JX.Stratcom.listen( 'keypress', null, function(e) { var raw = e.getRawEvent(); if ((String.fromCharCode(raw.charCode).charAt(0) == '`') && !raw.shiftKey && !raw.metaKey) { if (JX.Stratcom.pass()) { return; } var console = JX.DOM.find(document.body, 'table', 'dark-console'); - var data = JX.Stratcom.getData(console); - data.visible = !data.visible; - if (data.visible) { + + config.visible = !config.visible; + if (config.visible) { JX.DOM.show(console); } else { JX.DOM.hide(console); } -// new JX.Request(data.uri, JX.bag) -// .setData({visible: data.visible}) -// .send(); + new JX.Request(config.uri, JX.bag) + .setData({visible: config.visible ? 1 : 0}) + .send(); } }); });