diff --git a/conf/default.conf.php b/conf/default.conf.php index 1217dc6238..6cc166c323 100644 --- a/conf/default.conf.php +++ b/conf/default.conf.php @@ -839,6 +839,9 @@ return array( // Should Phabricator show beta applications on the homepage 'phabricator.show-beta-applications' => false, + // Contains a list of uninstalled applications + 'phabricator.uninstalled-applications' => array(), + // -- Files ----------------------------------------------------------------- // // Lists which uploaded file types may be viewed in the browser. If a file diff --git a/resources/sprite/conpher_1x/calendar_off.png b/resources/sprite/conpher_1x/calendar_off.png new file mode 100644 index 0000000000..d4bbae5dfe Binary files /dev/null and b/resources/sprite/conpher_1x/calendar_off.png differ diff --git a/resources/sprite/conpher_1x/calendar_on.png b/resources/sprite/conpher_1x/calendar_on.png new file mode 100644 index 0000000000..9759fc0546 Binary files /dev/null and b/resources/sprite/conpher_1x/calendar_on.png differ diff --git a/resources/sprite/conpher_1x/conversation_off.png b/resources/sprite/conpher_1x/conversation_off.png new file mode 100644 index 0000000000..2a0310bca2 Binary files /dev/null and b/resources/sprite/conpher_1x/conversation_off.png differ diff --git a/resources/sprite/conpher_1x/conversation_on.png b/resources/sprite/conpher_1x/conversation_on.png new file mode 100644 index 0000000000..11e8437a0a Binary files /dev/null and b/resources/sprite/conpher_1x/conversation_on.png differ diff --git a/resources/sprite/conpher_1x/files_off.png b/resources/sprite/conpher_1x/files_off.png new file mode 100644 index 0000000000..0eb7e3996d Binary files /dev/null and b/resources/sprite/conpher_1x/files_off.png differ diff --git a/resources/sprite/conpher_1x/files_on.png b/resources/sprite/conpher_1x/files_on.png new file mode 100644 index 0000000000..5699ec5f00 Binary files /dev/null and b/resources/sprite/conpher_1x/files_on.png differ diff --git a/resources/sprite/conpher_1x/list_off.png b/resources/sprite/conpher_1x/list_off.png new file mode 100644 index 0000000000..5f64e19b74 Binary files /dev/null and b/resources/sprite/conpher_1x/list_off.png differ diff --git a/resources/sprite/conpher_1x/list_on.png b/resources/sprite/conpher_1x/list_on.png new file mode 100644 index 0000000000..5f104f217d Binary files /dev/null and b/resources/sprite/conpher_1x/list_on.png differ diff --git a/resources/sprite/conpher_1x/more_off.png b/resources/sprite/conpher_1x/more_off.png new file mode 100644 index 0000000000..bd65726e00 Binary files /dev/null and b/resources/sprite/conpher_1x/more_off.png differ diff --git a/resources/sprite/conpher_1x/more_on.png b/resources/sprite/conpher_1x/more_on.png new file mode 100644 index 0000000000..d6c1c6b12a Binary files /dev/null and b/resources/sprite/conpher_1x/more_on.png differ diff --git a/resources/sprite/conpher_1x/people_off.png b/resources/sprite/conpher_1x/people_off.png new file mode 100644 index 0000000000..372f5ecd05 Binary files /dev/null and b/resources/sprite/conpher_1x/people_off.png differ diff --git a/resources/sprite/conpher_1x/people_on.png b/resources/sprite/conpher_1x/people_on.png new file mode 100644 index 0000000000..87fd885951 Binary files /dev/null and b/resources/sprite/conpher_1x/people_on.png differ diff --git a/resources/sprite/conpher_1x/settings_off.png b/resources/sprite/conpher_1x/settings_off.png new file mode 100644 index 0000000000..d75b0f1fb7 Binary files /dev/null and b/resources/sprite/conpher_1x/settings_off.png differ diff --git a/resources/sprite/conpher_1x/settings_on.png b/resources/sprite/conpher_1x/settings_on.png new file mode 100644 index 0000000000..f90ca7820d Binary files /dev/null and b/resources/sprite/conpher_1x/settings_on.png differ diff --git a/resources/sprite/conpher_2x/calendar_off.png b/resources/sprite/conpher_2x/calendar_off.png new file mode 100644 index 0000000000..b807c6457e Binary files /dev/null and b/resources/sprite/conpher_2x/calendar_off.png differ diff --git a/resources/sprite/conpher_2x/calendar_on.png b/resources/sprite/conpher_2x/calendar_on.png new file mode 100644 index 0000000000..b60e94dc02 Binary files /dev/null and b/resources/sprite/conpher_2x/calendar_on.png differ diff --git a/resources/sprite/conpher_2x/conversation_off.png b/resources/sprite/conpher_2x/conversation_off.png new file mode 100644 index 0000000000..5c50e5ccfd Binary files /dev/null and b/resources/sprite/conpher_2x/conversation_off.png differ diff --git a/resources/sprite/conpher_2x/conversation_on.png b/resources/sprite/conpher_2x/conversation_on.png new file mode 100644 index 0000000000..8d1bf1435d Binary files /dev/null and b/resources/sprite/conpher_2x/conversation_on.png differ diff --git a/resources/sprite/conpher_2x/files_off.png b/resources/sprite/conpher_2x/files_off.png new file mode 100644 index 0000000000..b593b1437f Binary files /dev/null and b/resources/sprite/conpher_2x/files_off.png differ diff --git a/resources/sprite/conpher_2x/files_on.png b/resources/sprite/conpher_2x/files_on.png new file mode 100644 index 0000000000..a31547d8d9 Binary files /dev/null and b/resources/sprite/conpher_2x/files_on.png differ diff --git a/resources/sprite/conpher_2x/list_off.png b/resources/sprite/conpher_2x/list_off.png new file mode 100644 index 0000000000..a9add168df Binary files /dev/null and b/resources/sprite/conpher_2x/list_off.png differ diff --git a/resources/sprite/conpher_2x/list_on.png b/resources/sprite/conpher_2x/list_on.png new file mode 100644 index 0000000000..0214b6b0ca Binary files /dev/null and b/resources/sprite/conpher_2x/list_on.png differ diff --git a/resources/sprite/conpher_2x/more_off.png b/resources/sprite/conpher_2x/more_off.png new file mode 100644 index 0000000000..ce90ebc722 Binary files /dev/null and b/resources/sprite/conpher_2x/more_off.png differ diff --git a/resources/sprite/conpher_2x/more_on.png b/resources/sprite/conpher_2x/more_on.png new file mode 100644 index 0000000000..9b2b0f4321 Binary files /dev/null and b/resources/sprite/conpher_2x/more_on.png differ diff --git a/resources/sprite/conpher_2x/people_off.png b/resources/sprite/conpher_2x/people_off.png new file mode 100644 index 0000000000..ad51a2427d Binary files /dev/null and b/resources/sprite/conpher_2x/people_off.png differ diff --git a/resources/sprite/conpher_2x/people_on.png b/resources/sprite/conpher_2x/people_on.png new file mode 100644 index 0000000000..ff42ad8f95 Binary files /dev/null and b/resources/sprite/conpher_2x/people_on.png differ diff --git a/resources/sprite/conpher_2x/settings_off.png b/resources/sprite/conpher_2x/settings_off.png new file mode 100644 index 0000000000..c25d7905ac Binary files /dev/null and b/resources/sprite/conpher_2x/settings_off.png differ diff --git a/resources/sprite/conpher_2x/settings_on.png b/resources/sprite/conpher_2x/settings_on.png new file mode 100644 index 0000000000..2aa6501876 Binary files /dev/null and b/resources/sprite/conpher_2x/settings_on.png differ diff --git a/resources/sprite/manifest/conph.json b/resources/sprite/manifest/conph.json new file mode 100644 index 0000000000..9554b39997 --- /dev/null +++ b/resources/sprite/manifest/conph.json @@ -0,0 +1,81 @@ +{ + "version" : 1, + "sprites" : { + "conpher_calendar_off" : { + "name" : "conpher_calendar_off", + "rule" : ".conpher_calendar_off", + "hash" : "a8228ab90fd90f4c2500d9285179bf26" + }, + "conpher_calendar_on" : { + "name" : "conpher_calendar_on", + "rule" : ".conpher_calendar_on, .device-desktop .conpher_calendar_off:hover ", + "hash" : "931243bc3c414782ddb2d1d9607908ba" + }, + "conpher_conversation_off" : { + "name" : "conpher_conversation_off", + "rule" : ".conpher_conversation_off", + "hash" : "931abb0377898297a9a603a6d280b977" + }, + "conpher_conversation_on" : { + "name" : "conpher_conversation_on", + "rule" : ".conpher_conversation_on, .device-desktop .conpher_conversation_off:hover ", + "hash" : "0b8a39dc5019dac1975b7be2ca0ccbfa" + }, + "conpher_files_off" : { + "name" : "conpher_files_off", + "rule" : ".conpher_files_off", + "hash" : "de1aee01b9b47b354e6ac280ae68bae1" + }, + "conpher_files_on" : { + "name" : "conpher_files_on", + "rule" : ".conpher_files_on, .device-desktop .conpher_files_off:hover ", + "hash" : "9ccbbd5e86fd4ec87a11aee0c9ec8c60" + }, + "conpher_list_off" : { + "name" : "conpher_list_off", + "rule" : ".conpher_list_off", + "hash" : "2611311d0c2aec04416433be74d3a30e" + }, + "conpher_list_on" : { + "name" : "conpher_list_on", + "rule" : ".conpher_list_on, .device-desktop .conpher_list_off:hover ", + "hash" : "cee6de0301c84b0d195282642642afa0" + }, + "conpher_more_off" : { + "name" : "conpher_more_off", + "rule" : ".conpher_more_off", + "hash" : "3b7099bdde20a13864b48552b11e92c3" + }, + "conpher_more_on" : { + "name" : "conpher_more_on", + "rule" : ".conpher_more_on, .device-desktop .conpher_more_off:hover ", + "hash" : "b146f0cff9c2e5f0b57f7ebcfe0704d3" + }, + "conpher_people_off" : { + "name" : "conpher_people_off", + "rule" : ".conpher_people_off", + "hash" : "641a6a21aa32a12416e85caf8a22e340" + }, + "conpher_people_on" : { + "name" : "conpher_people_on", + "rule" : ".conpher_people_on, .device-desktop .conpher_people_off:hover ", + "hash" : "f13745fd7036564eefb1c0ebc3502a92" + }, + "conpher_settings_off" : { + "name" : "conpher_settings_off", + "rule" : ".conpher_settings_off", + "hash" : "aa9ab000d9e33e3c50c2fe70367f30b4" + }, + "conpher_settings_on" : { + "name" : "conpher_settings_on", + "rule" : ".conpher_settings_on, .device-desktop .conpher_settings_off:hover ", + "hash" : "a5fe22965997f9559800ca7db5ea32c8" + } + }, + "scales" : [ + 1, + 2 + ], + "header" : "\/**\n * @provides sprite-conpher-css\n * @generated\n *\/\n\n.sprite-conpher {\n background-image: url(\/rsrc\/image\/sprite-conpher.png);\n background-repeat: no-repeat;\n}\n\n@media\nonly screen and (min-device-pixel-ratio: 1.5),\nonly screen and (-webkit-min-device-pixel-ratio: 1.5) {\n .sprite-conpher {\n background-image: url(\/rsrc\/image\/sprite-conpher-X2.png);\n background-size: {X}px {Y}px;\n }\n}\n", + "type" : "standard" +} diff --git a/scripts/celerity/generate_sprites.php b/scripts/celerity/generate_sprites.php index 1a337e2c63..1ac4d0fc2c 100755 --- a/scripts/celerity/generate_sprites.php +++ b/scripts/celerity/generate_sprites.php @@ -30,6 +30,7 @@ $sheets = array( 'icon' => $generator->buildIconSheet(), 'menu' => $generator->buildMenuSheet(), 'apps' => $generator->buildAppsSheet(), + 'conph' => $generator->buildConpherenceSheet(), 'apps-large' => $generator->buildAppsLargeSheet(), // TODO: @chad: should we actually remove this? // 'apps-xlarge' => $generator->buildAppsXLargeSheet(), diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index da47577120..e6b8caacdf 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -483,6 +483,20 @@ celerity_register_resource_map(array( 'disk' => '/rsrc/image/sprite-apps.png', 'type' => 'png', ), + '/rsrc/image/sprite-conph-X2.png' => + array( + 'hash' => '8f79b9888577dab95d6019d1b7f2a204', + 'uri' => '/res/8f79b988/rsrc/image/sprite-conph-X2.png', + 'disk' => '/rsrc/image/sprite-conph-X2.png', + 'type' => 'png', + ), + '/rsrc/image/sprite-conph.png' => + array( + 'hash' => 'a6329b4baa648b57c00de65f6758cbd2', + 'uri' => '/res/a6329b4b/rsrc/image/sprite-conph.png', + 'disk' => '/rsrc/image/sprite-conph.png', + 'type' => 'png', + ), '/rsrc/image/sprite-gradient.png' => array( 'hash' => '92aebaab67dcc6baf2ea99294368d895', @@ -677,7 +691,7 @@ celerity_register_resource_map(array( ), 'aphront-panel-view-css' => array( - 'uri' => '/res/789ff5e5/rsrc/css/aphront/panel-view.css', + 'uri' => '/res/3d1420b3/rsrc/css/aphront/panel-view.css', 'type' => 'css', 'requires' => array( @@ -804,7 +818,7 @@ celerity_register_resource_map(array( ), 'differential-inline-comment-editor' => array( - 'uri' => '/res/1bc649b7/rsrc/js/application/differential/DifferentialInlineCommentEditor.js', + 'uri' => '/res/e0ad34ac/rsrc/js/application/differential/DifferentialInlineCommentEditor.js', 'type' => 'js', 'requires' => array( @@ -994,7 +1008,7 @@ celerity_register_resource_map(array( ), 'javelin-behavior-aphlict-dropdown' => array( - 'uri' => '/res/f09bc90d/rsrc/js/application/aphlict/behavior-aphlict-dropdown.js', + 'uri' => '/res/2418f448/rsrc/js/application/aphlict/behavior-aphlict-dropdown.js', 'type' => 'js', 'requires' => array( @@ -1064,7 +1078,7 @@ celerity_register_resource_map(array( ), 'javelin-behavior-aphront-form-disable-on-submit' => array( - 'uri' => '/res/70fd43fd/rsrc/js/application/core/behavior-form.js', + 'uri' => '/res/b5052cd0/rsrc/js/application/core/behavior-form.js', 'type' => 'js', 'requires' => array( @@ -1791,7 +1805,7 @@ celerity_register_resource_map(array( ), 'javelin-behavior-phabricator-transaction-list' => array( - 'uri' => '/res/307a71af/rsrc/js/application/transactions/behavior-transaction-list.js', + 'uri' => '/res/f1fbb474/rsrc/js/application/transactions/behavior-transaction-list.js', 'type' => 'js', 'requires' => array( @@ -1829,6 +1843,17 @@ celerity_register_resource_map(array( ), 'disk' => '/rsrc/js/application/phame/phame-post-preview.js', ), + 'javelin-behavior-pholio-mock-view' => + array( + 'uri' => '/res/10fbdca1/rsrc/js/application/pholio/behavior-pholio-mock-view.js', + 'type' => 'js', + 'requires' => + array( + 0 => 'javelin-behavior', + 1 => 'javelin-stratcom', + ), + 'disk' => '/rsrc/js/application/pholio/behavior-pholio-mock-view.js', + ), 'javelin-behavior-phriction-document-preview' => array( 'uri' => '/res/f1665ecd/rsrc/js/application/phriction/phriction-document-preview.js', @@ -1947,7 +1972,7 @@ celerity_register_resource_map(array( ), 'javelin-behavior-workflow' => array( - 'uri' => '/res/2b0e2754/rsrc/js/application/core/behavior-workflow.js', + 'uri' => '/res/2c99beaf/rsrc/js/application/core/behavior-workflow.js', 'type' => 'js', 'requires' => array( @@ -2708,7 +2733,7 @@ celerity_register_resource_map(array( ), 'phabricator-main-menu-view' => array( - 'uri' => '/res/38ec42d4/rsrc/css/application/base/main-menu-view.css', + 'uri' => '/res/597e394b/rsrc/css/application/base/main-menu-view.css', 'type' => 'css', 'requires' => array( @@ -2728,7 +2753,7 @@ celerity_register_resource_map(array( ), 'phabricator-nav-view-css' => array( - 'uri' => '/res/df20ec17/rsrc/css/aphront/phabricator-nav-view.css', + 'uri' => '/res/f3c78a53/rsrc/css/aphront/phabricator-nav-view.css', 'type' => 'css', 'requires' => array( @@ -2760,7 +2785,7 @@ celerity_register_resource_map(array( ), 'phabricator-notification-menu-css' => array( - 'uri' => '/res/d0d0264c/rsrc/css/application/base/notification-menu.css', + 'uri' => '/res/b7cc25af/rsrc/css/application/base/notification-menu.css', 'type' => 'css', 'requires' => array( @@ -2905,7 +2930,7 @@ celerity_register_resource_map(array( ), 'phabricator-side-menu-view-css' => array( - 'uri' => '/res/2013e94f/rsrc/css/layout/phabricator-side-menu-view.css', + 'uri' => '/res/28a1e092/rsrc/css/layout/phabricator-side-menu-view.css', 'type' => 'css', 'requires' => array( @@ -2982,7 +3007,7 @@ celerity_register_resource_map(array( ), 'phabricator-transaction-view-css' => array( - 'uri' => '/res/d3599152/rsrc/css/aphront/transaction.css', + 'uri' => '/res/4c5c16dc/rsrc/css/aphront/transaction.css', 'type' => 'css', 'requires' => array( @@ -3160,7 +3185,7 @@ celerity_register_resource_map(array( ), 'pholio-css' => array( - 'uri' => '/res/e454c33f/rsrc/css/application/pholio/pholio.css', + 'uri' => '/res/f101ad7c/rsrc/css/application/pholio/pholio.css', 'type' => 'css', 'requires' => array( @@ -3284,6 +3309,15 @@ celerity_register_resource_map(array( ), 'disk' => '/rsrc/css/sprite-apps-xlarge.css', ), + 'sprite-conpher-css' => + array( + 'uri' => '/res/f640f0c5/rsrc/css/sprite-conph.css', + 'type' => 'css', + 'requires' => + array( + ), + 'disk' => '/rsrc/css/sprite-conph.css', + ), 'sprite-gradient-css' => array( 'uri' => '/res/e62e7a0f/rsrc/css/sprite-gradient.css', @@ -3341,7 +3375,7 @@ celerity_register_resource_map(array( ), array( 'packages' => array( - '023adc14' => + '0cb71c48' => array( 'name' => 'core.pkg.css', 'symbols' => @@ -3385,10 +3419,10 @@ celerity_register_resource_map(array( 36 => 'phabricator-object-item-list-view-css', 37 => 'global-drag-and-drop-css', ), - 'uri' => '/res/pkg/023adc14/core.pkg.css', + 'uri' => '/res/pkg/0cb71c48/core.pkg.css', 'type' => 'css', ), - 'b3c1b6e7' => + 'ff199687' => array( 'name' => 'core.pkg.js', 'symbols' => @@ -3427,7 +3461,7 @@ celerity_register_resource_map(array( 31 => 'javelin-behavior-global-drag-and-drop', 32 => 'javelin-behavior-phabricator-home-reveal-tiles', ), - 'uri' => '/res/pkg/b3c1b6e7/core.pkg.js', + 'uri' => '/res/pkg/ff199687/core.pkg.js', 'type' => 'js', ), '032118cf' => @@ -3464,7 +3498,7 @@ celerity_register_resource_map(array( 'uri' => '/res/pkg/ec01d039/differential.pkg.css', 'type' => 'css', ), - '310cd201' => + '9dae5f20' => array( 'name' => 'differential.pkg.js', 'symbols' => @@ -3489,7 +3523,7 @@ celerity_register_resource_map(array( 17 => 'javelin-behavior-differential-toggle-files', 18 => 'javelin-behavior-differential-user-select', ), - 'uri' => '/res/pkg/310cd201/differential.pkg.js', + 'uri' => '/res/pkg/9dae5f20/differential.pkg.js', 'type' => 'js', ), 'c8ce2d88' => @@ -3574,22 +3608,22 @@ celerity_register_resource_map(array( 'reverse' => array( 'aphront-attached-file-view-css' => 'e30a3fa8', - 'aphront-crumbs-view-css' => '023adc14', - 'aphront-dialog-view-css' => '023adc14', - 'aphront-error-view-css' => '023adc14', - 'aphront-form-view-css' => '023adc14', + 'aphront-crumbs-view-css' => '0cb71c48', + 'aphront-dialog-view-css' => '0cb71c48', + 'aphront-error-view-css' => '0cb71c48', + 'aphront-form-view-css' => '0cb71c48', 'aphront-headsup-action-list-view-css' => 'ec01d039', - 'aphront-headsup-view-css' => '023adc14', - 'aphront-list-filter-view-css' => '023adc14', - 'aphront-pager-view-css' => '023adc14', - 'aphront-panel-view-css' => '023adc14', - 'aphront-table-view-css' => '023adc14', - 'aphront-tokenizer-control-css' => '023adc14', - 'aphront-tooltip-css' => '023adc14', - 'aphront-typeahead-control-css' => '023adc14', + 'aphront-headsup-view-css' => '0cb71c48', + 'aphront-list-filter-view-css' => '0cb71c48', + 'aphront-pager-view-css' => '0cb71c48', + 'aphront-panel-view-css' => '0cb71c48', + 'aphront-table-view-css' => '0cb71c48', + 'aphront-tokenizer-control-css' => '0cb71c48', + 'aphront-tooltip-css' => '0cb71c48', + 'aphront-typeahead-control-css' => '0cb71c48', 'differential-changeset-view-css' => 'ec01d039', 'differential-core-view-css' => 'ec01d039', - 'differential-inline-comment-editor' => '310cd201', + 'differential-inline-comment-editor' => '9dae5f20', 'differential-local-commits-view-css' => 'ec01d039', 'differential-results-table-css' => 'ec01d039', 'differential-revision-add-comment-css' => 'ec01d039', @@ -3600,57 +3634,57 @@ celerity_register_resource_map(array( 'differential-table-of-contents-css' => 'ec01d039', 'diffusion-commit-view-css' => 'c8ce2d88', 'diffusion-icons-css' => 'c8ce2d88', - 'global-drag-and-drop-css' => '023adc14', + 'global-drag-and-drop-css' => '0cb71c48', 'inline-comment-summary-css' => 'ec01d039', - 'javelin-aphlict' => 'b3c1b6e7', + 'javelin-aphlict' => 'ff199687', 'javelin-behavior' => '1c6f020b', - 'javelin-behavior-aphlict-dropdown' => 'b3c1b6e7', - 'javelin-behavior-aphlict-listen' => 'b3c1b6e7', - 'javelin-behavior-aphront-basic-tokenizer' => 'b3c1b6e7', - 'javelin-behavior-aphront-drag-and-drop' => '310cd201', - 'javelin-behavior-aphront-drag-and-drop-textarea' => '310cd201', - 'javelin-behavior-aphront-form-disable-on-submit' => 'b3c1b6e7', + 'javelin-behavior-aphlict-dropdown' => 'ff199687', + 'javelin-behavior-aphlict-listen' => 'ff199687', + 'javelin-behavior-aphront-basic-tokenizer' => 'ff199687', + 'javelin-behavior-aphront-drag-and-drop' => '9dae5f20', + 'javelin-behavior-aphront-drag-and-drop-textarea' => '9dae5f20', + 'javelin-behavior-aphront-form-disable-on-submit' => 'ff199687', 'javelin-behavior-audit-preview' => 'f96657b8', 'javelin-behavior-dark-console' => '032118cf', - 'javelin-behavior-device' => 'b3c1b6e7', - 'javelin-behavior-differential-accept-with-errors' => '310cd201', - 'javelin-behavior-differential-add-reviewers-and-ccs' => '310cd201', - 'javelin-behavior-differential-comment-jump' => '310cd201', - 'javelin-behavior-differential-diff-radios' => '310cd201', - 'javelin-behavior-differential-dropdown-menus' => '310cd201', - 'javelin-behavior-differential-edit-inline-comments' => '310cd201', - 'javelin-behavior-differential-feedback-preview' => '310cd201', - 'javelin-behavior-differential-keyboard-navigation' => '310cd201', - 'javelin-behavior-differential-populate' => '310cd201', - 'javelin-behavior-differential-show-more' => '310cd201', - 'javelin-behavior-differential-toggle-files' => '310cd201', - 'javelin-behavior-differential-user-select' => '310cd201', + 'javelin-behavior-device' => 'ff199687', + 'javelin-behavior-differential-accept-with-errors' => '9dae5f20', + 'javelin-behavior-differential-add-reviewers-and-ccs' => '9dae5f20', + 'javelin-behavior-differential-comment-jump' => '9dae5f20', + 'javelin-behavior-differential-diff-radios' => '9dae5f20', + 'javelin-behavior-differential-dropdown-menus' => '9dae5f20', + 'javelin-behavior-differential-edit-inline-comments' => '9dae5f20', + 'javelin-behavior-differential-feedback-preview' => '9dae5f20', + 'javelin-behavior-differential-keyboard-navigation' => '9dae5f20', + 'javelin-behavior-differential-populate' => '9dae5f20', + 'javelin-behavior-differential-show-more' => '9dae5f20', + 'javelin-behavior-differential-toggle-files' => '9dae5f20', + 'javelin-behavior-differential-user-select' => '9dae5f20', 'javelin-behavior-diffusion-commit-graph' => 'f96657b8', 'javelin-behavior-diffusion-pull-lastmodified' => 'f96657b8', 'javelin-behavior-error-log' => '032118cf', - 'javelin-behavior-global-drag-and-drop' => 'b3c1b6e7', - 'javelin-behavior-konami' => 'b3c1b6e7', - 'javelin-behavior-lightbox-attachments' => 'b3c1b6e7', + 'javelin-behavior-global-drag-and-drop' => 'ff199687', + 'javelin-behavior-konami' => 'ff199687', + 'javelin-behavior-lightbox-attachments' => 'ff199687', 'javelin-behavior-maniphest-batch-selector' => '7707de41', 'javelin-behavior-maniphest-subpriority-editor' => '7707de41', 'javelin-behavior-maniphest-transaction-controls' => '7707de41', 'javelin-behavior-maniphest-transaction-expand' => '7707de41', 'javelin-behavior-maniphest-transaction-preview' => '7707de41', - 'javelin-behavior-phabricator-active-nav' => 'b3c1b6e7', - 'javelin-behavior-phabricator-autofocus' => 'b3c1b6e7', - 'javelin-behavior-phabricator-home-reveal-tiles' => 'b3c1b6e7', - 'javelin-behavior-phabricator-keyboard-shortcuts' => 'b3c1b6e7', - 'javelin-behavior-phabricator-nav' => 'b3c1b6e7', - 'javelin-behavior-phabricator-object-selector' => '310cd201', - 'javelin-behavior-phabricator-oncopy' => 'b3c1b6e7', - 'javelin-behavior-phabricator-remarkup-assist' => 'b3c1b6e7', - 'javelin-behavior-phabricator-search-typeahead' => 'b3c1b6e7', - 'javelin-behavior-phabricator-tooltips' => 'b3c1b6e7', - 'javelin-behavior-phabricator-watch-anchor' => 'b3c1b6e7', - 'javelin-behavior-refresh-csrf' => 'b3c1b6e7', - 'javelin-behavior-repository-crossreference' => '310cd201', - 'javelin-behavior-toggle-class' => 'b3c1b6e7', - 'javelin-behavior-workflow' => 'b3c1b6e7', + 'javelin-behavior-phabricator-active-nav' => 'ff199687', + 'javelin-behavior-phabricator-autofocus' => 'ff199687', + 'javelin-behavior-phabricator-home-reveal-tiles' => 'ff199687', + 'javelin-behavior-phabricator-keyboard-shortcuts' => 'ff199687', + 'javelin-behavior-phabricator-nav' => 'ff199687', + 'javelin-behavior-phabricator-object-selector' => '9dae5f20', + 'javelin-behavior-phabricator-oncopy' => 'ff199687', + 'javelin-behavior-phabricator-remarkup-assist' => 'ff199687', + 'javelin-behavior-phabricator-search-typeahead' => 'ff199687', + 'javelin-behavior-phabricator-tooltips' => 'ff199687', + 'javelin-behavior-phabricator-watch-anchor' => 'ff199687', + 'javelin-behavior-refresh-csrf' => 'ff199687', + 'javelin-behavior-repository-crossreference' => '9dae5f20', + 'javelin-behavior-toggle-class' => 'ff199687', + 'javelin-behavior-workflow' => 'ff199687', 'javelin-dom' => '1c6f020b', 'javelin-event' => '1c6f020b', 'javelin-install' => '1c6f020b', @@ -3669,48 +3703,48 @@ celerity_register_resource_map(array( 'javelin-util' => '1c6f020b', 'javelin-vector' => '1c6f020b', 'javelin-workflow' => '1c6f020b', - 'lightbox-attachment-css' => '023adc14', + 'lightbox-attachment-css' => '0cb71c48', 'maniphest-task-summary-css' => 'e30a3fa8', 'maniphest-transaction-detail-css' => 'e30a3fa8', - 'phabricator-busy' => 'b3c1b6e7', + 'phabricator-busy' => 'ff199687', 'phabricator-content-source-view-css' => 'ec01d039', - 'phabricator-core-buttons-css' => '023adc14', - 'phabricator-core-css' => '023adc14', - 'phabricator-crumbs-view-css' => '023adc14', - 'phabricator-directory-css' => '023adc14', - 'phabricator-drag-and-drop-file-upload' => '310cd201', - 'phabricator-dropdown-menu' => 'b3c1b6e7', - 'phabricator-file-upload' => 'b3c1b6e7', - 'phabricator-filetree-view-css' => '023adc14', - 'phabricator-flag-css' => '023adc14', - 'phabricator-form-view-css' => '023adc14', - 'phabricator-header-view-css' => '023adc14', - 'phabricator-jump-nav' => '023adc14', - 'phabricator-keyboard-shortcut' => 'b3c1b6e7', - 'phabricator-keyboard-shortcut-manager' => 'b3c1b6e7', - 'phabricator-main-menu-view' => '023adc14', - 'phabricator-menu-item' => 'b3c1b6e7', - 'phabricator-nav-view-css' => '023adc14', - 'phabricator-notification' => 'b3c1b6e7', - 'phabricator-notification-css' => '023adc14', - 'phabricator-notification-menu-css' => '023adc14', - 'phabricator-object-item-list-view-css' => '023adc14', + 'phabricator-core-buttons-css' => '0cb71c48', + 'phabricator-core-css' => '0cb71c48', + 'phabricator-crumbs-view-css' => '0cb71c48', + 'phabricator-directory-css' => '0cb71c48', + 'phabricator-drag-and-drop-file-upload' => '9dae5f20', + 'phabricator-dropdown-menu' => 'ff199687', + 'phabricator-file-upload' => 'ff199687', + 'phabricator-filetree-view-css' => '0cb71c48', + 'phabricator-flag-css' => '0cb71c48', + 'phabricator-form-view-css' => '0cb71c48', + 'phabricator-header-view-css' => '0cb71c48', + 'phabricator-jump-nav' => '0cb71c48', + 'phabricator-keyboard-shortcut' => 'ff199687', + 'phabricator-keyboard-shortcut-manager' => 'ff199687', + 'phabricator-main-menu-view' => '0cb71c48', + 'phabricator-menu-item' => 'ff199687', + 'phabricator-nav-view-css' => '0cb71c48', + 'phabricator-notification' => 'ff199687', + 'phabricator-notification-css' => '0cb71c48', + 'phabricator-notification-menu-css' => '0cb71c48', + 'phabricator-object-item-list-view-css' => '0cb71c48', 'phabricator-object-selector-css' => 'ec01d039', - 'phabricator-paste-file-upload' => 'b3c1b6e7', - 'phabricator-prefab' => 'b3c1b6e7', + 'phabricator-paste-file-upload' => 'ff199687', + 'phabricator-prefab' => 'ff199687', 'phabricator-project-tag-css' => 'e30a3fa8', - 'phabricator-remarkup-css' => '023adc14', - 'phabricator-shaped-request' => '310cd201', - 'phabricator-side-menu-view-css' => '023adc14', - 'phabricator-standard-page-view' => '023adc14', - 'phabricator-textareautils' => 'b3c1b6e7', - 'phabricator-tooltip' => 'b3c1b6e7', - 'phabricator-transaction-view-css' => '023adc14', - 'phabricator-zindex-css' => '023adc14', - 'sprite-apps-large-css' => '023adc14', - 'sprite-gradient-css' => '023adc14', - 'sprite-icon-css' => '023adc14', - 'sprite-menu-css' => '023adc14', - 'syntax-highlighting-css' => '023adc14', + 'phabricator-remarkup-css' => '0cb71c48', + 'phabricator-shaped-request' => '9dae5f20', + 'phabricator-side-menu-view-css' => '0cb71c48', + 'phabricator-standard-page-view' => '0cb71c48', + 'phabricator-textareautils' => 'ff199687', + 'phabricator-tooltip' => 'ff199687', + 'phabricator-transaction-view-css' => '0cb71c48', + 'phabricator-zindex-css' => '0cb71c48', + 'sprite-apps-large-css' => '0cb71c48', + 'sprite-gradient-css' => '0cb71c48', + 'sprite-icon-css' => '0cb71c48', + 'sprite-menu-css' => '0cb71c48', + 'syntax-highlighting-css' => '0cb71c48', ), )); diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index c5474942ed..7d83b3c810 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -674,6 +674,7 @@ phutil_register_library_map(array( 'PhabricatorApplicationTransactionView' => 'applications/transactions/view/PhabricatorApplicationTransactionView.php', 'PhabricatorApplicationTransactions' => 'applications/transactions/application/PhabricatorApplicationTransactions.php', 'PhabricatorApplicationUIExamples' => 'applications/uiexample/application/PhabricatorApplicationUIExamples.php', + 'PhabricatorApplicationUninstallController' => 'applications/meta/controller/PhabricatorApplicationUninstallController.php', 'PhabricatorApplicationsController' => 'applications/meta/controller/PhabricatorApplicationsController.php', 'PhabricatorApplicationsListController' => 'applications/meta/controller/PhabricatorApplicationsListController.php', 'PhabricatorAuditActionConstants' => 'applications/audit/constants/PhabricatorAuditActionConstants.php', @@ -1478,6 +1479,7 @@ phutil_register_library_map(array( 'celerity_get_resource_uri' => 'infrastructure/celerity/api.php', 'celerity_register_resource_map' => 'infrastructure/celerity/map.php', 'javelin_render_tag' => 'infrastructure/javelin/markup.php', + 'javelin_tag' => 'infrastructure/javelin/markup.php', 'phabricator_date' => 'view/viewutils.php', 'phabricator_datetime' => 'view/viewutils.php', 'phabricator_format_bytes' => 'view/viewutils.php', @@ -2105,6 +2107,7 @@ phutil_register_library_map(array( 'PhabricatorApplicationTransactionView' => 'AphrontView', 'PhabricatorApplicationTransactions' => 'PhabricatorApplication', 'PhabricatorApplicationUIExamples' => 'PhabricatorApplication', + 'PhabricatorApplicationUninstallController' => 'PhabricatorApplicationsController', 'PhabricatorApplicationsController' => 'PhabricatorController', 'PhabricatorApplicationsListController' => 'PhabricatorApplicationsController', 'PhabricatorAuditAddCommentController' => 'PhabricatorAuditController', diff --git a/src/applications/auth/application/PhabricatorApplicationAuth.php b/src/applications/auth/application/PhabricatorApplicationAuth.php index 18819a9410..acd98344ea 100644 --- a/src/applications/auth/application/PhabricatorApplicationAuth.php +++ b/src/applications/auth/application/PhabricatorApplicationAuth.php @@ -6,6 +6,10 @@ final class PhabricatorApplicationAuth extends PhabricatorApplication { return false; } + public function canUninstall() { + return false; + } + public function buildMainMenuItems( PhabricatorUser $user, PhabricatorController $controller = null) { diff --git a/src/applications/base/PhabricatorApplication.php b/src/applications/base/PhabricatorApplication.php index 9642a57fba..6bed54389a 100644 --- a/src/applications/base/PhabricatorApplication.php +++ b/src/applications/base/PhabricatorApplication.php @@ -62,10 +62,25 @@ abstract class PhabricatorApplication { return true; } + public function isInstalled() { + $uninstalled = + PhabricatorEnv::getEnvConfig('phabricator.uninstalled-applications'); + + if (isset($uninstalled[get_class($this)])) { + return false; + } else { + return true; + } + } + public function isBeta() { return false; } + public function canUninstall() { + return true; + } + public function getPHID() { return 'PHID-APPS-'.get_class($this); } @@ -212,6 +227,22 @@ abstract class PhabricatorApplication { /* -( Application Management )--------------------------------------------- */ + public static function getAllApplications() { + + $classes = id(new PhutilSymbolLoader()) + ->setAncestorClass(__CLASS__) + ->setConcreteOnly(true) + ->selectAndLoadSymbols(); + + $apps = array(); + + foreach ($classes as $class) { + $app = newv($class['name'], array()); + $apps[] = $app; + } + + return $apps; + } public static function getAllInstalledApplications() { static $applications; @@ -219,6 +250,11 @@ abstract class PhabricatorApplication { $show_beta = PhabricatorEnv::getEnvConfig('phabricator.show-beta-applications'); + $uninstalled = + PhabricatorEnv::getEnvConfig('phabricator.uninstalled-applications'); + + + if (empty($applications)) { $classes = id(new PhutilSymbolLoader()) ->setAncestorClass(__CLASS__) @@ -227,22 +263,29 @@ abstract class PhabricatorApplication { $apps = array(); foreach ($classes as $class) { - $app = newv($class['name'], array()); - if (!$app->isEnabled()) { - continue; - } - if (!$show_beta && $app->isBeta()) { - continue; - } - - $apps[] = $app; + if (isset($uninstalled[$class['name']])) { + continue; } + + $app = newv($class['name'], array()); + + if (!$app->isEnabled()) { + continue; + } + + if (!$show_beta && $app->isBeta()) { + continue; + } + + $apps[] = $app; + } + $applications = $apps; } return $applications; } - } + diff --git a/src/applications/config/application/PhabricatorApplicationConfig.php b/src/applications/config/application/PhabricatorApplicationConfig.php index 70c542c217..5c78cef272 100644 --- a/src/applications/config/application/PhabricatorApplicationConfig.php +++ b/src/applications/config/application/PhabricatorApplicationConfig.php @@ -18,6 +18,10 @@ final class PhabricatorApplicationConfig extends PhabricatorApplication { return self::GROUP_ADMIN; } + public function canUninstall() { + return false; + } + public function getRoutes() { return array( '/config/' => array( diff --git a/src/applications/config/check/PhabricatorSetupCheckBaseURI.php b/src/applications/config/check/PhabricatorSetupCheckBaseURI.php index 34bcfb9599..1a5671a737 100644 --- a/src/applications/config/check/PhabricatorSetupCheckBaseURI.php +++ b/src/applications/config/check/PhabricatorSetupCheckBaseURI.php @@ -4,6 +4,26 @@ final class PhabricatorSetupCheckBaseURI extends PhabricatorSetupCheck { protected function executeChecks() { $base_uri = PhabricatorEnv::getEnvConfig('phabricator.base-uri'); + + if (strpos($_SERVER['HTTP_HOST'], '.') === false) { + $summary = pht( + 'The domain does not contain a dot. This is necessary for some web '. + 'browsers to be able to set cookies.'); + + $message = pht( + 'The domain in the base URI must contain a dot ("."), e.g. '. + '"http://example.com", not just a bare name like "http://example/". '. + 'Some web browsers will not set cookies on domains with no TLD.'); + + $this + ->newIssue('config.phabricator.domain') + ->setShortName(pht('Dotless Domain')) + ->setName(pht('No Dot Character in Domain')) + ->setSummary($summary) + ->setMessage($message) + ->setIsFatal(true); + } + if ($base_uri) { return; } diff --git a/src/applications/config/option/PhabricatorCoreConfigOptions.php b/src/applications/config/option/PhabricatorCoreConfigOptions.php index c1dddac3d1..6dadb5c816 100644 --- a/src/applications/config/option/PhabricatorCoreConfigOptions.php +++ b/src/applications/config/option/PhabricatorCoreConfigOptions.php @@ -128,6 +128,11 @@ final class PhabricatorCoreConfigOptions $this->newOption('test.value', 'wild', null) ->setLocked(true) ->setDescription(pht('Unit test value.')), + $this->newOption('phabricator.uninstalled-applications', 'set', array()) + ->setLocked(true) + ->setDescription( + pht('Array containing list of Uninstalled applications.') + ), ); } diff --git a/src/applications/conpherence/controller/ConpherenceController.php b/src/applications/conpherence/controller/ConpherenceController.php index fbe8e3cd6a..6ca418661d 100644 --- a/src/applications/conpherence/controller/ConpherenceController.php +++ b/src/applications/conpherence/controller/ConpherenceController.php @@ -107,9 +107,9 @@ abstract class ConpherenceController extends PhabricatorController { $nav->addClass('conpherence-menu'); $nav->setMenuID('conpherence-menu'); - $nav->addFilter( + $nav->addButton( 'new', - pht('New Conpherence'), + pht('New Conversation'), $this->getApplicationURI('new/') ); $nav->addLabel(pht('Unread')); @@ -183,7 +183,7 @@ abstract class ConpherenceController extends PhabricatorController { $crumbs ->addAction( id(new PhabricatorMenuItemView()) - ->setName(pht('New Conpherence')) + ->setName(pht('New Conversation')) ->setHref($this->getApplicationURI('new/')) ->setIcon('create') ) diff --git a/src/applications/conpherence/controller/ConpherenceNewController.php b/src/applications/conpherence/controller/ConpherenceNewController.php index ded3b75f6f..dff27bcf9a 100644 --- a/src/applications/conpherence/controller/ConpherenceNewController.php +++ b/src/applications/conpherence/controller/ConpherenceNewController.php @@ -12,7 +12,7 @@ final class ConpherenceNewController extends ConpherenceController { $conpherence = id(new ConpherenceThread()) ->attachParticipants(array()) ->attachFilePHIDs(array()); - $title = pht('New Conpherence'); + $title = pht('New Conversation'); $participants = array(); $message = ''; $files = array(); diff --git a/src/applications/daemon/application/PhabricatorApplicationDaemons.php b/src/applications/daemon/application/PhabricatorApplicationDaemons.php index 4d02d15680..344a80ebcd 100644 --- a/src/applications/daemon/application/PhabricatorApplicationDaemons.php +++ b/src/applications/daemon/application/PhabricatorApplicationDaemons.php @@ -26,6 +26,10 @@ final class PhabricatorApplicationDaemons extends PhabricatorApplication { return self::GROUP_ADMIN; } + public function canUninstall() { + return false; + } + public function getRoutes() { return array( '/daemon/' => array( diff --git a/src/applications/differential/controller/DifferentialRevisionViewController.php b/src/applications/differential/controller/DifferentialRevisionViewController.php index 9b5edc1077..3096cfaeb4 100644 --- a/src/applications/differential/controller/DifferentialRevisionViewController.php +++ b/src/applications/differential/controller/DifferentialRevisionViewController.php @@ -176,14 +176,17 @@ final class DifferentialRevisionViewController extends DifferentialController { $limit = 100; $large = $request->getStr('large'); if (count($changesets) > $limit && !$large) { - $count = number_format(count($changesets)); + $count = count($changesets); $warning = new AphrontErrorView(); $warning->setTitle('Very Large Diff'); $warning->setSeverity(AphrontErrorView::SEVERITY_WARNING); $warning->appendChild( - '

'.pht('This diff is very large and affects %d files. Load '. - 'each file individually. ', $count). - "". + pht( + 'This diff is very large and affects %2$s files. Load each file '. + 'individually.', + $count, + PhutilTranslator::getInstance()->formatNumber($count)). + " ". phutil_tag( 'a', array( diff --git a/src/applications/differential/field/specification/DifferentialBlameRevisionFieldSpecification.php b/src/applications/differential/field/specification/DifferentialBlameRevisionFieldSpecification.php index 0c4bb8c366..11f7b5cf19 100644 --- a/src/applications/differential/field/specification/DifferentialBlameRevisionFieldSpecification.php +++ b/src/applications/differential/field/specification/DifferentialBlameRevisionFieldSpecification.php @@ -29,8 +29,9 @@ final class DifferentialBlameRevisionFieldSpecification public function renderEditControl() { return id(new AphrontFormTextControl()) - ->setLabel('Blame Revision') - ->setCaption('Revision which broke the stuff which this change fixes.') + ->setLabel(pht('Blame Revision')) + ->setCaption( + pht('Revision which broke the stuff which this change fixes.')) ->setName($this->getStorageKey()) ->setValue($this->value); } @@ -40,7 +41,7 @@ final class DifferentialBlameRevisionFieldSpecification } public function renderLabelForRevisionView() { - return 'Blame Revision:'; + return pht('Blame Revision:'); } public function renderValueForRevisionView() { diff --git a/src/applications/feed/application/PhabricatorApplicationFeed.php b/src/applications/feed/application/PhabricatorApplicationFeed.php index 2c089ac344..d1c0e0cf3e 100644 --- a/src/applications/feed/application/PhabricatorApplicationFeed.php +++ b/src/applications/feed/application/PhabricatorApplicationFeed.php @@ -14,6 +14,10 @@ final class PhabricatorApplicationFeed extends PhabricatorApplication { return 'feed'; } + public function canUninstall() { + return false; + } + public function getRoutes() { return array( '/feed/' => array( diff --git a/src/applications/files/PhabricatorImageTransformer.php b/src/applications/files/PhabricatorImageTransformer.php index 4f0f86edee..173b92b6c9 100644 --- a/src/applications/files/PhabricatorImageTransformer.php +++ b/src/applications/files/PhabricatorImageTransformer.php @@ -228,7 +228,7 @@ final class PhabricatorImageTransformer { $text_height = abs($bbox[3] - $bbox[5]); $text_width = abs($bbox[0] - $bbox[2]); return array( - "doesfit" => ($text_height * 1.05 <= imagesy($img) + "doesfit" => ($text_height * 1.05 <= imagesy($img) / 2 && $text_width * 1.05 <= imagesx($img)), "txtwidth" => $text_width, "txtheight" => $text_height, diff --git a/src/applications/files/application/PhabricatorApplicationFiles.php b/src/applications/files/application/PhabricatorApplicationFiles.php index 9c8491a4ca..2d79ac1003 100644 --- a/src/applications/files/application/PhabricatorApplicationFiles.php +++ b/src/applications/files/application/PhabricatorApplicationFiles.php @@ -30,6 +30,10 @@ final class PhabricatorApplicationFiles extends PhabricatorApplication { return $this->getBaseURI().'upload/'; } + public function canUninstall() { + return false; + } + public function getRoutes() { return array( '/F(?P[1-9]\d*)' => 'PhabricatorFileShortcutController', diff --git a/src/applications/meta/application/PhabricatorApplicationApplications.php b/src/applications/meta/application/PhabricatorApplicationApplications.php index 6139945b6f..58a50e4cbe 100644 --- a/src/applications/meta/application/PhabricatorApplicationApplications.php +++ b/src/applications/meta/application/PhabricatorApplicationApplications.php @@ -2,6 +2,10 @@ final class PhabricatorApplicationApplications extends PhabricatorApplication { + public function canUninstall() { + return false; + } + public function getBaseURI() { return '/applications/'; } @@ -28,7 +32,8 @@ final class PhabricatorApplicationApplications extends PhabricatorApplication { '' => 'PhabricatorApplicationsListController', 'view/(?P\w+)/' => 'PhabricatorApplicationDetailViewController', - + '(?P\w+)/(?Pinstall|uninstall)/' => + 'PhabricatorApplicationUninstallController', ), ); diff --git a/src/applications/meta/controller/PhabricatorApplicationDetailViewController.php b/src/applications/meta/controller/PhabricatorApplicationDetailViewController.php index db9cd66199..460dd6b0f1 100644 --- a/src/applications/meta/controller/PhabricatorApplicationDetailViewController.php +++ b/src/applications/meta/controller/PhabricatorApplicationDetailViewController.php @@ -14,12 +14,12 @@ final class PhabricatorApplicationDetailViewController $user = $request->getUser(); $selected = null; - $applications = PhabricatorApplication::getAllInstalledApplications(); + $applications = PhabricatorApplication::getAllApplications(); foreach ($applications as $application) { if (get_class($application) == $this->application) { - $selected = $application; - break; + $selected = $application; + break; } } @@ -35,27 +35,33 @@ final class PhabricatorApplicationDetailViewController ->setName(pht('Applications')) ->setHref($this->getApplicationURI())); - $properties = $this->buildPropertyView($selected); - $actions = $this->buildActionView($user); + $properties = $this->buildPropertyView($selected); + $actions = $this->buildActionView($user, $selected); - return $this->buildApplicationPage( - array( - $crumbs, - id(new PhabricatorHeaderView())->setHeader($title), - $actions, - $properties, - ), - array( - 'title' => $title, - 'device' => true, - )); + return $this->buildApplicationPage( + array( + $crumbs, + id(new PhabricatorHeaderView())->setHeader($title), + $actions, + $properties, + ), + array( + 'title' => $title, + 'device' => true, + )); } private function buildPropertyView(PhabricatorApplication $selected) { $properties = new PhabricatorPropertyListView(); - $properties->addProperty( - pht('Status'), pht('Installed')); + if ($selected->isInstalled()) { + $properties->addProperty( + pht('Status'), pht('Installed')); + + } else { + $properties->addProperty( + pht('Status'), pht('Uninstalled')); + } $properties->addProperty( pht('Description'), $selected->getShortDescription()); @@ -63,15 +69,33 @@ final class PhabricatorApplicationDetailViewController return $properties; } - private function buildActionView(PhabricatorUser $user) { + private function buildActionView( + PhabricatorUser $user, PhabricatorApplication $selected) { - return id(new PhabricatorActionListView()) - ->setUser($user) - ->addAction( - id(new PhabricatorActionView()) - ->setName(pht('Uninstall')) - ->setIcon('delete') - ); + if ($selected->canUninstall()) { + if ($selected->isInstalled()) { + + return id(new PhabricatorActionListView()) + ->setUser($user) + ->addAction( + id(new PhabricatorActionView()) + ->setName(pht('Uninstall')) + ->setIcon('delete') + ->setHref( + $this->getApplicationURI(get_class($selected).'/uninstall/')) + ); + } else { + return id(new PhabricatorActionListView()) + ->setUser($user) + ->addAction( + id(new PhabricatorActionView()) + ->setName(pht('Install')) + ->setIcon('new') + ->setHref( + $this->getApplicationURI(get_class($selected).'/install/')) + ); + } + } } } diff --git a/src/applications/meta/controller/PhabricatorApplicationUninstallController.php b/src/applications/meta/controller/PhabricatorApplicationUninstallController.php new file mode 100644 index 0000000000..7ba660d3cf --- /dev/null +++ b/src/applications/meta/controller/PhabricatorApplicationUninstallController.php @@ -0,0 +1,93 @@ +application = $data['application']; + $this->action = $data['action']; + } + + public function processRequest() { + $request = $this->getRequest(); + $user = $request->getUser(); + $app_name = substr($this->application, strlen('PhabricatorApplication')); + + if ($request->isDialogFormPost()) { + $this->manageApplication(); + return id(new AphrontRedirectResponse())->setURI('/applications/'); + } + + if ($this->action == 'install') { + + $dialog = id(new AphrontDialogView()) + ->setUser($user) + ->setTitle('Confirmation') + ->appendChild('Install '. $app_name. ' application ?') + ->addSubmitButton('Install') + ->addCancelButton('/applications/view/'.$this->application); + } else { + $dialog = id(new AphrontDialogView()) + ->setUser($user) + ->setTitle('Confirmation') + ->appendChild('Really Uninstall '. $app_name. ' application ?') + ->addSubmitButton('Uninstall') + ->addCancelButton('/applications/view/'.$this->application); + } + + return id(new AphrontDialogResponse())->setDialog($dialog); + } + + public function manageApplication() { + $key = 'phabricator.uninstalled-applications'; + + $config_entry = id(new PhabricatorConfigEntry()) + ->loadOneWhere( + 'configKey = %s AND namespace = %s', + $key, + 'default'); + + if (!$config_entry) { + $config_entry = id(new PhabricatorConfigEntry()) + ->setConfigKey($key) + ->setNamespace('default'); + } + + $list = $config_entry->getValue(); + + $uninstalled = PhabricatorEnv::getEnvConfig($key); + + if ($uninstalled[$this->application]) { + unset($list[$this->application]); + } else { + $list[$this->application] = true; + } + + $xaction = id(new PhabricatorConfigTransaction()) + ->setTransactionType(PhabricatorConfigTransaction::TYPE_EDIT) + ->setNewValue( + array( + 'deleted' => false, + 'value' => $list + )); + + $editor = id(new PhabricatorConfigEditor()) + ->setActor($this->getRequest()->getUser()) + ->setContinueOnNoEffect(true) + ->setContentSource( + PhabricatorContentSource::newForSource( + PhabricatorContentSource::SOURCE_WEB, + array( + 'ip' => $this->getRequest()->getRemoteAddr(), + ))); + + + $editor->applyTransactions($config_entry, array($xaction)); + + } + +} + diff --git a/src/applications/meta/controller/PhabricatorApplicationsListController.php b/src/applications/meta/controller/PhabricatorApplicationsListController.php index 0b70056699..e3d1cfb8fb 100644 --- a/src/applications/meta/controller/PhabricatorApplicationsListController.php +++ b/src/applications/meta/controller/PhabricatorApplicationsListController.php @@ -10,7 +10,7 @@ final class PhabricatorApplicationsListController $nav = $this->buildSideNavView(); $nav->selectFilter('/'); - $applications = PhabricatorApplication::getAllInstalledApplications(); + $applications = PhabricatorApplication::getAllApplications(); $list = $this->buildInstalledApplicationsList($applications); diff --git a/src/applications/metamta/application/PhabricatorApplicationMetaMTA.php b/src/applications/metamta/application/PhabricatorApplicationMetaMTA.php index 9c8acf9863..0ac2afd146 100644 --- a/src/applications/metamta/application/PhabricatorApplicationMetaMTA.php +++ b/src/applications/metamta/application/PhabricatorApplicationMetaMTA.php @@ -7,7 +7,7 @@ final class PhabricatorApplicationMetaMTA extends PhabricatorApplication { } public function getShortDescription() { - return 'View Mail Logs'; + return pht('View Mail Logs'); } public function getIconName() { @@ -22,6 +22,10 @@ final class PhabricatorApplicationMetaMTA extends PhabricatorApplication { return self::GROUP_ADMIN; } + public function canUninstall() { + return false; + } + public function getRoutes() { return array( $this->getBaseURI() => array( diff --git a/src/applications/metamta/controller/PhabricatorMetaMTAController.php b/src/applications/metamta/controller/PhabricatorMetaMTAController.php index 1978cdaa1a..48acdf86ce 100644 --- a/src/applications/metamta/controller/PhabricatorMetaMTAController.php +++ b/src/applications/metamta/controller/PhabricatorMetaMTAController.php @@ -10,14 +10,14 @@ abstract class PhabricatorMetaMTAController extends PhabricatorController { $nav = new AphrontSideNavFilterView(); $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); - $nav->addLabel('Mail Logs'); - $nav->addFilter('sent', 'Sent Mail', $this->getApplicationURI()); - $nav->addFilter('received', 'Received Mail'); + $nav->addLabel(pht('Mail Logs')); + $nav->addFilter('sent', pht('Sent Mail'), $this->getApplicationURI()); + $nav->addFilter('received', pht('Received Mail')); if ($this->getRequest()->getUser()->getIsAdmin()) { - $nav->addLabel('Diagnostics'); - $nav->addFilter('send', 'Send Test'); - $nav->addFilter('receive', 'Receive Test'); + $nav->addLabel(pht('Diagnostics')); + $nav->addFilter('send', pht('Send Test')); + $nav->addFilter('receive', pht('Receive Test')); } return $nav; diff --git a/src/applications/metamta/controller/PhabricatorMetaMTAListController.php b/src/applications/metamta/controller/PhabricatorMetaMTAListController.php index 5e419987c4..eca61ceaf9 100644 --- a/src/applications/metamta/controller/PhabricatorMetaMTAListController.php +++ b/src/applications/metamta/controller/PhabricatorMetaMTAListController.php @@ -74,19 +74,19 @@ final class PhabricatorMetaMTAListController 'class' => 'button small grey', 'href' => $this->getApplicationURI('/view/'.$mail->getID().'/'), ), - 'View'), + pht('View')), ); } $table = new AphrontTableView($rows); $table->setHeaders( array( - 'Status', - 'Retry', - 'Next', - 'Created', - 'Updated', - 'Subject', + pht('Status'), + pht('Retry'), + pht('Next'), + pht('Created'), + pht('Updated'), + pht('Subject'), '', )); $table->setColumnClasses( @@ -103,8 +103,9 @@ final class PhabricatorMetaMTAListController // Render the whole page. $panel = new AphrontPanelView(); $panel->appendChild($table); - $panel->setHeader('MetaMTA Messages'); + $panel->setHeader(pht('MetaMTA Messages')); $panel->appendChild($pager); + $panel->setNoBackground(); $nav = $this->buildSideNavView(); $nav->selectFilter('sent'); @@ -113,7 +114,8 @@ final class PhabricatorMetaMTAListController return $this->buildApplicationPage( $nav, array( - 'title' => 'Sent Mail', + 'title' => pht('Sent Mail'), + 'device' => true, )); } } diff --git a/src/applications/metamta/controller/PhabricatorMetaMTAReceiveController.php b/src/applications/metamta/controller/PhabricatorMetaMTAReceiveController.php index 2ae211e2ad..2d4591408c 100644 --- a/src/applications/metamta/controller/PhabricatorMetaMTAReceiveController.php +++ b/src/applications/metamta/controller/PhabricatorMetaMTAReceiveController.php @@ -12,7 +12,7 @@ final class PhabricatorMetaMTAReceiveController $receiver = PhabricatorMetaMTAReceivedMail::loadReceiverObject( $request->getStr('obj')); if (!$receiver) { - throw new Exception("No such task or revision!"); + throw new Exception(pht("No such task or revision!")); } $hash = PhabricatorMetaMTAReceivedMail::computeMailHash( @@ -50,25 +50,26 @@ final class PhabricatorMetaMTAReceiveController $form->setAction($this->getApplicationURI('/receive/')); $form ->appendChild( - '

This form will simulate '. - 'sending mail to an object.

') + '

'. + pht('This form will simulate sending mail to an object.'). + '

') ->appendChild( id(new AphrontFormTextControl()) - ->setLabel('To') + ->setLabel(pht('To')) ->setName('obj') - ->setCaption('e.g. D1234 or T1234')) + ->setCaption(pht('e.g. D1234 or T1234'))) ->appendChild( id(new AphrontFormTextAreaControl()) - ->setLabel('Body') + ->setLabel(pht('Body')) ->setName('body')) ->appendChild( id(new AphrontFormSubmitControl()) - ->setValue('Receive Mail')); + ->setValue(pht('Receive Mail'))); $panel = new AphrontPanelView(); - $panel->setHeader('Receive Email'); + $panel->setHeader(pht('Receive Email')); $panel->appendChild($form); - $panel->setWidth(AphrontPanelView::WIDTH_FORM); + $panel->setNoBackground(); $nav = $this->buildSideNavView(); $nav->selectFilter('receive'); @@ -77,7 +78,8 @@ final class PhabricatorMetaMTAReceiveController return $this->buildApplicationPage( $nav, array( - 'title' => 'Receive Test', + 'title' => pht('Receive Test'), + 'device' => true, )); } diff --git a/src/applications/metamta/controller/PhabricatorMetaMTAReceivedListController.php b/src/applications/metamta/controller/PhabricatorMetaMTAReceivedListController.php index 926bfb0be4..d55f4b7174 100644 --- a/src/applications/metamta/controller/PhabricatorMetaMTAReceivedListController.php +++ b/src/applications/metamta/controller/PhabricatorMetaMTAReceivedListController.php @@ -45,12 +45,12 @@ final class PhabricatorMetaMTAReceivedListController $table = new AphrontTableView($rows); $table->setHeaders( array( - 'ID', - 'Date', - 'Time', - 'Author', - 'Object', - 'Message', + pht('ID'), + pht('Date'), + pht('Time'), + pht('Author'), + pht('Object'), + pht('Message'), )); $table->setColumnClasses( array( @@ -63,9 +63,10 @@ final class PhabricatorMetaMTAReceivedListController )); $panel = new AphrontPanelView(); - $panel->setHeader('Received Mail'); + $panel->setHeader(pht('Received Mail')); $panel->appendChild($table); $panel->appendChild($pager); + $panel->setNoBackground(); $nav = $this->buildSideNavView(); $nav->selectFilter('received'); @@ -74,7 +75,8 @@ final class PhabricatorMetaMTAReceivedListController return $this->buildApplicationPage( $nav, array( - 'title' => 'Received Mail', + 'title' => pht('Received Mail'), + 'device' => true, )); } } diff --git a/src/applications/metamta/controller/PhabricatorMetaMTASendController.php b/src/applications/metamta/controller/PhabricatorMetaMTASendController.php index bf1917e430..3c1d6a6a99 100644 --- a/src/applications/metamta/controller/PhabricatorMetaMTASendController.php +++ b/src/applications/metamta/controller/PhabricatorMetaMTASendController.php @@ -42,8 +42,8 @@ final class PhabricatorMetaMTASendController } $failure_caption = - "Enter a number to simulate that many consecutive send failures before ". - "really attempting to deliver via the underlying MTA."; + pht("Enter a number to simulate that many consecutive send failures ". + "brefore really attempting to deliver via the underlying MTA."); $doclink_href = PhabricatorEnv::getDoclink( 'article/Configuring_Outbound_Email.html'); @@ -54,11 +54,12 @@ final class PhabricatorMetaMTASendController 'href' => $doclink_href, 'target' => '_blank', ), - 'Configuring Outbound Email'); + pht('Configuring Outbound Email')); $instructions = - '

This form will send a normal '. - 'email using the settings you have configured for Phabricator. For more '. - 'information, see '.$doclink.'.

'; + '

'. + pht('This form will send a normal email using the settings you have '. + 'configured for Phabricator. For more information, see %s.', $doclink). + '

'; $adapter = PhabricatorEnv::getEnvConfig('metamta.mail-adapter'); $warning = null; @@ -67,11 +68,11 @@ final class PhabricatorMetaMTASendController $warning->setTitle('Email is Disabled'); $warning->setSeverity(AphrontErrorView::SEVERITY_WARNING); $warning->appendChild( - '

This installation of Phabricator is currently set to use '. + '

'.pht('This installation of Phabricator is currently set to use '. 'PhabricatorMailImplementationTestAdapter to deliver '. 'outbound email. This completely disables outbound email! All '. 'outbound email will be thrown in a deep, dark hole until you '. - 'configure a real adapter.

'); + 'configure a real adapter.').'

'); } $phdlink_href = PhabricatorEnv::getDoclink( @@ -91,49 +92,49 @@ final class PhabricatorMetaMTASendController ->appendChild($instructions) ->appendChild( id(new AphrontFormStaticControl()) - ->setLabel('Adapter') + ->setLabel(pht('Adapter')) ->setValue($adapter)) ->appendChild( id(new AphrontFormTokenizerControl()) - ->setLabel('To') + ->setLabel(pht('To')) ->setName('to') ->setDatasource('/typeahead/common/mailable/')) ->appendChild( id(new AphrontFormTokenizerControl()) - ->setLabel('CC') + ->setLabel(pht('CC')) ->setName('cc') ->setDatasource('/typeahead/common/mailable/')) ->appendChild( id(new AphrontFormTextControl()) - ->setLabel('Subject') + ->setLabel(pht('Subject')) ->setName('subject')) ->appendChild( id(new AphrontFormTextAreaControl()) - ->setLabel('Body') + ->setLabel(pht('Body')) ->setName('body')) ->appendChild( id(new AphrontFormTextControl()) - ->setLabel('Mail Tags') + ->setLabel(pht('Mail Tags')) ->setName('mailtags') ->setCaption( - 'Example: differential-cc, differential-comment')) + pht('Example:').' differential-cc, differential-comment')) ->appendChild( id(new AphrontFormDragAndDropUploadControl()) - ->setLabel('Attach Files') + ->setLabel(pht('Attach Files')) ->setName('files') ->setActivatedClass('aphront-panel-view-drag-and-drop')) ->appendChild( id(new AphrontFormTextControl()) - ->setLabel('Simulate Failures') + ->setLabel(pht('Simulate Failures')) ->setName('failures') ->setCaption($failure_caption)) ->appendChild( id(new AphrontFormCheckboxControl()) - ->setLabel('HTML') + ->setLabel(pht('HTML')) ->addCheckbox('html', '1', 'Send as HTML email.')) ->appendChild( id(new AphrontFormCheckboxControl()) - ->setLabel('Bulk') + ->setLabel(pht('Bulk')) ->addCheckbox('bulk', '1', 'Send with bulk email headers.')) ->appendChild( id(new AphrontFormCheckboxControl()) @@ -141,18 +142,18 @@ final class PhabricatorMetaMTASendController ->addCheckbox( 'immediately', '1', - 'Send immediately. (Do not enqueue for daemons.)', + pht('Send immediately. (Do not enqueue for daemons.)'), PhabricatorEnv::getEnvConfig('metamta.send-immediately')) - ->setCaption('Daemons can be started with '.$phdlink.'.') + ->setCaption(pht('Daemons can be started with %s.', $phdlink)) ) ->appendChild( id(new AphrontFormSubmitControl()) - ->setValue('Send Mail')); + ->setValue(pht('Send Mail'))); $panel = new AphrontPanelView(); - $panel->setHeader('Send Email'); + $panel->setHeader(pht('Send Email')); $panel->appendChild($form); - $panel->setWidth(AphrontPanelView::WIDTH_FORM); + $panel->setNoBackground(); $nav = $this->buildSideNavView(); $nav->selectFilter('send'); @@ -165,7 +166,8 @@ final class PhabricatorMetaMTASendController return $this->buildApplicationPage( $nav, array( - 'title' => 'Send Test', + 'title' => pht('Send Test'), + 'device' => true, )); } diff --git a/src/applications/metamta/controller/PhabricatorMetaMTASendGridReceiveController.php b/src/applications/metamta/controller/PhabricatorMetaMTASendGridReceiveController.php index fbd86a2871..ae75b348c2 100644 --- a/src/applications/metamta/controller/PhabricatorMetaMTASendGridReceiveController.php +++ b/src/applications/metamta/controller/PhabricatorMetaMTASendGridReceiveController.php @@ -61,7 +61,7 @@ final class PhabricatorMetaMTASendGridReceiveController $received->processReceivedMail(); $response = new AphrontWebpageResponse(); - $response->setContent("Got it! Thanks, SendGrid!\n"); + $response->setContent(pht("Got it! Thanks, SendGrid!\n")); return $response; } diff --git a/src/applications/metamta/controller/PhabricatorMetaMTAViewController.php b/src/applications/metamta/controller/PhabricatorMetaMTAViewController.php index dd15b099e1..537678c381 100644 --- a/src/applications/metamta/controller/PhabricatorMetaMTAViewController.php +++ b/src/applications/metamta/controller/PhabricatorMetaMTAViewController.php @@ -26,41 +26,43 @@ final class PhabricatorMetaMTAViewController $form ->appendChild( id(new AphrontFormStaticControl()) - ->setLabel('Subject') + ->setLabel(pht('Subject')) ->setValue($mail->getSubject())) ->appendChild( id(new AphrontFormStaticControl()) - ->setLabel('Created') + ->setLabel(pht('Created')) ->setValue(phabricator_datetime($mail->getDateCreated(), $user))) ->appendChild( id(new AphrontFormStaticControl()) - ->setLabel('Status') + ->setLabel(pht('Status')) ->setValue($status)) ->appendChild( id(new AphrontFormStaticControl()) - ->setLabel('Retry Count') + ->setLabel(pht('Retry Count')) ->setValue($mail->getRetryCount())) ->appendChild( id(new AphrontFormStaticControl()) - ->setLabel('Message') + ->setLabel(pht('Message')) ->setValue($mail->getMessage())) ->appendChild( id(new AphrontFormStaticControl()) - ->setLabel('Related PHID') + ->setLabel(pht('Related PHID')) ->setValue($mail->getRelatedPHID())) ->appendChild( id(new AphrontFormSubmitControl()) - ->addCancelButton($this->getApplicationURI(), 'Done')); + ->addCancelButton($this->getApplicationURI(), pht('Done'))); $panel = new AphrontPanelView(); - $panel->setHeader('View Email'); + $panel->setHeader(pht('View Email')); $panel->appendChild($form); $panel->setWidth(AphrontPanelView::WIDTH_WIDE); + $panel->setNoBackground(); return $this->buildApplicationPage( $panel, array( - 'title' => 'View Mail', + 'title' => pht('View Mail'), + 'device' => true, )); } diff --git a/src/applications/people/application/PhabricatorApplicationPeople.php b/src/applications/people/application/PhabricatorApplicationPeople.php index b37667196b..80217a143e 100644 --- a/src/applications/people/application/PhabricatorApplicationPeople.php +++ b/src/applications/people/application/PhabricatorApplicationPeople.php @@ -26,6 +26,10 @@ final class PhabricatorApplicationPeople extends PhabricatorApplication { return self::GROUP_ADMIN; } + public function canUninstall() { + return false; + } + public function getRoutes() { return array( '/people/' => array( diff --git a/src/applications/phid/handle/PhabricatorObjectHandleData.php b/src/applications/phid/handle/PhabricatorObjectHandleData.php index 8b216e8f5a..52c1bb7d0c 100644 --- a/src/applications/phid/handle/PhabricatorObjectHandleData.php +++ b/src/applications/phid/handle/PhabricatorObjectHandleData.php @@ -26,143 +26,171 @@ final class PhabricatorObjectHandleData { } public function loadObjects() { - $types = array(); - foreach ($this->phids as $phid) { - $type = phid_get_type($phid); - $types[$type][] = $phid; - } + $types = phid_group_by_type($this->phids); $objects = array(); foreach ($types as $type => $phids) { - switch ($type) { - case PhabricatorPHIDConstants::PHID_TYPE_USER: - $user_dao = new PhabricatorUser(); - $users = $user_dao->loadAllWhere( - 'phid in (%Ls)', - $phids); - foreach ($users as $user) { - $objects[$user->getPHID()] = $user; - } - break; - case PhabricatorPHIDConstants::PHID_TYPE_CMIT: - $commit_dao = new PhabricatorRepositoryCommit(); - $commits = $commit_dao->loadAllWhere( - 'phid IN (%Ls)', - $phids); - $commit_data = array(); - if ($commits) { - $data_dao = new PhabricatorRepositoryCommitData(); - $commit_data = $data_dao->loadAllWhere( - 'commitID IN (%Ld)', - mpull($commits, 'getID')); - $commit_data = mpull($commit_data, null, 'getCommitID'); - } - foreach ($commits as $commit) { - $data = idx($commit_data, $commit->getID()); - if ($data) { - $commit->attachCommitData($data); - $objects[$commit->getPHID()] = $commit; - } else { - // If we couldn't load the commit data, just act as though we - // couldn't load the object at all so we don't load half an object. - } - } - break; - case PhabricatorPHIDConstants::PHID_TYPE_TASK: - $task_dao = new ManiphestTask(); - $tasks = $task_dao->loadAllWhere( - 'phid IN (%Ls)', - $phids); - foreach ($tasks as $task) { - $objects[$task->getPHID()] = $task; - } - break; - case PhabricatorPHIDConstants::PHID_TYPE_CONF: - $config_dao = new PhabricatorConfigEntry(); - $entries = $config_dao->loadAllWhere( - 'phid IN (%Ls)', - $phids); - foreach ($entries as $entry) { - $objects[$entry->getPHID()] = $entry; - } - break; - case PhabricatorPHIDConstants::PHID_TYPE_DREV: - $revision_dao = new DifferentialRevision(); - $revisions = $revision_dao->loadAllWhere( - 'phid IN (%Ls)', - $phids); - foreach ($revisions as $revision) { - $objects[$revision->getPHID()] = $revision; - } - break; - case PhabricatorPHIDConstants::PHID_TYPE_WIKI: - $document_dao = new PhrictionDocument(); - $documents = $document_dao->loadAllWhere( - 'phid IN (%Ls)', - $phids); - foreach ($documents as $document) { - $objects[$document->getPHID()] = $document; - } - break; - case PhabricatorPHIDConstants::PHID_TYPE_QUES: - $questions = id(new PonderQuestionQuery()) - ->setViewer($this->viewer) - ->withPHIDs($phids) - ->execute(); - foreach ($questions as $question) { - $objects[$question->getPHID()] = $question; - } - break; - case PhabricatorPHIDConstants::PHID_TYPE_MOCK: - $mocks = id(new PholioMockQuery()) - ->setViewer($this->viewer) - ->withPHIDs($phids) - ->execute(); - foreach ($mocks as $mock) { - $objects[$mock->getPHID()] = $mock; - } - break; - case PhabricatorPHIDConstants::PHID_TYPE_XACT: - $subtypes = array(); - foreach ($phids as $phid) { - $subtypes[phid_get_subtype($phid)][] = $phid; - } - $xactions = array(); - foreach ($subtypes as $subtype => $subtype_phids) { - // TODO: Do this magically. - switch ($subtype) { - case PhabricatorPHIDConstants::PHID_TYPE_MOCK: - $results = id(new PholioTransactionQuery()) - ->setViewer($this->viewer) - ->withPHIDs($subtype_phids) - ->execute(); - $xactions += mpull($results, null, 'getPHID'); - break; - case PhabricatorPHIDConstants::PHID_TYPE_MCRO: - $results = id(new PhabricatorMacroTransactionQuery()) - ->setViewer($this->viewer) - ->withPHIDs($subtype_phids) - ->execute(); - $xactions += mpull($results, null, 'getPHID'); - break; - } - } - foreach ($xactions as $xaction) { - $objects[$xaction->getPHID()] = $xaction; - } - break; - case PhabricatorPHIDConstants::PHID_TYPE_MCRO: - $macros = id(new PhabricatorFileImageMacro())->loadAllWhere( - 'phid IN (%Ls)', - $phids); - $objects += mpull($macros, null, 'getPHID'); - break; - } + $objects += $this->loadObjectsOfType($type, $phids); } return $objects; } + private function loadObjectsOfType($type, array $phids) { + switch ($type) { + + case PhabricatorPHIDConstants::PHID_TYPE_USER: + $user_dao = new PhabricatorUser(); + $users = $user_dao->loadAllWhere( + 'phid in (%Ls)', + $phids); + return mpull($users, null, 'getPHID'); + + case PhabricatorPHIDConstants::PHID_TYPE_CMIT: + $commit_dao = new PhabricatorRepositoryCommit(); + $commits = $commit_dao->putInSet(new LiskDAOSet())->loadAllWhere( + 'phid IN (%Ls)', + $phids); + return mpull($commits, null, 'getPHID'); + + case PhabricatorPHIDConstants::PHID_TYPE_TASK: + $task_dao = new ManiphestTask(); + $tasks = $task_dao->loadAllWhere( + 'phid IN (%Ls)', + $phids); + return mpull($tasks, null, 'getPHID'); + + case PhabricatorPHIDConstants::PHID_TYPE_CONF: + $config_dao = new PhabricatorConfigEntry(); + $entries = $config_dao->loadAllWhere( + 'phid IN (%Ls)', + $phids); + return mpull($entries, null, 'getPHID'); + + case PhabricatorPHIDConstants::PHID_TYPE_FILE: + $object = new PhabricatorFile(); + $files = $object->loadAllWhere('phid IN (%Ls)', $phids); + return mpull($files, null, 'getPHID'); + + case PhabricatorPHIDConstants::PHID_TYPE_PROJ: + $object = new PhabricatorProject(); + if ($this->viewer) { + $projects = id(new PhabricatorProjectQuery()) + ->setViewer($this->viewer) + ->withPHIDs($phids) + ->execute(); + } else { + $projects = $object->loadAllWhere('phid IN (%Ls)', $phids); + } + return mpull($projects, null, 'getPHID'); + + case PhabricatorPHIDConstants::PHID_TYPE_REPO: + $object = new PhabricatorRepository(); + $repositories = $object->loadAllWhere('phid in (%Ls)', $phids); + return mpull($repositories, null, 'getPHID'); + + case PhabricatorPHIDConstants::PHID_TYPE_OPKG: + $object = new PhabricatorOwnersPackage(); + $packages = $object->loadAllWhere('phid in (%Ls)', $phids); + return mpull($packages, null, 'getPHID'); + + case PhabricatorPHIDConstants::PHID_TYPE_APRJ: + $project_dao = new PhabricatorRepositoryArcanistProject(); + $projects = $project_dao->loadAllWhere( + 'phid IN (%Ls)', + $phids); + return mpull($projects, null, 'getPHID'); + + case PhabricatorPHIDConstants::PHID_TYPE_MLST: + $object = new PhabricatorMetaMTAMailingList(); + $lists = $object->loadAllWhere('phid IN (%Ls)', $phids); + return mpull($lists, null, 'getPHID'); + + case PhabricatorPHIDConstants::PHID_TYPE_DREV: + $revision_dao = new DifferentialRevision(); + $revisions = $revision_dao->loadAllWhere( + 'phid IN (%Ls)', + $phids); + return mpull($revisions, null, 'getPHID'); + + case PhabricatorPHIDConstants::PHID_TYPE_WIKI: + $document_dao = new PhrictionDocument(); + $documents = $document_dao->loadAllWhere( + 'phid IN (%Ls)', + $phids); + return mpull($documents, null, 'getPHID'); + + case PhabricatorPHIDConstants::PHID_TYPE_QUES: + $questions = id(new PonderQuestionQuery()) + ->setViewer($this->viewer) + ->withPHIDs($phids) + ->execute(); + return mpull($questions, null, 'getPHID'); + + case PhabricatorPHIDConstants::PHID_TYPE_MOCK: + $mocks = id(new PholioMockQuery()) + ->setViewer($this->viewer) + ->withPHIDs($phids) + ->execute(); + return mpull($mocks, null, 'getPHID'); + + case PhabricatorPHIDConstants::PHID_TYPE_XACT: + $subtypes = array(); + foreach ($phids as $phid) { + $subtypes[phid_get_subtype($phid)][] = $phid; + } + $xactions = array(); + foreach ($subtypes as $subtype => $subtype_phids) { + // TODO: Do this magically. + switch ($subtype) { + case PhabricatorPHIDConstants::PHID_TYPE_MOCK: + $results = id(new PholioTransactionQuery()) + ->setViewer($this->viewer) + ->withPHIDs($subtype_phids) + ->execute(); + $xactions += mpull($results, null, 'getPHID'); + break; + case PhabricatorPHIDConstants::PHID_TYPE_MCRO: + $results = id(new PhabricatorMacroTransactionQuery()) + ->setViewer($this->viewer) + ->withPHIDs($subtype_phids) + ->execute(); + $xactions += mpull($results, null, 'getPHID'); + break; + } + } + return mpull($xactions, null, 'getPHID'); + + case PhabricatorPHIDConstants::PHID_TYPE_MCRO: + $macros = id(new PhabricatorFileImageMacro())->loadAllWhere( + 'phid IN (%Ls)', + $phids); + return mpull($macros, null, 'getPHID'); + + case PhabricatorPHIDConstants::PHID_TYPE_PSTE: + $pastes = id(new PhabricatorPasteQuery()) + ->withPHIDs($phids) + ->setViewer($this->viewer) + ->execute(); + return mpull($pastes, null, 'getPHID'); + + case PhabricatorPHIDConstants::PHID_TYPE_BLOG: + $blogs = id(new PhameBlogQuery()) + ->withPHIDs($phids) + ->setViewer($this->viewer) + ->execute(); + return mpull($blogs, null, 'getPHID'); + + case PhabricatorPHIDConstants::PHID_TYPE_POST: + $posts = id(new PhamePostQuery()) + ->withPHIDs($phids) + ->setViewer($this->viewer) + ->execute(); + return mpull($posts, null, 'getPHID'); + + } + } + public function loadHandles() { $types = phid_group_by_type($this->phids); @@ -172,7 +200,10 @@ final class PhabricatorObjectHandleData { $external_loaders = PhabricatorEnv::getEnvConfig('phid.external-loaders'); foreach ($types as $type => $phids) { + $objects = $this->loadObjectsOfType($type, $phids); + switch ($type) { + case PhabricatorPHIDConstants::PHID_TYPE_MAGIC: // Black magic! foreach ($phids as $phid) { @@ -197,13 +228,9 @@ final class PhabricatorObjectHandleData { $handles[$phid] = $handle; } break; + case PhabricatorPHIDConstants::PHID_TYPE_USER: - $object = new PhabricatorUser(); - - $users = $object->loadAllWhere('phid IN (%Ls)', $phids); - $users = mpull($users, null, 'getPHID'); - - $image_phids = mpull($users, 'getProfileImagePHID'); + $image_phids = mpull($objects, 'getProfileImagePHID'); $image_phids = array_unique(array_filter($image_phids)); $images = array(); @@ -221,10 +248,10 @@ final class PhabricatorObjectHandleData { $handle = new PhabricatorObjectHandle(); $handle->setPHID($phid); $handle->setType($type); - if (empty($users[$phid])) { + if (empty($objects[$phid])) { $handle->setName('Unknown User'); } else { - $user = $users[$phid]; + $user = $objects[$phid]; $handle->setName($user->getUsername()); $handle->setURI('/p/'.$user->getUsername().'/'); $handle->setFullName( @@ -251,20 +278,16 @@ final class PhabricatorObjectHandleData { $handles[$phid] = $handle; } break; + case PhabricatorPHIDConstants::PHID_TYPE_MLST: - $object = new PhabricatorMetaMTAMailingList(); - - $lists = $object->loadAllWhere('phid IN (%Ls)', $phids); - $lists = mpull($lists, null, 'getPHID'); - foreach ($phids as $phid) { $handle = new PhabricatorObjectHandle(); $handle->setPHID($phid); $handle->setType($type); - if (empty($lists[$phid])) { + if (empty($objects[$phid])) { $handle->setName('Unknown Mailing List'); } else { - $list = $lists[$phid]; + $list = $objects[$phid]; $handle->setName($list->getName()); $handle->setURI($list->getURI()); $handle->setFullName($list->getName()); @@ -273,20 +296,16 @@ final class PhabricatorObjectHandleData { $handles[$phid] = $handle; } break; + case PhabricatorPHIDConstants::PHID_TYPE_DREV: - $object = new DifferentialRevision(); - - $revs = $object->loadAllWhere('phid in (%Ls)', $phids); - $revs = mpull($revs, null, 'getPHID'); - foreach ($phids as $phid) { $handle = new PhabricatorObjectHandle(); $handle->setPHID($phid); $handle->setType($type); - if (empty($revs[$phid])) { + if (empty($objects[$phid])) { $handle->setName('Unknown Revision'); } else { - $rev = $revs[$phid]; + $rev = $objects[$phid]; $handle->setName($rev->getTitle()); $handle->setURI('/D'.$rev->getID()); $handle->setFullName('D'.$rev->getID().': '.$rev->getTitle()); @@ -303,36 +322,28 @@ final class PhabricatorObjectHandleData { $handles[$phid] = $handle; } break; + case PhabricatorPHIDConstants::PHID_TYPE_CMIT: - $object = new PhabricatorRepositoryCommit(); - - $commits = $object->loadAllWhere('phid in (%Ls)', $phids); - $commits = mpull($commits, null, 'getPHID'); - - $repository_ids = array(); - $callsigns = array(); - if ($commits) { - $repository_ids = mpull($commits, 'getRepositoryID'); - $repositories = id(new PhabricatorRepository())->loadAllWhere( - 'id in (%Ld)', array_unique($repository_ids)); - $callsigns = mpull($repositories, 'getCallsign'); - } - foreach ($phids as $phid) { $handle = new PhabricatorObjectHandle(); $handle->setPHID($phid); $handle->setType($type); - if (empty($commits[$phid]) || - !isset($callsigns[$repository_ids[$phid]])) { + $repository = null; + if (!empty($objects[$phid])) { + $repository = $objects[$phid]->loadOneRelative( + new PhabricatorRepository(), + 'id', + 'getRepositoryID'); + } + if (!$repository) { $handle->setName('Unknown Commit'); } else { - $commit = $commits[$phid]; - $callsign = $callsigns[$repository_ids[$phid]]; - $repository = $repositories[$repository_ids[$phid]]; + $commit = $objects[$phid]; + $callsign = $repository->getCallsign(); $commit_identifier = $commit->getCommitIdentifier(); // In case where the repository for the commit was deleted, - // we don't have have info about the repository anymore. + // we don't have info about the repository anymore. if ($repository) { $name = $repository->formatCommitName($commit_identifier); $handle->setName($name); @@ -348,20 +359,16 @@ final class PhabricatorObjectHandleData { $handles[$phid] = $handle; } break; + case PhabricatorPHIDConstants::PHID_TYPE_TASK: - $object = new ManiphestTask(); - - $tasks = $object->loadAllWhere('phid in (%Ls)', $phids); - $tasks = mpull($tasks, null, 'getPHID'); - foreach ($phids as $phid) { $handle = new PhabricatorObjectHandle(); $handle->setPHID($phid); $handle->setType($type); - if (empty($tasks[$phid])) { + if (empty($objects[$phid])) { $handle->setName('Unknown Task'); } else { - $task = $tasks[$phid]; + $task = $objects[$phid]; $handle->setName($task->getTitle()); $handle->setURI('/T'.$task->getID()); $handle->setFullName('T'.$task->getID().': '.$task->getTitle()); @@ -375,20 +382,16 @@ final class PhabricatorObjectHandleData { $handles[$phid] = $handle; } break; + case PhabricatorPHIDConstants::PHID_TYPE_CONF: - $object = new PhabricatorConfigEntry(); - - $entries = $object->loadAllWhere('phid in (%Ls)', $phids); - $entries = mpull($entries, null, 'getPHID'); - foreach ($phids as $phid) { $handle = new PhabricatorObjectHandle(); $handle->setPHID($phid); $handle->setType($type); - if (empty($entries[$phid])) { + if (empty($objects[$phid])) { $handle->setName('Unknown Config Entry'); } else { - $entry = $entries[$phid]; + $entry = $objects[$phid]; $handle->setName($entry->getKey()); $handle->setURI('/config/edit/'.$entry->getKey()); $handle->setFullName($entry->getKey()); @@ -397,20 +400,16 @@ final class PhabricatorObjectHandleData { $handles[$phid] = $handle; } break; + case PhabricatorPHIDConstants::PHID_TYPE_FILE: - $object = new PhabricatorFile(); - - $files = $object->loadAllWhere('phid IN (%Ls)', $phids); - $files = mpull($files, null, 'getPHID'); - foreach ($phids as $phid) { $handle = new PhabricatorObjectHandle(); $handle->setPHID($phid); $handle->setType($type); - if (empty($files[$phid])) { + if (empty($objects[$phid])) { $handle->setName('Unknown File'); } else { - $file = $files[$phid]; + $file = $objects[$phid]; $handle->setName($file->getName()); $handle->setURI($file->getBestURI()); $handle->setComplete(true); @@ -421,28 +420,16 @@ final class PhabricatorObjectHandleData { $handles[$phid] = $handle; } break; + case PhabricatorPHIDConstants::PHID_TYPE_PROJ: - $object = new PhabricatorProject(); - - if ($this->viewer) { - $projects = id(new PhabricatorProjectQuery()) - ->setViewer($this->viewer) - ->withPHIDs($phids) - ->execute(); - } else { - $projects = $object->loadAllWhere('phid IN (%Ls)', $phids); - } - - $projects = mpull($projects, null, 'getPHID'); - foreach ($phids as $phid) { $handle = new PhabricatorObjectHandle(); $handle->setPHID($phid); $handle->setType($type); - if (empty($projects[$phid])) { + if (empty($objects[$phid])) { $handle->setName('Unknown Project'); } else { - $project = $projects[$phid]; + $project = $objects[$phid]; $handle->setName($project->getName()); $handle->setURI('/project/view/'.$project->getID().'/'); $handle->setComplete(true); @@ -450,20 +437,16 @@ final class PhabricatorObjectHandleData { $handles[$phid] = $handle; } break; + case PhabricatorPHIDConstants::PHID_TYPE_REPO: - $object = new PhabricatorRepository(); - - $repositories = $object->loadAllWhere('phid in (%Ls)', $phids); - $repositories = mpull($repositories, null, 'getPHID'); - foreach ($phids as $phid) { $handle = new PhabricatorObjectHandle(); $handle->setPHID($phid); $handle->setType($type); - if (empty($repositories[$phid])) { + if (empty($objects[$phid])) { $handle->setName('Unknown Repository'); } else { - $repository = $repositories[$phid]; + $repository = $objects[$phid]; $handle->setName($repository->getCallsign()); $handle->setURI('/diffusion/'.$repository->getCallsign().'/'); $handle->setComplete(true); @@ -471,20 +454,16 @@ final class PhabricatorObjectHandleData { $handles[$phid] = $handle; } break; + case PhabricatorPHIDConstants::PHID_TYPE_OPKG: - $object = new PhabricatorOwnersPackage(); - - $packages = $object->loadAllWhere('phid in (%Ls)', $phids); - $packages = mpull($packages, null, 'getPHID'); - foreach ($phids as $phid) { $handle = new PhabricatorObjectHandle(); $handle->setPHID($phid); $handle->setType($type); - if (empty($packages[$phid])) { + if (empty($objects[$phid])) { $handle->setName('Unknown Package'); } else { - $package = $packages[$phid]; + $package = $objects[$phid]; $handle->setName($package->getName()); $handle->setURI('/owners/package/'.$package->getID().'/'); $handle->setComplete(true); @@ -492,27 +471,23 @@ final class PhabricatorObjectHandleData { $handles[$phid] = $handle; } break; - case PhabricatorPHIDConstants::PHID_TYPE_APRJ: - $project_dao = new PhabricatorRepositoryArcanistProject(); - $projects = $project_dao->loadAllWhere( - 'phid IN (%Ls)', - $phids); - $projects = mpull($projects, null, 'getPHID'); + case PhabricatorPHIDConstants::PHID_TYPE_APRJ: foreach ($phids as $phid) { $handle = new PhabricatorObjectHandle(); $handle->setPHID($phid); $handle->setType($type); - if (empty($projects[$phid])) { + if (empty($objects[$phid])) { $handle->setName('Unknown Arcanist Project'); } else { - $project = $projects[$phid]; + $project = $objects[$phid]; $handle->setName($project->getName()); $handle->setComplete(true); } $handles[$phid] = $handle; } break; + case PhabricatorPHIDConstants::PHID_TYPE_WIKI: $document_dao = new PhrictionDocument(); $content_dao = new PhrictionContent(); @@ -547,21 +522,16 @@ final class PhabricatorObjectHandleData { $handles[$phid] = $handle; } break; - case PhabricatorPHIDConstants::PHID_TYPE_QUES: - $questions = id(new PonderQuestionQuery()) - ->setViewer($this->viewer) - ->withPHIDs($phids) - ->execute(); - $questions = mpull($questions, null, 'getPHID'); + case PhabricatorPHIDConstants::PHID_TYPE_QUES: foreach ($phids as $phid) { $handle = new PhabricatorObjectHandle(); $handle->setPHID($phid); $handle->setType($type); - if (empty($questions[$phid])) { + if (empty($objects[$phid])) { $handle->setName('Unknown Ponder Question'); } else { - $question = $questions[$phid]; + $question = $objects[$phid]; $handle->setName(phutil_utf8_shorten($question->getTitle(), 60)); $handle->setURI(new PhutilURI('/Q' . $question->getID())); $handle->setComplete(true); @@ -569,21 +539,16 @@ final class PhabricatorObjectHandleData { $handles[$phid] = $handle; } break; - case PhabricatorPHIDConstants::PHID_TYPE_PSTE: - $pastes = id(new PhabricatorPasteQuery()) - ->withPHIDs($phids) - ->setViewer($this->viewer) - ->execute(); - $pastes = mpull($pastes, null, 'getPHID'); + case PhabricatorPHIDConstants::PHID_TYPE_PSTE: foreach ($phids as $phid) { $handle = new PhabricatorObjectHandle(); $handle->setPHID($phid); $handle->setType($type); - if (empty($pastes[$phid])) { + if (empty($objects[$phid])) { $handle->setName('Unknown Paste'); } else { - $paste = $pastes[$phid]; + $paste = $objects[$phid]; $handle->setName($paste->getTitle()); $handle->setFullName($paste->getFullName()); $handle->setURI('/P'.$paste->getID()); @@ -592,21 +557,16 @@ final class PhabricatorObjectHandleData { $handles[$phid] = $handle; } break; - case PhabricatorPHIDConstants::PHID_TYPE_BLOG: - $blogs = id(new PhameBlogQuery()) - ->withPHIDs($phids) - ->setViewer($this->viewer) - ->execute(); - $blogs = mpull($blogs, null, 'getPHID'); + case PhabricatorPHIDConstants::PHID_TYPE_BLOG: foreach ($phids as $phid) { $handle = new PhabricatorObjectHandle(); $handle->setPHID($phid); $handle->setType($type); - if (empty($blogs[$phid])) { + if (empty($objects[$phid])) { $handle->setName('Unknown Blog'); } else { - $blog = $blogs[$phid]; + $blog = $objects[$phid]; $handle->setName($blog->getName()); $handle->setFullName($blog->getName()); $handle->setURI('/phame/blog/view/'.$blog->getID().'/'); @@ -615,21 +575,16 @@ final class PhabricatorObjectHandleData { $handles[$phid] = $handle; } break; - case PhabricatorPHIDConstants::PHID_TYPE_POST: - $posts = id(new PhamePostQuery()) - ->withPHIDs($phids) - ->setViewer($this->viewer) - ->execute(); - $posts = mpull($posts, null, 'getPHID'); + case PhabricatorPHIDConstants::PHID_TYPE_POST: foreach ($phids as $phid) { $handle = new PhabricatorObjectHandle(); $handle->setPHID($phid); $handle->setType($type); - if (empty($posts[$phid])) { + if (empty($objects[$phid])) { $handle->setName('Unknown Post'); } else { - $post = $posts[$phid]; + $post = $objects[$phid]; $handle->setName($post->getTitle()); $handle->setFullName($post->getTitle()); $handle->setURI('/phame/post/view/'.$post->getID().'/'); @@ -638,21 +593,16 @@ final class PhabricatorObjectHandleData { $handles[$phid] = $handle; } break; - case PhabricatorPHIDConstants::PHID_TYPE_MOCK: - $mocks = id(new PholioMockQuery()) - ->withPHIDs($phids) - ->setViewer($this->viewer) - ->execute(); - $mocks = mpull($mocks, null, 'getPHID'); + case PhabricatorPHIDConstants::PHID_TYPE_MOCK: foreach ($phids as $phid) { $handle = new PhabricatorObjectHandle(); $handle->setPHID($phid); $handle->setType($type); - if (empty($mocks[$phid])) { + if (empty($objects[$phid])) { $handle->setName('Unknown Mock'); } else { - $mock = $mocks[$phid]; + $mock = $objects[$phid]; $handle->setName($mock->getName()); $handle->setFullName('M'.$mock->getID().': '.$mock->getName()); $handle->setURI('/M'.$mock->getID()); @@ -661,19 +611,16 @@ final class PhabricatorObjectHandleData { $handles[$phid] = $handle; } break; + case PhabricatorPHIDConstants::PHID_TYPE_MCRO: - $macros = id(new PhabricatorFileImageMacro())->loadAllWhere( - 'phid IN (%Ls)', - $phids); - $macros = mpull($macros, null, 'getPHID'); foreach ($phids as $phid) { $handle = new PhabricatorObjectHandle(); $handle->setPHID($phid); $handle->setType($type); - if (empty($macros[$phid])) { + if (empty($objects[$phid])) { $handle->setName('Unknown Macro'); } else { - $macro = $macros[$phid]; + $macro = $objects[$phid]; $handle->setName($macro->getName()); $handle->setFullName('Image Macro "'.$macro->getName().'"'); $handle->setURI('/macro/view/'.$macro->getID().'/'); @@ -682,6 +629,7 @@ final class PhabricatorObjectHandleData { $handles[$phid] = $handle; } break; + default: $loader = null; if (isset($external_loaders[$type])) { @@ -706,6 +654,7 @@ final class PhabricatorObjectHandleData { $handles[$phid] = $handle; } break; + } } diff --git a/src/applications/pholio/controller/PholioMockViewController.php b/src/applications/pholio/controller/PholioMockViewController.php index a03452a6d4..5e0f72c016 100644 --- a/src/applications/pholio/controller/PholioMockViewController.php +++ b/src/applications/pholio/controller/PholioMockViewController.php @@ -67,8 +67,6 @@ final class PholioMockViewController extends PholioController { $output = new PholioMockImagesView(); $output->setMock($mock); - $carousel = $output->render(); - $xaction_view = id(new PhabricatorApplicationTransactionView()) ->setUser($this->getRequest()->getUser()) ->setTransactions($xactions) @@ -80,11 +78,12 @@ final class PholioMockViewController extends PholioController { $header, $actions, $properties, - $carousel, + $output->render(), $xaction_view, $add_comment, ); + return $this->buildApplicationPage( $content, array( diff --git a/src/applications/pholio/view/PholioMockImagesView.php b/src/applications/pholio/view/PholioMockImagesView.php index 5b6a28c7fb..33d6a91fe4 100644 --- a/src/applications/pholio/view/PholioMockImagesView.php +++ b/src/applications/pholio/view/PholioMockImagesView.php @@ -13,22 +13,59 @@ final class PholioMockImagesView extends AphrontView { throw new Exception("Call setMock() before render()!"); } + $mockview = array(); + $file = head($this->mock->getImages())->getFile(); - $image_tag = phutil_tag( - 'img', - array( - 'src' => $file->getBestURI(), - 'class' => 'pholio-mock-image', - ), - ''); + $main_image_id = celerity_generate_unique_node_id(); - return phutil_tag( + $main_image_tag = phutil_tag( + 'img', + array( + 'src' => $file->getBestURI(), + 'class' => 'pholio-mock-image', + 'id' => $main_image_id, + )); + + $mockview[] = phutil_tag( 'div', array( 'class' => 'pholio-mock-image-container', ), - $image_tag); + $main_image_tag); + + if (count($this->mock->getImages()) > 1) { + require_celerity_resource('javelin-behavior-pholio-mock-view'); + $config = array('mainID' => $main_image_id); + Javelin::initBehavior('pholio-mock-view', $config); + + $thumbnails = array(); + foreach ($this->mock->getImages() as $image) { + $thumbfile = $image->getFile(); + + $tag = javelin_tag( + 'img', + array( + 'src' => $thumbfile->getThumb160x120URI(), + 'sigil' => 'mock-thumbnail', + 'class' => 'pholio-mock-carousel-thumbnail', + 'meta' => array( + 'fullSizeURI' => $thumbfile->getBestURI(), + 'imageID' => $image->getID(), + ), + )); + $thumbnails[] = $tag; + } + + $mockview[] = phutil_tag( + 'div', + array( + 'class' => 'pholio-mock-carousel', + ), + $thumbnails); + } + + return $this->renderHTMLView($mockview); } } diff --git a/src/applications/search/view/PhabricatorSearchResultView.php b/src/applications/search/view/PhabricatorSearchResultView.php index b2a1aaafa9..821853d2aa 100644 --- a/src/applications/search/view/PhabricatorSearchResultView.php +++ b/src/applications/search/view/PhabricatorSearchResultView.php @@ -57,9 +57,10 @@ final class PhabricatorSearchResultView extends AphrontView { case PhabricatorPHIDConstants::PHID_TYPE_CMIT: $object_name = $handle->getName(); if ($this->object) { - $data = $this->object->getCommitData(); - $summary = $data->getSummary(); - if (strlen($summary)) { + $data = $this->object->loadOneRelative( + new PhabricatorRepositoryCommitData(), + 'commitID'); + if ($data && strlen($data->getSummary())) { $object_name = $handle->getName().': '.$data->getSummary(); } } diff --git a/src/applications/settings/application/PhabricatorApplicationSettings.php b/src/applications/settings/application/PhabricatorApplicationSettings.php index 36abbb47c4..613ec7168a 100644 --- a/src/applications/settings/application/PhabricatorApplicationSettings.php +++ b/src/applications/settings/application/PhabricatorApplicationSettings.php @@ -14,6 +14,10 @@ final class PhabricatorApplicationSettings extends PhabricatorApplication { return 'settings'; } + public function canUninstall() { + return false; + } + public function getRoutes() { return array( '/settings/' => array( diff --git a/src/applications/subscriptions/application/PhabricatorApplicationSubscriptions.php b/src/applications/subscriptions/application/PhabricatorApplicationSubscriptions.php index 21543d4eb9..ad4b4eea15 100644 --- a/src/applications/subscriptions/application/PhabricatorApplicationSubscriptions.php +++ b/src/applications/subscriptions/application/PhabricatorApplicationSubscriptions.php @@ -6,6 +6,10 @@ final class PhabricatorApplicationSubscriptions extends PhabricatorApplication { return false; } + public function canUninstall() { + return false; + } + public function getEventListeners() { return array( new PhabricatorSubscriptionsUIEventListener(), diff --git a/src/applications/transactions/application/PhabricatorApplicationTransactions.php b/src/applications/transactions/application/PhabricatorApplicationTransactions.php index b597b394dd..ac0b4e1bc9 100644 --- a/src/applications/transactions/application/PhabricatorApplicationTransactions.php +++ b/src/applications/transactions/application/PhabricatorApplicationTransactions.php @@ -6,6 +6,10 @@ final class PhabricatorApplicationTransactions extends PhabricatorApplication { return false; } + public function canUninstall() { + return false; + } + public function getRoutes() { return array( '/transactions/' => array( diff --git a/src/docs/configuration/configuration_guide.diviner b/src/docs/configuration/configuration_guide.diviner index e8a2e5765b..b5e2c5c69d 100644 --- a/src/docs/configuration/configuration_guide.diviner +++ b/src/docs/configuration/configuration_guide.diviner @@ -30,6 +30,9 @@ some subdirectory of an existing website. Navigate to whatever domain you're going to use and make sure Apache serves you something to verify that DNS is correctly configured. +NOTE: The domain must contain a dot ('.'), i.e. not be just a bare name like +'http://example/'. Some web browsers will not set cookies otherwise. + Now, either create a VirtualHost entry (to put Phabricator on a subdomain) or edit the Directory entry for the DocumentRoot. It should look something like this: diff --git a/src/infrastructure/celerity/CeleritySpriteGenerator.php b/src/infrastructure/celerity/CeleritySpriteGenerator.php index c6b2ac23d0..4f9f4f26b7 100644 --- a/src/infrastructure/celerity/CeleritySpriteGenerator.php +++ b/src/infrastructure/celerity/CeleritySpriteGenerator.php @@ -176,6 +176,49 @@ final class CeleritySpriteGenerator { return $sheet; } + public function buildConpherenceSheet() { + $icons = $this->getDirectoryList('conpher_1x'); + $scales = array( + '1x' => 1, + '2x' => 2, + ); + $template = id(new PhutilSprite()) + ->setSourceSize(32, 32); + + $sprites = array(); + foreach ($icons as $icon) { + $color = preg_match('/_on/', $icon) ? 'on' : 'off'; + + $prefix = 'conpher_'; + + $sprite = id(clone $template) + ->setName($prefix.$icon); + + $tcss = array(); + $tcss[] = '.'.$prefix.$icon; + if ($color == 'on') { + $class = str_replace('_on', '_off', $prefix.$icon); + $tcss[] = '.device-desktop .'.$class.':hover '; + } + + $sprite->setTargetCSS(implode(', ', $tcss)); + + foreach ($scales as $scale_key => $scale) { + $path = $this->getPath($prefix.$scale_key.'/'.$icon.'.png'); + $sprite->setSourceFile($path, $scale); + } + $sprites[] = $sprite; + } + + $sheet = $this->buildSheet('conpher', true); + $sheet->setScales($scales); + foreach ($sprites as $sprite) { + $sheet->addSprite($sprite); + } + + return $sheet; + } + public function buildGradientSheet() { $gradients = $this->getDirectoryList('gradients'); diff --git a/src/view/layout/AphrontSideNavFilterView.php b/src/view/layout/AphrontSideNavFilterView.php index 6bea1c3e42..ad7cd815a8 100644 --- a/src/view/layout/AphrontSideNavFilterView.php +++ b/src/view/layout/AphrontSideNavFilterView.php @@ -83,13 +83,25 @@ final class AphrontSideNavFilterView extends AphrontView { return $this->menu; } - public function addFilter( + public function addFilter($key, $name, $uri = null) { + return $this->addThing( + $key, $name, $uri, PhabricatorMenuItemView::TYPE_LINK); + } + + public function addButton($key, $name, $uri = null) { + return $this->addThing( + $key, $name, $uri, PhabricatorMenuItemView::TYPE_BUTTON); + } + + private function addThing( $key, $name, - $uri = null) { + $uri = null, + $type) { $item = id(new PhabricatorMenuItemView()) - ->setName($name); + ->setName($name) + ->setType($type); if (strlen($key)) { $item->setKey($key); diff --git a/src/view/layout/PhabricatorMenuItemView.php b/src/view/layout/PhabricatorMenuItemView.php index 768ee2a709..ab5e21c410 100644 --- a/src/view/layout/PhabricatorMenuItemView.php +++ b/src/view/layout/PhabricatorMenuItemView.php @@ -5,6 +5,7 @@ final class PhabricatorMenuItemView extends AphrontTagView { const TYPE_LINK = 'type-link'; const TYPE_SPACER = 'type-spacer'; const TYPE_LABEL = 'type-label'; + const TYPE_BUTTON = 'type-button'; private $name; private $href; diff --git a/src/view/layout/PhabricatorMenuView.php b/src/view/layout/PhabricatorMenuView.php index 334c5e3ab0..b5e7fc8a8d 100644 --- a/src/view/layout/PhabricatorMenuView.php +++ b/src/view/layout/PhabricatorMenuView.php @@ -25,6 +25,17 @@ final class PhabricatorMenuView extends AphrontTagView { return $item; } + public function newButton($name, $href) { + $item = id(new PhabricatorMenuItemView()) + ->setType(PhabricatorMenuItemView::TYPE_BUTTON) + ->setName($name) + ->setHref($href); + + $this->addMenuItem($item); + + return $item; + } + public function addMenuItem(PhabricatorMenuItemView $item) { $key = $item->getKey(); $this->items[] = $item; diff --git a/src/view/page/menu/PhabricatorMainMenuView.php b/src/view/page/menu/PhabricatorMainMenuView.php index e56ab6bcee..45922530c9 100644 --- a/src/view/page/menu/PhabricatorMainMenuView.php +++ b/src/view/page/menu/PhabricatorMainMenuView.php @@ -393,6 +393,7 @@ final class PhabricatorMainMenuView extends AphrontView { 'bubbleID' => $bubble_id, 'countID' => $count_id, 'dropdownID' => $dropdown_id, + 'loadingText' => pht('Loading...'), )); $notification_dropdown = javelin_tag( diff --git a/webroot/rsrc/css/aphront/panel-view.css b/webroot/rsrc/css/aphront/panel-view.css index ce5d3944a3..fd6a3aaa91 100644 --- a/webroot/rsrc/css/aphront/panel-view.css +++ b/webroot/rsrc/css/aphront/panel-view.css @@ -86,6 +86,10 @@ padding: 15px 20px; } +.device-phone .aphront-panel-preview { + display: none; +} + .aphront-panel-preview-wide { width: 1080px; margin-left: auto; diff --git a/webroot/rsrc/css/aphront/phabricator-nav-view.css b/webroot/rsrc/css/aphront/phabricator-nav-view.css index a9338387fb..5c1da125a9 100644 --- a/webroot/rsrc/css/aphront/phabricator-nav-view.css +++ b/webroot/rsrc/css/aphront/phabricator-nav-view.css @@ -83,9 +83,3 @@ .device-desktop .phabricator-side-menu-home .phabricator-nav-content { margin-left: 240px; } - -/* Chrome annoyingly has a url display over the nav - if the nav is the tallest page (home) */ -.phabricator-menu-view { - margin-bottom: 40px; -} diff --git a/webroot/rsrc/css/aphront/transaction.css b/webroot/rsrc/css/aphront/transaction.css index 6eb4052f77..2c8c3618ab 100644 --- a/webroot/rsrc/css/aphront/transaction.css +++ b/webroot/rsrc/css/aphront/transaction.css @@ -9,6 +9,10 @@ padding: 2px 0px; } +.device-phone .phabricator-transaction-view { + margin: 10px 0; +} + .phabricator-transaction-detail { border-color: #dddddd; border-width: 1px 10px; @@ -18,6 +22,8 @@ .device-phone .phabricator-transaction-detail { margin: 0; + min-height: 50px; + background: #fff; } .phabricator-transaction-header { @@ -30,9 +36,13 @@ } .phabricator-transaction-info { - color: #666666; - float: right; - font-size: 11px; + color: #777; + float: right; + font-size: 11px; +} + +.device-phone .phabricator-transaction-info { + display: none; } .phabricator-transaction-content { diff --git a/webroot/rsrc/css/application/base/main-menu-view.css b/webroot/rsrc/css/application/base/main-menu-view.css index 6ce50a0004..ad3f65c090 100644 --- a/webroot/rsrc/css/application/base/main-menu-view.css +++ b/webroot/rsrc/css/application/base/main-menu-view.css @@ -42,6 +42,8 @@ .phabricator-main-menu-logo { display: inline-block; width: 139px; + height: 44px; + float: left; margin-right: 6px; padding-right: 6px; padding-left: 6px; @@ -152,6 +154,7 @@ } .device .phabricator-main-menu-search-container { + padding: 0; margin: 0 18px 0 60px; } @@ -172,6 +175,12 @@ padding: 6px 32px 6px 10px; } +.device .phabricator-main-menu-search input { + height: 16px; + font-size: 15px; + border-radius: 15px; +} + .phabricator-main-menu-search input:focus { background: #c9c9c9; } @@ -196,6 +205,11 @@ right: 6px; } +.device .phabricator-main-menu-search button { + top: 3px; +} + + .phabricator-main-menu-search-target div.jx-typeahead-results { border-radius: 4px; box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.35); @@ -253,6 +267,7 @@ a:hover .phabricator-main-search-typeahead-result .result-type { .phabricator-main-menu-alerts { display: inline-block; border-radius: 15px; + float: left; background: rgba(0,0,0,.2); height: 20px; padding: 3px 10px; @@ -299,23 +314,16 @@ a:hover .phabricator-main-search-typeahead-result .result-type { .device .phabricator-dark-menu, .device .phabricator-dark-menu a.phabricator-menu-item-type-link { color: #fff; -} -.device .phabricator-dark-menu .phabricator-menu-item-view { - display: block; - padding: 4px 0; } .device .phabricator-dark-menu .phabricator-menu-item-type-label { text-transform: uppercase; - font-size: 11px; + font-size: 12px; background-color: #151719; padding: 0 0 0 12px; - height: 24px; -} - -.device .phabricator-dark-menu .phabricator-menu-item-type-spacer { - display: none; + height: 25px; + font-weight: bold; } .device .phabricator-dark-menu .phabricator-menu-item-type-label @@ -329,6 +337,8 @@ a:hover .phabricator-main-search-typeahead-result .result-type { border-style: solid; border-color: #34373b transparent #282c2d; background-image: url(/rsrc/image/texture/dark-menu.png); + padding: 4px 0; + display: block; } @@ -358,15 +368,15 @@ a:hover .phabricator-main-search-typeahead-result .result-type { padding-top: 44px; } -.device .phabricator-core-menu .phabricator-menu-item-type-link { +.device .phabricator-dark-menu .phabricator-menu-item-type-link { font-size: 15px; min-height: 30px; + line-height: 28px; } .device .phabricator-core-menu .phabricator-menu-item-type-link .phabricator-menu-item-name { margin-left: 40px; - line-height: 28px; } .device-desktop .phabricator-core-menu { @@ -428,9 +438,3 @@ a:hover .phabricator-main-search-typeahead-result .result-type { .phabricator-menu-item-name { padding-left: 12px; } - -.device .phabricator-application-menu-expanded .phabricator-application-menu { - display: block; - padding-top: 44px; -} - diff --git a/webroot/rsrc/css/application/base/notification-menu.css b/webroot/rsrc/css/application/base/notification-menu.css index 3782456789..e3766ed4dd 100644 --- a/webroot/rsrc/css/application/base/notification-menu.css +++ b/webroot/rsrc/css/application/base/notification-menu.css @@ -9,6 +9,12 @@ overflow-y: auto; } +.phabricator-notification-menu-loading { + text-align: center; + padding: 10px 0; + color: #888888; +} + .device-desktop .phabricator-notification-menu { position: fixed; width: 360px; diff --git a/webroot/rsrc/css/application/pholio/pholio.css b/webroot/rsrc/css/application/pholio/pholio.css index 38e81f19ed..5cf10e93dc 100644 --- a/webroot/rsrc/css/application/pholio/pholio.css +++ b/webroot/rsrc/css/application/pholio/pholio.css @@ -6,7 +6,18 @@ text-align: center; } -.pholio-mock-image { - margin: 10px 0px; - display: inline-block; +.pholio-mock-carousel { + background-color: #282828; + text-align: center; +} + +.pholio-mock-carousel-thumbnail { + margin-right: 5px; + display: inline-block; + cursor: pointer; +} + +.pholio-mock-image { + margin: 10px 0px; + display: inline-block; } diff --git a/webroot/rsrc/css/layout/phabricator-side-menu-view.css b/webroot/rsrc/css/layout/phabricator-side-menu-view.css index d06a7e5609..2aa467d6ec 100644 --- a/webroot/rsrc/css/layout/phabricator-side-menu-view.css +++ b/webroot/rsrc/css/layout/phabricator-side-menu-view.css @@ -25,6 +25,25 @@ background-color: #000; } +.phabricator-dark-menu .phabricator-menu-item-type-button, +.phabricator-side-menu .phabricator-menu-item-type-button { + width: 50%; + padding: 5px 8px; + display: block; + border-radius: 4px; + border: 2px solid #000; + margin: 10px auto; + color: #fff; + font-size: 14px; + text-shadow: 0px 1px 1px #000000; + font-weight: bold; + text-align: center; +} + +.phabricator-side-menu .phabricator-menu-item-type-button:hover { + background-image: url(/rsrc/image/texture/dark-menu-hover.png); +} + .device-desktop .phabricator-side-menu a.phabricator-menu-item-type-link:hover { text-decoration: none; } diff --git a/webroot/rsrc/css/sprite-conph.css b/webroot/rsrc/css/sprite-conph.css new file mode 100644 index 0000000000..d11ae477c5 --- /dev/null +++ b/webroot/rsrc/css/sprite-conph.css @@ -0,0 +1,75 @@ +/** + * @provides sprite-conpher-css + * @generated + */ + +.sprite-conpher { + background-image: url(/rsrc/image/sprite-conpher.png); + background-repeat: no-repeat; +} + +@media +only screen and (min-device-pixel-ratio: 1.5), +only screen and (-webkit-min-device-pixel-ratio: 1.5) { + .sprite-conpher { + background-image: url(/rsrc/image/sprite-conpher-X2.png); + background-size: 132px 132px; + } +} + + +.conpher_calendar_off { + background-position: 0px 0px; +} + +.conpher_calendar_on, .device-desktop .conpher_calendar_off:hover { + background-position: -33px 0px; +} + +.conpher_conversation_off { + background-position: -66px 0px; +} + +.conpher_conversation_on, .device-desktop .conpher_conversation_off:hover { + background-position: -99px 0px; +} + +.conpher_files_off { + background-position: 0px -33px; +} + +.conpher_files_on, .device-desktop .conpher_files_off:hover { + background-position: -33px -33px; +} + +.conpher_list_off { + background-position: -66px -33px; +} + +.conpher_list_on, .device-desktop .conpher_list_off:hover { + background-position: -99px -33px; +} + +.conpher_more_off { + background-position: 0px -66px; +} + +.conpher_more_on, .device-desktop .conpher_more_off:hover { + background-position: -33px -66px; +} + +.conpher_people_off { + background-position: -66px -66px; +} + +.conpher_people_on, .device-desktop .conpher_people_off:hover { + background-position: 0px -99px; +} + +.conpher_settings_off { + background-position: -33px -99px; +} + +.conpher_settings_on, .device-desktop .conpher_settings_off:hover { + background-position: -66px -99px; +} diff --git a/webroot/rsrc/image/sprite-conph-X2.png b/webroot/rsrc/image/sprite-conph-X2.png new file mode 100644 index 0000000000..ef32a410db Binary files /dev/null and b/webroot/rsrc/image/sprite-conph-X2.png differ diff --git a/webroot/rsrc/image/sprite-conph.png b/webroot/rsrc/image/sprite-conph.png new file mode 100644 index 0000000000..490f12d2c5 Binary files /dev/null and b/webroot/rsrc/image/sprite-conph.png differ diff --git a/webroot/rsrc/js/application/aphlict/behavior-aphlict-dropdown.js b/webroot/rsrc/js/application/aphlict/behavior-aphlict-dropdown.js index f75fc3b53c..5fd59a0f5f 100644 --- a/webroot/rsrc/js/application/aphlict/behavior-aphlict-dropdown.js +++ b/webroot/rsrc/js/application/aphlict/behavior-aphlict-dropdown.js @@ -14,8 +14,17 @@ JX.behavior('aphlict-dropdown', function(config) { var bubble = JX.$(config.bubbleID); var visible = false; var request = null; + var dirty = true; function refresh() { + if (dirty) { + JX.DOM.setContent(dropdown, config.loadingText); + JX.DOM.alterClass( + dropdown, + 'phabricator-notification-menu-loading', + true); + } + if (request) { //already fetching return; } @@ -30,6 +39,11 @@ JX.behavior('aphlict-dropdown', function(config) { } else { JX.DOM.alterClass(bubble, 'alert-unread', true); } + dirty = false; + JX.DOM.alterClass( + dropdown, + 'phabricator-notification-menu-loading', + false); JX.DOM.setContent(dropdown, JX.$H(response.content)); request = null; }); @@ -74,7 +88,9 @@ JX.behavior('aphlict-dropdown', function(config) { if (visible) { JX.DOM.hide(dropdown); } else { - refresh(); + if (dirty) { + refresh(); + } var p = JX.$V(bubble); p.y = null; @@ -88,5 +104,8 @@ JX.behavior('aphlict-dropdown', function(config) { } ) - JX.Stratcom.listen('notification-panel-update', null, refresh); + JX.Stratcom.listen('notification-panel-update', null, function() { + dirty = true; + refresh(); + }); }); diff --git a/webroot/rsrc/js/application/core/behavior-form.js b/webroot/rsrc/js/application/core/behavior-form.js index 9e4942d533..310001cdb5 100644 --- a/webroot/rsrc/js/application/core/behavior-form.js +++ b/webroot/rsrc/js/application/core/behavior-form.js @@ -14,6 +14,39 @@ JX.behavior('aphront-form-disable-on-submit', function(config) { new_tab = (raw.altKey || raw.ctrlKey || raw.metaKey || raw.shiftKey); }); + + JX.Stratcom.listen('keypress', ['tag:form', 'tag:textarea'], function(e) { + var raw = e.getRawEvent(); + if (e.getSpecialKey() != 'return' || !raw.ctrlKey) { + return; + } + + e.kill(); + + var form = e.getNode('tag:form'); + + // This allows 'workflow' and similar actions to take effect. + var r = JX.DOM.invoke(form, 'didSyntheticSubmit'); + if (r.getPrevented()) { + return; + } + + // If nothing handled the synthetic submit, submit normally. + form.submit(); + }); + + function will_submit(root) { + root._disabled = true; + var buttons = JX.DOM.scry(root, 'button'); + for (var ii = 0; ii < buttons.length; ii++) { + if (!buttons[ii].disabled) { + buttons[ii].disabled = 'disabled'; + JX.DOM.alterClass(buttons[ii], 'disabled', true); + restore.push(buttons[ii]); + } + } + } + JX.Stratcom.listen('submit', 'tag:form', function(e) { if (e.getNode('workflow')) { // Don't activate for forms with workflow, the workflow behavior will @@ -40,15 +73,8 @@ JX.behavior('aphront-form-disable-on-submit', function(config) { if (root._disabled) { e.kill(); } - root._disabled = true; - var buttons = JX.DOM.scry(root, 'button'); - for (var ii = 0; ii < buttons.length; ii++) { - if (!buttons[ii].disabled) { - buttons[ii].disabled = 'disabled'; - JX.DOM.alterClass(buttons[ii], 'disabled', true); - restore.push(buttons[ii]); - } - } + + will_submit(root); }); JX.Stratcom.listen('unload', null, function(e) { diff --git a/webroot/rsrc/js/application/core/behavior-workflow.js b/webroot/rsrc/js/application/core/behavior-workflow.js index 82c0cfb0b8..c55c8e9317 100644 --- a/webroot/rsrc/js/application/core/behavior-workflow.js +++ b/webroot/rsrc/js/application/core/behavior-workflow.js @@ -7,8 +7,10 @@ */ JX.behavior('workflow', function() { + + // Listen for both real JX.Stratcom.listen( - 'submit', + ['submit', 'didSyntheticSubmit'], ['workflow', 'tag:form'], function(e) { if (JX.Stratcom.pass()) { @@ -18,6 +20,7 @@ JX.behavior('workflow', function() { e.prevent(); JX.Workflow.newFromForm(target).start(); }); + JX.Stratcom.listen( 'click', ['workflow', 'tag:a'], @@ -40,4 +43,5 @@ JX.behavior('workflow', function() { e.prevent(); JX.Workflow.newFromLink(target).start(); }); + }); diff --git a/webroot/rsrc/js/application/differential/DifferentialInlineCommentEditor.js b/webroot/rsrc/js/application/differential/DifferentialInlineCommentEditor.js index 6e544fe787..08339e1585 100644 --- a/webroot/rsrc/js/application/differential/DifferentialInlineCommentEditor.js +++ b/webroot/rsrc/js/application/differential/DifferentialInlineCommentEditor.js @@ -119,7 +119,11 @@ JX.install('DifferentialInlineCommentEditor', { JX.DOM.alterClass(drawn[0], 'differential-inline-loading', true); }); - JX.DOM.listen(drawn[0], 'submit', 'inline-edit-form', onsubmit); + JX.DOM.listen( + drawn[0], + ['submit', 'didSyntheticSubmit'], + 'inline-edit-form', + onsubmit); }, _didCompleteWorkflow : function(response) { var op = this.getOperation(); diff --git a/webroot/rsrc/js/application/pholio/behavior-pholio-mock-view.js b/webroot/rsrc/js/application/pholio/behavior-pholio-mock-view.js new file mode 100644 index 0000000000..c9953e33d6 --- /dev/null +++ b/webroot/rsrc/js/application/pholio/behavior-pholio-mock-view.js @@ -0,0 +1,17 @@ +/** + * @provides javelin-behavior-pholio-mock-view + * @requires javelin-behavior + * javelin-stratcom + */ +JX.behavior('pholio-mock-view', function(config) { + JX.Stratcom.listen( + 'click', // Listen for clicks... + 'mock-thumbnail', // ...on nodes with sigil "mock-thumbnail". + function(e) { + var data = e.getNodeData('mock-thumbnail'); + + var main = JX.$(config.mainID); + main.src = data.fullSizeURI; + }); +}); + diff --git a/webroot/rsrc/js/application/transactions/behavior-transaction-list.js b/webroot/rsrc/js/application/transactions/behavior-transaction-list.js index 0658ac2ebf..64195ad18e 100644 --- a/webroot/rsrc/js/application/transactions/behavior-transaction-list.js +++ b/webroot/rsrc/js/application/transactions/behavior-transaction-list.js @@ -75,21 +75,24 @@ JX.behavior('phabricator-transaction-list', function(config) { e.kill(); }); - JX.Stratcom.listen('submit', 'transaction-append', function(e) { - var form = e.getTarget(); + JX.Stratcom.listen( + ['submit', 'didSyntheticSubmit'], + 'transaction-append', + function(e) { + var form = e.getTarget(); - JX.Workflow.newFromForm(form, {anchor: next_anchor}) - .setHandler(function(response) { - ontransactions(response); + JX.Workflow.newFromForm(form, {anchor: next_anchor}) + .setHandler(function(response) { + ontransactions(response); - var e = JX.DOM.invoke(form, 'willClear'); - if (!e.getPrevented()) { - form.reset(); - } - }) - .start(); + var e = JX.DOM.invoke(form, 'willClear'); + if (!e.getPrevented()) { + form.reset(); + } + }) + .start(); - e.kill(); - }); + e.kill(); + }); });