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