(stable) Promote 2016 Week 28
1
.gitignore
vendored
|
@ -4,6 +4,7 @@
|
|||
# Diviner
|
||||
/docs/
|
||||
/.divinercache/
|
||||
/src/.cache/
|
||||
|
||||
# libphutil
|
||||
/src/.phutil_module_cache
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
*/
|
||||
return array(
|
||||
'names' => array(
|
||||
'core.pkg.css' => '55d9bb83',
|
||||
'core.pkg.js' => 'f2139810',
|
||||
'core.pkg.css' => '4e7e9bde',
|
||||
'core.pkg.js' => '1bcca0f3',
|
||||
'darkconsole.pkg.js' => 'e7393ebb',
|
||||
'differential.pkg.css' => '3e81ae60',
|
||||
'differential.pkg.js' => '634399e9',
|
||||
|
@ -81,7 +81,7 @@ return array(
|
|||
'rsrc/css/application/owners/owners-path-editor.css' => '2f00933b',
|
||||
'rsrc/css/application/paste/paste.css' => '1898e534',
|
||||
'rsrc/css/application/people/people-profile.css' => '2473d929',
|
||||
'rsrc/css/application/phame/phame.css' => 'bf6a743f',
|
||||
'rsrc/css/application/phame/phame.css' => '8efb0729',
|
||||
'rsrc/css/application/pholio/pholio-edit.css' => '07676f51',
|
||||
'rsrc/css/application/pholio/pholio-inline-comments.css' => '8e545e49',
|
||||
'rsrc/css/application/pholio/pholio.css' => 'ca89d380',
|
||||
|
@ -155,7 +155,7 @@ return array(
|
|||
'rsrc/css/phui/phui-spacing.css' => '042804d6',
|
||||
'rsrc/css/phui/phui-status.css' => 'd5263e49',
|
||||
'rsrc/css/phui/phui-tag-view.css' => '6bbd83e2',
|
||||
'rsrc/css/phui/phui-timeline-view.css' => 'c3782437',
|
||||
'rsrc/css/phui/phui-timeline-view.css' => 'bc523970',
|
||||
'rsrc/css/phui/phui-two-column-view.css' => '9fb86c85',
|
||||
'rsrc/css/phui/workboards/phui-workboard-color.css' => 'ac6fe6a7',
|
||||
'rsrc/css/phui/workboards/phui-workboard.css' => 'e6d89647',
|
||||
|
@ -163,7 +163,7 @@ return array(
|
|||
'rsrc/css/phui/workboards/phui-workpanel.css' => '92197373',
|
||||
'rsrc/css/sprite-login.css' => '60e8560e',
|
||||
'rsrc/css/sprite-menu.css' => '9dd65b92',
|
||||
'rsrc/css/sprite-tokens.css' => '72b952bd',
|
||||
'rsrc/css/sprite-tokens.css' => '9cdfd599',
|
||||
'rsrc/css/syntax/syntax-default.css' => '9923583c',
|
||||
'rsrc/externals/d3/d3.min.js' => 'a11a5ff2',
|
||||
'rsrc/externals/font/aleo/aleo-bold.eot' => 'd3d3bed7',
|
||||
|
@ -344,8 +344,8 @@ return array(
|
|||
'rsrc/image/sprite-login.png' => '03d5af29',
|
||||
'rsrc/image/sprite-menu-X2.png' => 'cfd8fca5',
|
||||
'rsrc/image/sprite-menu.png' => 'd7a99faa',
|
||||
'rsrc/image/sprite-tokens-X2.png' => 'e991bb40',
|
||||
'rsrc/image/sprite-tokens.png' => 'fe69d6ab',
|
||||
'rsrc/image/sprite-tokens-X2.png' => '804a5232',
|
||||
'rsrc/image/sprite-tokens.png' => 'b41d03da',
|
||||
'rsrc/image/texture/card-gradient.png' => '815f26e8',
|
||||
'rsrc/image/texture/dark-menu-hover.png' => '5fa7ece8',
|
||||
'rsrc/image/texture/dark-menu.png' => '7e22296e',
|
||||
|
@ -463,7 +463,7 @@ return array(
|
|||
'rsrc/js/core/FileUpload.js' => '680ea2c8',
|
||||
'rsrc/js/core/Hovercard.js' => '1bd28176',
|
||||
'rsrc/js/core/KeyboardShortcut.js' => '1ae869f2',
|
||||
'rsrc/js/core/KeyboardShortcutManager.js' => 'c1700f6f',
|
||||
'rsrc/js/core/KeyboardShortcutManager.js' => '4a021c10',
|
||||
'rsrc/js/core/MultirowRowManager.js' => 'b5d57730',
|
||||
'rsrc/js/core/Notification.js' => 'ccf1cbf8',
|
||||
'rsrc/js/core/Prefab.js' => 'cfd23f37',
|
||||
|
@ -491,7 +491,7 @@ return array(
|
|||
'rsrc/js/core/behavior-history-install.js' => '7ee2b591',
|
||||
'rsrc/js/core/behavior-hovercard.js' => 'bcaccd64',
|
||||
'rsrc/js/core/behavior-keyboard-pager.js' => 'a8da01f0',
|
||||
'rsrc/js/core/behavior-keyboard-shortcuts.js' => '7835f8c9',
|
||||
'rsrc/js/core/behavior-keyboard-shortcuts.js' => '01fca1f0',
|
||||
'rsrc/js/core/behavior-lightbox-attachments.js' => 'f8ba29d7',
|
||||
'rsrc/js/core/behavior-line-linker.js' => '1499a8cb',
|
||||
'rsrc/js/core/behavior-more.js' => 'a80d0378',
|
||||
|
@ -523,7 +523,7 @@ return array(
|
|||
'rsrc/js/phui/behavior-phui-tab-group.js' => '0a0b10e9',
|
||||
'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8',
|
||||
'rsrc/js/phuix/PHUIXActionView.js' => '8cf6d262',
|
||||
'rsrc/js/phuix/PHUIXAutocomplete.js' => '9196fb06',
|
||||
'rsrc/js/phuix/PHUIXAutocomplete.js' => '6d86ce8b',
|
||||
'rsrc/js/phuix/PHUIXDropdownMenu.js' => '82e270da',
|
||||
'rsrc/js/phuix/PHUIXFormControl.js' => 'e15869a8',
|
||||
'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b',
|
||||
|
@ -654,7 +654,7 @@ return array(
|
|||
'javelin-behavior-phabricator-gesture' => '3ab51e2c',
|
||||
'javelin-behavior-phabricator-gesture-example' => '558829c2',
|
||||
'javelin-behavior-phabricator-keyboard-pager' => 'a8da01f0',
|
||||
'javelin-behavior-phabricator-keyboard-shortcuts' => '7835f8c9',
|
||||
'javelin-behavior-phabricator-keyboard-shortcuts' => '01fca1f0',
|
||||
'javelin-behavior-phabricator-line-linker' => '1499a8cb',
|
||||
'javelin-behavior-phabricator-nav' => '56a1ca03',
|
||||
'javelin-behavior-phabricator-notification-example' => '8ce821c5',
|
||||
|
@ -780,7 +780,7 @@ return array(
|
|||
'phabricator-filetree-view-css' => 'fccf9f82',
|
||||
'phabricator-flag-css' => '5337623f',
|
||||
'phabricator-keyboard-shortcut' => '1ae869f2',
|
||||
'phabricator-keyboard-shortcut-manager' => 'c1700f6f',
|
||||
'phabricator-keyboard-shortcut-manager' => '4a021c10',
|
||||
'phabricator-main-menu-view' => 'b623169f',
|
||||
'phabricator-nav-view-css' => 'ac79a758',
|
||||
'phabricator-notification' => 'ccf1cbf8',
|
||||
|
@ -811,7 +811,7 @@ return array(
|
|||
'phabricator-uiexample-reactor-sendclass' => '1def2711',
|
||||
'phabricator-uiexample-reactor-sendproperties' => 'b1f0ccee',
|
||||
'phabricator-zindex-css' => '5b6fcf3f',
|
||||
'phame-css' => 'bf6a743f',
|
||||
'phame-css' => '8efb0729',
|
||||
'pholio-css' => 'ca89d380',
|
||||
'pholio-edit-css' => '07676f51',
|
||||
'pholio-inline-comments-css' => '8e545e49',
|
||||
|
@ -863,7 +863,7 @@ return array(
|
|||
'phui-status-list-view-css' => 'd5263e49',
|
||||
'phui-tag-view-css' => '6bbd83e2',
|
||||
'phui-theme-css' => '027ba77e',
|
||||
'phui-timeline-view-css' => 'c3782437',
|
||||
'phui-timeline-view-css' => 'bc523970',
|
||||
'phui-two-column-view-css' => '9fb86c85',
|
||||
'phui-workboard-color-css' => 'ac6fe6a7',
|
||||
'phui-workboard-view-css' => 'e6d89647',
|
||||
|
@ -871,7 +871,7 @@ return array(
|
|||
'phui-workpanel-view-css' => '92197373',
|
||||
'phuix-action-list-view' => 'b5c256b8',
|
||||
'phuix-action-view' => '8cf6d262',
|
||||
'phuix-autocomplete' => '9196fb06',
|
||||
'phuix-autocomplete' => '6d86ce8b',
|
||||
'phuix-dropdown-menu' => '82e270da',
|
||||
'phuix-form-control-view' => 'e15869a8',
|
||||
'phuix-icon-view' => 'bff6884b',
|
||||
|
@ -888,7 +888,7 @@ return array(
|
|||
'setup-issue-css' => 'db7e9c40',
|
||||
'sprite-login-css' => '60e8560e',
|
||||
'sprite-menu-css' => '9dd65b92',
|
||||
'sprite-tokens-css' => '72b952bd',
|
||||
'sprite-tokens-css' => '9cdfd599',
|
||||
'syntax-default-css' => '9923583c',
|
||||
'syntax-highlighting-css' => '769d3498',
|
||||
'tokens-css' => '3d0f239e',
|
||||
|
@ -921,6 +921,13 @@ return array(
|
|||
'javelin-workflow',
|
||||
'phabricator-draggable-list',
|
||||
),
|
||||
'01fca1f0' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-workflow',
|
||||
'javelin-json',
|
||||
'javelin-dom',
|
||||
'phabricator-keyboard-shortcut',
|
||||
),
|
||||
'031cee25' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-request',
|
||||
|
@ -1222,6 +1229,13 @@ return array(
|
|||
'javelin-dom',
|
||||
'javelin-stratcom',
|
||||
),
|
||||
'4a021c10' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
'javelin-vector',
|
||||
),
|
||||
'4b700e9e' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
|
@ -1441,6 +1455,12 @@ return array(
|
|||
'javelin-typeahead',
|
||||
'javelin-uri',
|
||||
),
|
||||
'6d86ce8b' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
'phuix-icon-view',
|
||||
'phabricator-prefab',
|
||||
),
|
||||
'70baed2f' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
|
@ -1486,13 +1506,6 @@ return array(
|
|||
'multirow-row-manager',
|
||||
'javelin-json',
|
||||
),
|
||||
'7835f8c9' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-workflow',
|
||||
'javelin-json',
|
||||
'javelin-dom',
|
||||
'phabricator-keyboard-shortcut',
|
||||
),
|
||||
'7927a7d3' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-quicksand',
|
||||
|
@ -1608,12 +1621,6 @@ return array(
|
|||
'javelin-dom',
|
||||
'javelin-request',
|
||||
),
|
||||
'9196fb06' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
'phuix-icon-view',
|
||||
'phabricator-prefab',
|
||||
),
|
||||
92197373 => array(
|
||||
'phui-workcard-view-css',
|
||||
),
|
||||
|
@ -1882,13 +1889,6 @@ return array(
|
|||
'javelin-install',
|
||||
'javelin-dom',
|
||||
),
|
||||
'c1700f6f' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
'javelin-vector',
|
||||
),
|
||||
'c587b80f' => array(
|
||||
'javelin-install',
|
||||
),
|
||||
|
|
|
@ -21,6 +21,46 @@
|
|||
"rule": ".tokens-coin-4",
|
||||
"hash": "75832b7e42df9287b3c35c6afed12a93"
|
||||
},
|
||||
"tokens-emoji-1": {
|
||||
"name": "tokens-emoji-1",
|
||||
"rule": ".tokens-emoji-1",
|
||||
"hash": "17f57bdeb4078f9c05f1f037ccb1c162"
|
||||
},
|
||||
"tokens-emoji-2": {
|
||||
"name": "tokens-emoji-2",
|
||||
"rule": ".tokens-emoji-2",
|
||||
"hash": "6877c6e0c63522d5819531aaf4aba787"
|
||||
},
|
||||
"tokens-emoji-3": {
|
||||
"name": "tokens-emoji-3",
|
||||
"rule": ".tokens-emoji-3",
|
||||
"hash": "cc67534b0119d4cc385a93ed5aff86e4"
|
||||
},
|
||||
"tokens-emoji-4": {
|
||||
"name": "tokens-emoji-4",
|
||||
"rule": ".tokens-emoji-4",
|
||||
"hash": "f2a6febd638670962dfb5fdd76b23cfb"
|
||||
},
|
||||
"tokens-emoji-5": {
|
||||
"name": "tokens-emoji-5",
|
||||
"rule": ".tokens-emoji-5",
|
||||
"hash": "22bc23d162449fde492e0fd3eccc7301"
|
||||
},
|
||||
"tokens-emoji-6": {
|
||||
"name": "tokens-emoji-6",
|
||||
"rule": ".tokens-emoji-6",
|
||||
"hash": "e3689840f410ff1bbf365f6b06043d3f"
|
||||
},
|
||||
"tokens-emoji-7": {
|
||||
"name": "tokens-emoji-7",
|
||||
"rule": ".tokens-emoji-7",
|
||||
"hash": "a689b9fe7c9f6f300d757b5350e2cc4b"
|
||||
},
|
||||
"tokens-emoji-8": {
|
||||
"name": "tokens-emoji-8",
|
||||
"rule": ".tokens-emoji-8",
|
||||
"hash": "26570ef132caea33307e1e7574d754e8"
|
||||
},
|
||||
"tokens-heart-1": {
|
||||
"name": "tokens-heart-1",
|
||||
"rule": ".tokens-heart-1",
|
||||
|
|
BIN
resources/sprite/tokens_1x/emoji-1.png
Normal file
After Width: | Height: | Size: 611 B |
BIN
resources/sprite/tokens_1x/emoji-2.png
Normal file
After Width: | Height: | Size: 752 B |
BIN
resources/sprite/tokens_1x/emoji-3.png
Normal file
After Width: | Height: | Size: 581 B |
BIN
resources/sprite/tokens_1x/emoji-4.png
Normal file
After Width: | Height: | Size: 516 B |
BIN
resources/sprite/tokens_1x/emoji-5.png
Normal file
After Width: | Height: | Size: 638 B |
BIN
resources/sprite/tokens_1x/emoji-6.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
resources/sprite/tokens_1x/emoji-7.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
resources/sprite/tokens_1x/emoji-8.png
Normal file
After Width: | Height: | Size: 717 B |
BIN
resources/sprite/tokens_2x/emoji-1.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
resources/sprite/tokens_2x/emoji-2.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
resources/sprite/tokens_2x/emoji-3.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
resources/sprite/tokens_2x/emoji-4.png
Normal file
After Width: | Height: | Size: 1 KiB |
BIN
resources/sprite/tokens_2x/emoji-5.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
resources/sprite/tokens_2x/emoji-6.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
resources/sprite/tokens_2x/emoji-7.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
resources/sprite/tokens_2x/emoji-8.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_phame.phame_blog
|
||||
MODIFY parentDomain VARCHAR(128) NULL COLLATE {$COLLATE_TEXT};
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_phame.phame_blog
|
||||
MODIFY parentSite VARCHAR(128) NULL COLLATE {$COLLATE_TEXT};
|
|
@ -2295,7 +2295,10 @@ phutil_register_library_map(array(
|
|||
'PhabricatorDashboardPanelArchiveController' => 'applications/dashboard/controller/PhabricatorDashboardPanelArchiveController.php',
|
||||
'PhabricatorDashboardPanelCoreCustomField' => 'applications/dashboard/customfield/PhabricatorDashboardPanelCoreCustomField.php',
|
||||
'PhabricatorDashboardPanelCustomField' => 'applications/dashboard/customfield/PhabricatorDashboardPanelCustomField.php',
|
||||
'PhabricatorDashboardPanelEditConduitAPIMethod' => 'applications/dashboard/conduit/PhabricatorDashboardPanelEditConduitAPIMethod.php',
|
||||
'PhabricatorDashboardPanelEditController' => 'applications/dashboard/controller/PhabricatorDashboardPanelEditController.php',
|
||||
'PhabricatorDashboardPanelEditEngine' => 'applications/dashboard/editor/PhabricatorDashboardPanelEditEngine.php',
|
||||
'PhabricatorDashboardPanelEditproController' => 'applications/dashboard/controller/PhabricatorDashboardPanelEditproController.php',
|
||||
'PhabricatorDashboardPanelHasDashboardEdgeType' => 'applications/dashboard/edge/PhabricatorDashboardPanelHasDashboardEdgeType.php',
|
||||
'PhabricatorDashboardPanelListController' => 'applications/dashboard/controller/PhabricatorDashboardPanelListController.php',
|
||||
'PhabricatorDashboardPanelPHIDType' => 'applications/dashboard/phid/PhabricatorDashboardPanelPHIDType.php',
|
||||
|
@ -3831,7 +3834,6 @@ phutil_register_library_map(array(
|
|||
'PhameNextPostView' => 'applications/phame/view/PhameNextPostView.php',
|
||||
'PhamePost' => 'applications/phame/storage/PhamePost.php',
|
||||
'PhamePostArchiveController' => 'applications/phame/controller/post/PhamePostArchiveController.php',
|
||||
'PhamePostCommentController' => 'applications/phame/controller/post/PhamePostCommentController.php',
|
||||
'PhamePostController' => 'applications/phame/controller/post/PhamePostController.php',
|
||||
'PhamePostEditConduitAPIMethod' => 'applications/phame/conduit/PhamePostEditConduitAPIMethod.php',
|
||||
'PhamePostEditController' => 'applications/phame/controller/post/PhamePostEditController.php',
|
||||
|
@ -3845,6 +3847,7 @@ phutil_register_library_map(array(
|
|||
'PhamePostMoveController' => 'applications/phame/controller/post/PhamePostMoveController.php',
|
||||
'PhamePostPublishController' => 'applications/phame/controller/post/PhamePostPublishController.php',
|
||||
'PhamePostQuery' => 'applications/phame/query/PhamePostQuery.php',
|
||||
'PhamePostRemarkupRule' => 'applications/phame/remarkup/PhamePostRemarkupRule.php',
|
||||
'PhamePostReplyHandler' => 'applications/phame/mail/PhamePostReplyHandler.php',
|
||||
'PhamePostSearchConduitAPIMethod' => 'applications/phame/conduit/PhamePostSearchConduitAPIMethod.php',
|
||||
'PhamePostSearchEngine' => 'applications/phame/query/PhamePostSearchEngine.php',
|
||||
|
@ -6942,7 +6945,6 @@ phutil_register_library_map(array(
|
|||
'PhabricatorPolicyInterface',
|
||||
'PhabricatorCustomFieldInterface',
|
||||
'PhabricatorFlaggableInterface',
|
||||
'PhabricatorProjectInterface',
|
||||
'PhabricatorDestructibleInterface',
|
||||
),
|
||||
'PhabricatorDashboardPanelArchiveController' => 'PhabricatorDashboardController',
|
||||
|
@ -6951,7 +6953,10 @@ phutil_register_library_map(array(
|
|||
'PhabricatorStandardCustomFieldInterface',
|
||||
),
|
||||
'PhabricatorDashboardPanelCustomField' => 'PhabricatorCustomField',
|
||||
'PhabricatorDashboardPanelEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
|
||||
'PhabricatorDashboardPanelEditController' => 'PhabricatorDashboardController',
|
||||
'PhabricatorDashboardPanelEditEngine' => 'PhabricatorEditEngine',
|
||||
'PhabricatorDashboardPanelEditproController' => 'PhabricatorDashboardController',
|
||||
'PhabricatorDashboardPanelHasDashboardEdgeType' => 'PhabricatorEdgeType',
|
||||
'PhabricatorDashboardPanelListController' => 'PhabricatorDashboardController',
|
||||
'PhabricatorDashboardPanelPHIDType' => 'PhabricatorPHIDType',
|
||||
|
@ -8755,7 +8760,6 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFulltextInterface',
|
||||
),
|
||||
'PhamePostArchiveController' => 'PhamePostController',
|
||||
'PhamePostCommentController' => 'PhamePostController',
|
||||
'PhamePostController' => 'PhameController',
|
||||
'PhamePostEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
|
||||
'PhamePostEditController' => 'PhamePostController',
|
||||
|
@ -8769,6 +8773,7 @@ phutil_register_library_map(array(
|
|||
'PhamePostMoveController' => 'PhamePostController',
|
||||
'PhamePostPublishController' => 'PhamePostController',
|
||||
'PhamePostQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhamePostRemarkupRule' => 'PhabricatorObjectRemarkupRule',
|
||||
'PhamePostReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
|
||||
'PhamePostSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
|
||||
'PhamePostSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
|
|
|
@ -183,7 +183,8 @@ abstract class PhabricatorApplication
|
|||
$item = id(new PHUIListItemView())
|
||||
->setName($article['name'])
|
||||
->setIcon('fa-book')
|
||||
->setHref($article['href']);
|
||||
->setHref($article['href'])
|
||||
->setOpenInNewWindow(true);
|
||||
|
||||
$items[] = $item;
|
||||
}
|
||||
|
@ -203,7 +204,8 @@ abstract class PhabricatorApplication
|
|||
$item = id(new PHUIListItemView())
|
||||
->setName($spec['name'])
|
||||
->setIcon('fa-envelope-o')
|
||||
->setHref($href);
|
||||
->setHref($href)
|
||||
->setOpenInNewWindow(true);
|
||||
$items[] = $item;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,23 @@ final class PhabricatorPHPConfigSetupCheck extends PhabricatorSetupCheck {
|
|||
}
|
||||
|
||||
protected function executeChecks() {
|
||||
if (version_compare(phpversion(), 7, '>=')) {
|
||||
$message = pht(
|
||||
'This version of Phabricator does not support PHP 7. You '.
|
||||
'are running PHP %s.',
|
||||
phpversion());
|
||||
|
||||
$this->newIssue('php.version7')
|
||||
->setIsFatal(true)
|
||||
->setName(pht('PHP 7 Not Supported'))
|
||||
->setMessage($message)
|
||||
->addLink(
|
||||
'https://phurl.io/u/php7',
|
||||
pht('Phabricator PHP 7 Compatibility Information'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$safe_mode = ini_get('safe_mode');
|
||||
if ($safe_mode) {
|
||||
$message = pht(
|
||||
|
|
|
@ -190,6 +190,7 @@ final class PhabricatorDaemonConsoleController
|
|||
$triggers = id(new PhabricatorWorkerTriggerQuery())
|
||||
->setViewer($viewer)
|
||||
->setOrder(PhabricatorWorkerTriggerQuery::ORDER_EXECUTION)
|
||||
->withNextEventBetween(0, null)
|
||||
->needEvents(true)
|
||||
->setLimit(10)
|
||||
->execute();
|
||||
|
|
|
@ -15,9 +15,13 @@ final class PhabricatorWorkerTaskDetailController
|
|||
$task = reset($tasks);
|
||||
}
|
||||
|
||||
$header = new PHUIHeaderView();
|
||||
|
||||
if (!$task) {
|
||||
$title = pht('Task Does Not Exist');
|
||||
|
||||
$header->setHeader(pht('Task %d Missing', $id));
|
||||
|
||||
$error_view = new PHUIInfoView();
|
||||
$error_view->setTitle(pht('No Such Task'));
|
||||
$error_view->appendChild(phutil_tag(
|
||||
|
@ -30,11 +34,11 @@ final class PhabricatorWorkerTaskDetailController
|
|||
} else {
|
||||
$title = pht('Task %d', $task->getID());
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader(pht('Task %d: %s',
|
||||
$header->setHeader(
|
||||
pht(
|
||||
'Task %d: %s',
|
||||
$task->getID(),
|
||||
$task->getTaskClass()))
|
||||
->setHeaderIcon('fa-sort');
|
||||
$task->getTaskClass()));
|
||||
|
||||
$properties = $this->buildPropertyListView($task);
|
||||
|
||||
|
@ -59,6 +63,8 @@ final class PhabricatorWorkerTaskDetailController
|
|||
);
|
||||
}
|
||||
|
||||
$header->setHeaderIcon('fa-sort');
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$crumbs->addTextCrumb($title);
|
||||
$crumbs->setBorder(true);
|
||||
|
|
|
@ -40,6 +40,8 @@ final class PhabricatorDashboardApplication extends PhabricatorApplication {
|
|||
'(?:query/(?P<queryKey>[^/]+)/)?'
|
||||
=> 'PhabricatorDashboardPanelListController',
|
||||
'create/' => 'PhabricatorDashboardPanelEditController',
|
||||
$this->getEditRoutePattern('editpro/')
|
||||
=> 'PhabricatorDashboardPanelEditproController',
|
||||
'edit/(?:(?P<id>\d+)/)?' => 'PhabricatorDashboardPanelEditController',
|
||||
'render/(?P<id>\d+)/' => 'PhabricatorDashboardPanelRenderController',
|
||||
'archive/(?P<id>\d+)/'
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorDashboardPanelEditConduitAPIMethod
|
||||
extends PhabricatorEditEngineAPIMethod {
|
||||
|
||||
public function getAPIMethodName() {
|
||||
return 'dashboard.panel.edit';
|
||||
}
|
||||
|
||||
public function newEditEngine() {
|
||||
return new PhabricatorDashboardPanelEditEngine();
|
||||
}
|
||||
|
||||
public function getMethodSummary() {
|
||||
return pht(
|
||||
'Apply transactions to create a new dashboard panel or edit an '.
|
||||
'existing one.');
|
||||
}
|
||||
|
||||
}
|
|
@ -51,10 +51,6 @@ final class PhabricatorDashboardPanelEditController
|
|||
if (!$panel) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
$v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs(
|
||||
$panel->getPHID(),
|
||||
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
|
||||
$v_projects = array_reverse($v_projects);
|
||||
|
||||
if ($dashboard) {
|
||||
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||
|
@ -84,7 +80,6 @@ final class PhabricatorDashboardPanelEditController
|
|||
if (empty($types[$type])) {
|
||||
return $this->processPanelTypeRequest($request);
|
||||
}
|
||||
$v_projects = array();
|
||||
|
||||
$panel->setPanelType($type);
|
||||
}
|
||||
|
@ -135,7 +130,6 @@ final class PhabricatorDashboardPanelEditController
|
|||
$v_name = $request->getStr('name');
|
||||
$v_view_policy = $request->getStr('viewPolicy');
|
||||
$v_edit_policy = $request->getStr('editPolicy');
|
||||
$v_projects = $request->getArr('projects');
|
||||
|
||||
$type_name = PhabricatorDashboardPanelTransaction::TYPE_NAME;
|
||||
$type_view_policy = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
||||
|
@ -155,12 +149,6 @@ final class PhabricatorDashboardPanelEditController
|
|||
->setTransactionType($type_edit_policy)
|
||||
->setNewValue($v_edit_policy);
|
||||
|
||||
$proj_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
|
||||
$xactions[] = id(new PhabricatorDashboardPanelTransaction())
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
|
||||
->setMetadataValue('edge:type', $proj_edge_type)
|
||||
->setNewValue(array('=' => array_fuse($v_projects)));
|
||||
|
||||
$field_xactions = $field_list->buildFieldTransactionsFromRequest(
|
||||
new PhabricatorDashboardPanelTransaction(),
|
||||
$request);
|
||||
|
@ -238,13 +226,6 @@ final class PhabricatorDashboardPanelEditController
|
|||
->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
|
||||
->setPolicies($policies));
|
||||
|
||||
$form->appendControl(
|
||||
id(new AphrontFormTokenizerControl())
|
||||
->setLabel(pht('Tags'))
|
||||
->setName('projects')
|
||||
->setValue($v_projects)
|
||||
->setDatasource(new PhabricatorProjectDatasource()));
|
||||
|
||||
$field_list->appendFieldsToForm($form);
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorDashboardPanelEditproController
|
||||
extends PhabricatorDashboardController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$engine = id(new PhabricatorDashboardPanelEditEngine())
|
||||
->setController($this);
|
||||
|
||||
$id = $request->getURIData('id');
|
||||
if (!$id) {
|
||||
$list_uri = $this->getApplicationURI('panel/');
|
||||
|
||||
$panel_type = $request->getStr('panelType');
|
||||
$panel_types = PhabricatorDashboardPanelType::getAllPanelTypes();
|
||||
if (empty($panel_types[$panel_type])) {
|
||||
return $this->buildPanelTypeResponse($list_uri);
|
||||
}
|
||||
|
||||
$engine
|
||||
->addContextParameter('panelType', $panel_type)
|
||||
->setPanelType($panel_type);
|
||||
}
|
||||
|
||||
return $engine->buildResponse();
|
||||
}
|
||||
|
||||
private function buildPanelTypeResponse($cancel_uri) {
|
||||
$panel_types = PhabricatorDashboardPanelType::getAllPanelTypes();
|
||||
|
||||
$viewer = $this->getViewer();
|
||||
$request = $this->getRequest();
|
||||
|
||||
$e_type = null;
|
||||
$errors = array();
|
||||
if ($request->isFormPost()) {
|
||||
$e_type = pht('Required');
|
||||
$errors[] = pht(
|
||||
'To create a new dashboard panel, you must select a panel type.');
|
||||
}
|
||||
|
||||
$type_control = id(new AphrontFormRadioButtonControl())
|
||||
->setLabel(pht('Panel Type'))
|
||||
->setName('panelType')
|
||||
->setError($e_type);
|
||||
|
||||
foreach ($panel_types as $key => $type) {
|
||||
$type_control->addButton(
|
||||
$key,
|
||||
$type->getPanelTypeName(),
|
||||
$type->getPanelTypeDescription());
|
||||
}
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($viewer)
|
||||
->appendRemarkupInstructions(
|
||||
pht('Choose the type of dashboard panel to create:'))
|
||||
->appendChild($type_control);
|
||||
|
||||
if ($request->isAjax()) {
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Add New Panel'))
|
||||
->setWidth(AphrontDialogView::WIDTH_FORM)
|
||||
->setErrors($errors)
|
||||
->appendForm($form)
|
||||
->addCancelButton($cancel_uri)
|
||||
->addSubmitButton(pht('Continue'));
|
||||
}
|
||||
|
||||
$form->appendChild(
|
||||
id(new AphrontFormSubmitControl())
|
||||
->setValue(pht('Continue'))
|
||||
->addCancelButton($cancel_uri));
|
||||
|
||||
$title = pht('Create Dashboard Panel');
|
||||
$header_icon = 'fa-plus-square';
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$crumbs->addTextCrumb(
|
||||
pht('Panels'),
|
||||
$this->getApplicationURI('panel/'));
|
||||
$crumbs->addTextCrumb(pht('New Panel'));
|
||||
$crumbs->setBorder(true);
|
||||
|
||||
$box = id(new PHUIObjectBoxView())
|
||||
->setHeaderText(pht('Panel'))
|
||||
->setFormErrors($errors)
|
||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->setForm($form);
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader($title)
|
||||
->setHeaderIcon($header_icon);
|
||||
|
||||
$view = id(new PHUITwoColumnView())
|
||||
->setHeader($header)
|
||||
->setFooter($box);
|
||||
|
||||
return $this->newPage()
|
||||
->setTitle($title)
|
||||
->setCrumbs($crumbs)
|
||||
->appendChild($view);
|
||||
}
|
||||
|
||||
}
|
|
@ -9,6 +9,10 @@ final class PhabricatorDashboardPanelCoreCustomField
|
|||
}
|
||||
|
||||
public function createFields($object) {
|
||||
if (!$object->getPanelType()) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$impl = $object->requireImplementation();
|
||||
$specs = $impl->getFieldSpecifications();
|
||||
return PhabricatorStandardCustomField::buildStandardFields($this, $specs);
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorDashboardPanelEditEngine
|
||||
extends PhabricatorEditEngine {
|
||||
|
||||
const ENGINECONST = 'dashboard.panel';
|
||||
|
||||
private $panelType;
|
||||
|
||||
public function setPanelType($panel_type) {
|
||||
$this->panelType = $panel_type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPanelType() {
|
||||
return $this->panelType;
|
||||
}
|
||||
|
||||
public function isEngineConfigurable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getEngineName() {
|
||||
return pht('Dashboard Panels');
|
||||
}
|
||||
|
||||
public function getSummaryHeader() {
|
||||
return pht('Edit Dashboard Panels');
|
||||
}
|
||||
|
||||
public function getSummaryText() {
|
||||
return pht('This engine is used to modify dashboard panels.');
|
||||
}
|
||||
|
||||
public function getEngineApplicationClass() {
|
||||
return 'PhabricatorSearchApplication';
|
||||
}
|
||||
|
||||
protected function newEditableObject() {
|
||||
$viewer = $this->getViewer();
|
||||
$panel = PhabricatorDashboardPanel::initializeNewPanel($viewer);
|
||||
|
||||
if ($this->panelType) {
|
||||
$panel->setPanelType($this->panelType);
|
||||
}
|
||||
|
||||
return $panel;
|
||||
}
|
||||
|
||||
protected function newObjectQuery() {
|
||||
return new PhabricatorDashboardPanelQuery();
|
||||
}
|
||||
|
||||
protected function getObjectCreateTitleText($object) {
|
||||
return pht('Create Dashboard Panel');
|
||||
}
|
||||
|
||||
protected function getObjectCreateButtonText($object) {
|
||||
return pht('Create Panel');
|
||||
}
|
||||
|
||||
protected function getObjectEditTitleText($object) {
|
||||
return pht('Edit Panel: %s', $object->getName());
|
||||
}
|
||||
|
||||
protected function getObjectEditShortText($object) {
|
||||
return pht('Edit Panel');
|
||||
}
|
||||
|
||||
protected function getObjectCreateShortText() {
|
||||
return pht('Edit Panel');
|
||||
}
|
||||
|
||||
protected function getObjectName() {
|
||||
return pht('Dashboard Panel');
|
||||
}
|
||||
|
||||
protected function getObjectViewURI($object) {
|
||||
return $object->getURI();
|
||||
}
|
||||
|
||||
protected function buildCustomEditFields($object) {
|
||||
return array(
|
||||
id(new PhabricatorTextEditField())
|
||||
->setKey('name')
|
||||
->setLabel(pht('Name'))
|
||||
->setDescription(pht('Name of the panel.'))
|
||||
->setConduitDescription(pht('Rename the panel.'))
|
||||
->setConduitTypeDescription(pht('New panel name.'))
|
||||
->setTransactionType(PhabricatorDashboardPanelTransaction::TYPE_NAME)
|
||||
->setIsRequired(true)
|
||||
->setValue($object->getName()),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -10,7 +10,6 @@ final class PhabricatorDashboardPanel
|
|||
PhabricatorPolicyInterface,
|
||||
PhabricatorCustomFieldInterface,
|
||||
PhabricatorFlaggableInterface,
|
||||
PhabricatorProjectInterface,
|
||||
PhabricatorDestructibleInterface {
|
||||
|
||||
protected $name;
|
||||
|
@ -72,6 +71,10 @@ final class PhabricatorDashboardPanel
|
|||
return 'W'.$this->getID();
|
||||
}
|
||||
|
||||
public function getURI() {
|
||||
return '/'.$this->getMonogram();
|
||||
}
|
||||
|
||||
public function getPanelTypes() {
|
||||
$panel_types = PhabricatorDashboardPanelType::getAllPanelTypes();
|
||||
$panel_types = mpull($panel_types, 'getPanelTypeName', 'getPanelTypeKey');
|
||||
|
|
|
@ -173,7 +173,7 @@ final class DiffusionHistoryQueryConduitAPIMethod
|
|||
}
|
||||
|
||||
$hash_list = array_reverse($hash_list);
|
||||
$this->parents = $parent_map;
|
||||
$this->parents = array_reverse($parent_map, true);
|
||||
|
||||
return DiffusionQuery::loadHistoryForCommitIdentifiers(
|
||||
$hash_list,
|
||||
|
|
|
@ -56,9 +56,30 @@ final class DiffusionRepositoryEditController
|
|||
$layout->addColumn($action);
|
||||
}
|
||||
|
||||
$hints = id(new AphrontMultiColumnView())
|
||||
->setFluidLayout(true);
|
||||
|
||||
$observe_href = PhabricatorEnv::getDoclink(
|
||||
'Diffusion User Guide: Existing Repositories');
|
||||
|
||||
$hints->addColumn(
|
||||
id(new PHUIActionPanelView())
|
||||
->setIcon('fa-book')
|
||||
->setHeader(pht('Import or Observe an Existing Repository'))
|
||||
->setHref($observe_href)
|
||||
->setSubheader(
|
||||
pht(
|
||||
'Review the documentation describing how to import or observe an '.
|
||||
'existing repository.')));
|
||||
|
||||
$view = id(new PHUITwoColumnView())
|
||||
->setHeader($header)
|
||||
->setFooter($layout);
|
||||
->setFooter(
|
||||
array(
|
||||
$layout,
|
||||
phutil_tag('br'),
|
||||
$hints,
|
||||
));
|
||||
|
||||
return $this->newPage()
|
||||
->setTitle($title)
|
||||
|
|
|
@ -77,6 +77,11 @@ final class DiffusionURIEditor
|
|||
$old_uri = $object->getEffectiveURI();
|
||||
} else {
|
||||
$old_uri = null;
|
||||
|
||||
// When creating a URI, we may not have processed the repository
|
||||
// transaction yet. Attach the repository here to make sure we
|
||||
// have it for the calls below.
|
||||
$object->attachRepository($this->repository);
|
||||
}
|
||||
|
||||
$object->setURI($xaction->getNewValue());
|
||||
|
|
|
@ -5,6 +5,7 @@ final class PhabricatorNotificationBuilder extends Phobject {
|
|||
private $stories;
|
||||
private $parsedStories;
|
||||
private $user = null;
|
||||
private $showTimestamps = true;
|
||||
|
||||
public function __construct(array $stories) {
|
||||
assert_instances_of($stories, 'PhabricatorFeedStory');
|
||||
|
@ -16,6 +17,15 @@ final class PhabricatorNotificationBuilder extends Phobject {
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function setShowTimestamps($show_timestamps) {
|
||||
$this->showTimestamps = $show_timestamps;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getShowTimestamps() {
|
||||
return $this->showTimestamps;
|
||||
}
|
||||
|
||||
private function parseStories() {
|
||||
|
||||
if ($this->parsedStories) {
|
||||
|
@ -121,6 +131,9 @@ final class PhabricatorNotificationBuilder extends Phobject {
|
|||
// TODO: Render a nice debuggable notice instead?
|
||||
continue;
|
||||
}
|
||||
|
||||
$view->setShowTimestamp($this->getShowTimestamps());
|
||||
|
||||
$null_view->appendChild($view->renderNotification($this->user));
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,8 @@ final class PhabricatorNotificationIndividualController
|
|||
}
|
||||
|
||||
$builder = id(new PhabricatorNotificationBuilder(array($story)))
|
||||
->setUser($viewer);
|
||||
->setUser($viewer)
|
||||
->setShowTimestamps(false);
|
||||
|
||||
$content = $builder->buildView()->render();
|
||||
$dict = $builder->buildDict();
|
||||
|
|
|
@ -82,7 +82,13 @@ final class PhabricatorOwnersPathsController
|
|||
}
|
||||
}
|
||||
|
||||
$repos = mpull($repos, 'getDisplayName', 'getPHID');
|
||||
|
||||
$repo_map = array();
|
||||
foreach ($repos as $key => $repo) {
|
||||
$monogram = $repo->getMonogram();
|
||||
$name = $repo->getName();
|
||||
$repo_map[$repo->getPHID()] = "{$monogram} {$name}";
|
||||
}
|
||||
asort($repos);
|
||||
|
||||
$template = new AphrontTypeaheadTemplateView();
|
||||
|
@ -94,7 +100,7 @@ final class PhabricatorOwnersPathsController
|
|||
'root' => 'path-editor',
|
||||
'table' => 'paths',
|
||||
'add_button' => 'addpath',
|
||||
'repositories' => $repos,
|
||||
'repositories' => $repo_map,
|
||||
'input_template' => $template,
|
||||
'pathRefs' => $path_refs,
|
||||
|
||||
|
|
|
@ -15,15 +15,19 @@ final class PhabricatorPasteLanguageTransaction
|
|||
|
||||
public function getTitle() {
|
||||
return pht(
|
||||
"%s updated the paste's language.",
|
||||
$this->renderAuthor());
|
||||
"%s updated the paste's language from %s to %s.",
|
||||
$this->renderAuthor(),
|
||||
$this->renderValue($this->getOldValue()),
|
||||
$this->renderValue($this->getNewValue()));
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
return pht(
|
||||
'%s updated the language for %s.',
|
||||
'%s updated the language for %s from %s to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject());
|
||||
$this->renderObject(),
|
||||
$this->renderValue($this->getOldValue()),
|
||||
$this->renderValue($this->getNewValue()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ final class PhabricatorPasteStatusTransaction
|
|||
}
|
||||
|
||||
private function isActivate() {
|
||||
return ($this->getNewValue() == PhabricatorPaste::STATUS_ARCHIVED);
|
||||
return ($this->getNewValue() == PhabricatorPaste::STATUS_ACTIVE);
|
||||
}
|
||||
|
||||
public function getIcon() {
|
||||
|
|
|
@ -31,13 +31,10 @@ final class PhabricatorPhameApplication extends PhabricatorApplication {
|
|||
);
|
||||
}
|
||||
|
||||
public function isPrototype() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getRoutes() {
|
||||
return array(
|
||||
'/phame/' => array(
|
||||
'/J(?P<id>[1-9]\d*)' => 'PhamePostViewController',
|
||||
'/phame/' => array(
|
||||
'' => 'PhameHomeController',
|
||||
|
||||
// NOTE: The live routes include an initial "/", so leave it off
|
||||
|
@ -46,17 +43,16 @@ final class PhabricatorPhameApplication extends PhabricatorApplication {
|
|||
'post/' => array(
|
||||
'(?:query/(?P<queryKey>[^/]+)/)?' => 'PhamePostListController',
|
||||
'blogger/(?P<bloggername>[\w\.-_]+)/' => 'PhamePostListController',
|
||||
'edit/(?:(?P<id>[^/]+)/)?' => 'PhamePostEditController',
|
||||
$this->getEditRoutePattern('edit/')
|
||||
=> 'PhamePostEditController',
|
||||
'history/(?P<id>\d+)/' => 'PhamePostHistoryController',
|
||||
'view/(?P<id>\d+)/(?:(?P<slug>[^/]+)/)?' => 'PhamePostViewController',
|
||||
'(?P<action>publish|unpublish)/(?P<id>\d+)/'
|
||||
=> 'PhamePostPublishController',
|
||||
'preview/(?P<id>\d+)/' => 'PhamePostPreviewController',
|
||||
'preview/' => 'PhabricatorMarkupPreviewController',
|
||||
'framed/(?P<id>\d+)/' => 'PhamePostFramedController',
|
||||
'move/(?P<id>\d+)/' => 'PhamePostMoveController',
|
||||
'archive/(?P<id>\d+)/' => 'PhamePostArchiveController',
|
||||
'comment/(?P<id>[1-9]\d*)/' => 'PhamePostCommentController',
|
||||
),
|
||||
'blog/' => array(
|
||||
'(?:query/(?P<queryKey>[^/]+)/)?' => 'PhameBlogListController',
|
||||
|
@ -107,6 +103,13 @@ final class PhabricatorPhameApplication extends PhabricatorApplication {
|
|||
);
|
||||
}
|
||||
|
||||
public function getRemarkupRules() {
|
||||
return array(
|
||||
new PhamePostRemarkupRule(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
protected function getCustomCapabilities() {
|
||||
return array(
|
||||
PhameBlogCreateCapability::CAPABILITY => array(
|
||||
|
|
|
@ -195,7 +195,9 @@ abstract class PhameLiveController extends PhameController {
|
|||
}
|
||||
|
||||
if ($post) {
|
||||
$crumbs->addTextCrumb($post->getTitle());
|
||||
if (!$is_external) {
|
||||
$crumbs->addTextCrumb('J'.$post->getID());
|
||||
}
|
||||
}
|
||||
|
||||
return $crumbs;
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class PhamePostCommentController
|
||||
extends PhamePostController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
$id = $request->getURIData('id');
|
||||
|
||||
if (!$request->isFormPost()) {
|
||||
return new Aphront400Response();
|
||||
}
|
||||
|
||||
$post = id(new PhamePostQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($id))
|
||||
->executeOne();
|
||||
if (!$post) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$is_preview = $request->isPreviewRequest();
|
||||
$draft = PhabricatorDraft::buildFromRequest($request);
|
||||
|
||||
$view_uri = $this->getApplicationURI('post/view/'.$post->getID().'/');
|
||||
|
||||
$xactions = array();
|
||||
$xactions[] = id(new PhamePostTransaction())
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
|
||||
->attachComment(
|
||||
id(new PhamePostTransactionComment())
|
||||
->setContent($request->getStr('comment')));
|
||||
|
||||
$editor = id(new PhamePostEditor())
|
||||
->setActor($viewer)
|
||||
->setContinueOnNoEffect($request->isContinueRequest())
|
||||
->setContentSourceFromRequest($request)
|
||||
->setIsPreview($is_preview);
|
||||
|
||||
try {
|
||||
$xactions = $editor->applyTransactions($post, $xactions);
|
||||
} catch (PhabricatorApplicationTransactionNoEffectException $ex) {
|
||||
return id(new PhabricatorApplicationTransactionNoEffectResponse())
|
||||
->setCancelURI($view_uri)
|
||||
->setException($ex);
|
||||
}
|
||||
|
||||
if ($draft) {
|
||||
$draft->replaceOrDelete();
|
||||
}
|
||||
|
||||
if ($request->isAjax() && $is_preview) {
|
||||
return id(new PhabricatorApplicationTransactionResponse())
|
||||
->setViewer($viewer)
|
||||
->setTransactions($xactions)
|
||||
->setIsPreview($is_preview);
|
||||
} else {
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI($view_uri);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -123,19 +123,22 @@ final class PhamePostViewController
|
|||
->setImage($blogger->getProfileImageURI())
|
||||
->setImageHref($author_uri);
|
||||
|
||||
$monogram = $post->getMonogram();
|
||||
$timeline = $this->buildTransactionTimeline(
|
||||
$post,
|
||||
id(new PhamePostTransactionQuery())
|
||||
->withTransactionTypes(array(PhabricatorTransactions::TYPE_COMMENT)));
|
||||
$timeline = phutil_tag_div('phui-document-view-pro-box', $timeline);
|
||||
$timeline->setQuoteRef($monogram);
|
||||
|
||||
if ($is_external) {
|
||||
$add_comment = null;
|
||||
} else {
|
||||
$add_comment = $this->buildCommentForm($post);
|
||||
$add_comment = phutil_tag_div('mlb mlt', $add_comment);
|
||||
$add_comment = $this->buildCommentForm($post, $timeline);
|
||||
$add_comment = phutil_tag_div('mlb mlt phame-comment-view', $add_comment);
|
||||
}
|
||||
|
||||
$timeline = phutil_tag_div('phui-document-view-pro-box', $timeline);
|
||||
|
||||
list($prev, $next) = $this->loadAdjacentPosts($post);
|
||||
|
||||
$properties = id(new PHUIPropertyListView())
|
||||
|
@ -273,19 +276,13 @@ final class PhamePostViewController
|
|||
return $actions;
|
||||
}
|
||||
|
||||
private function buildCommentForm(PhamePost $post) {
|
||||
private function buildCommentForm(PhamePost $post, $timeline) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$draft = PhabricatorDraft::newFromUserAndKey(
|
||||
$viewer, $post->getPHID());
|
||||
|
||||
$box = id(new PhabricatorApplicationTransactionCommentView())
|
||||
->setUser($viewer)
|
||||
->setObjectPHID($post->getPHID())
|
||||
->setDraft($draft)
|
||||
->setHeaderText(pht('Add Comment'))
|
||||
->setAction($this->getApplicationURI('post/comment/'.$post->getID().'/'))
|
||||
->setSubmitButtonName(pht('Add Comment'));
|
||||
$box = id(new PhamePostEditEngine())
|
||||
->setViewer($viewer)
|
||||
->buildEditEngineCommentView($post)
|
||||
->setTransactionTimeline($timeline);
|
||||
|
||||
return phutil_tag_div('phui-document-view-pro-box', $box);
|
||||
}
|
||||
|
|
|
@ -66,6 +66,7 @@ final class PhameBlogEditor
|
|||
case PhameBlogTransaction::TYPE_DESCRIPTION:
|
||||
case PhameBlogTransaction::TYPE_STATUS:
|
||||
case PhameBlogTransaction::TYPE_PARENTSITE:
|
||||
case PhameBlogTransaction::TYPE_PARENTDOMAIN:
|
||||
case PhameBlogTransaction::TYPE_PROFILEIMAGE:
|
||||
case PhameBlogTransaction::TYPE_HEADERIMAGE:
|
||||
return $xaction->getNewValue();
|
||||
|
|
|
@ -68,6 +68,10 @@ final class PhamePostEditEngine
|
|||
return $object->getViewURI();
|
||||
}
|
||||
|
||||
protected function getEditorURI() {
|
||||
return $this->getApplication()->getApplicationURI('post/edit/');
|
||||
}
|
||||
|
||||
protected function buildCustomEditFields($object) {
|
||||
$blog_phid = $object->getBlog()->getPHID();
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ final class PhamePostMailReceiver
|
|||
}
|
||||
|
||||
protected function getObjectPattern() {
|
||||
return 'POST[1-9]\d*';
|
||||
return 'J[1-9]\d*';
|
||||
}
|
||||
|
||||
protected function loadObject($pattern, PhabricatorUser $viewer) {
|
||||
|
|
|
@ -32,8 +32,8 @@ final class PhabricatorPhamePostPHIDType extends PhabricatorPHIDType {
|
|||
foreach ($handles as $phid => $handle) {
|
||||
$post = $objects[$phid];
|
||||
$handle->setName($post->getTitle());
|
||||
$handle->setFullName($post->getTitle());
|
||||
$handle->setURI('/phame/post/view/'.$post->getID().'/');
|
||||
$handle->setFullName(pht('Blog Post: ').$post->getTitle());
|
||||
$handle->setURI('/J'.$post->getID());
|
||||
|
||||
if ($post->isArchived()) {
|
||||
$handle->setStatus(PhabricatorObjectHandle::STATUS_CLOSED);
|
||||
|
|
|
@ -108,6 +108,7 @@ final class PhamePostSearchEngine
|
|||
$item = id(new PHUIObjectItemView())
|
||||
->setUser($viewer)
|
||||
->setObject($post)
|
||||
->setObjectName($post->getMonogram())
|
||||
->setHeader($post->getTitle())
|
||||
->setStatusIcon('fa-star')
|
||||
->setHref($post->getViewURI())
|
||||
|
|
19
src/applications/phame/remarkup/PhamePostRemarkupRule.php
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
final class PhamePostRemarkupRule
|
||||
extends PhabricatorObjectRemarkupRule {
|
||||
|
||||
protected function getObjectNamePrefix() {
|
||||
return 'J';
|
||||
}
|
||||
|
||||
protected function loadObjects(array $ids) {
|
||||
$viewer = $this->getEngine()->getConfig('viewer');
|
||||
|
||||
return id(new PhamePostQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs($ids)
|
||||
->execute();
|
||||
}
|
||||
|
||||
}
|
|
@ -18,9 +18,10 @@ final class PhameBlogSite extends PhameSite {
|
|||
}
|
||||
|
||||
public function shouldRequireHTTPS() {
|
||||
// TODO: We should probably provide options here eventually, but for now
|
||||
// just never require HTTPS for external-domain blogs.
|
||||
return false;
|
||||
$full_uri = $this->getBlog()->getDomainFullURI();
|
||||
$full_uri = new PhutilURI($full_uri);
|
||||
|
||||
return ($full_uri->getProtocol() == 'https');
|
||||
}
|
||||
|
||||
public function getPriority() {
|
||||
|
|
|
@ -48,8 +48,8 @@ final class PhameBlog extends PhameDAO
|
|||
'description' => 'text',
|
||||
'domain' => 'text128?',
|
||||
'domainFullURI' => 'text128?',
|
||||
'parentSite' => 'text128',
|
||||
'parentDomain' => 'text128',
|
||||
'parentSite' => 'text128?',
|
||||
'parentDomain' => 'text128?',
|
||||
'status' => 'text32',
|
||||
'mailKey' => 'bytes20',
|
||||
'profileImagePHID' => 'phid?',
|
||||
|
|
|
@ -51,6 +51,10 @@ final class PhamePost extends PhameDAO
|
|||
return $this->assertAttached($this->blog);
|
||||
}
|
||||
|
||||
public function getMonogram() {
|
||||
return 'J'.$this->getID();
|
||||
}
|
||||
|
||||
public function getLiveURI() {
|
||||
$blog = $this->getBlog();
|
||||
$is_draft = $this->isDraft();
|
||||
|
|
|
@ -14,6 +14,7 @@ final class PHUIHandleView
|
|||
private $handleList;
|
||||
private $handlePHID;
|
||||
private $asTag;
|
||||
private $asText;
|
||||
private $useShortName;
|
||||
private $showHovercard;
|
||||
|
||||
|
@ -32,6 +33,11 @@ final class PHUIHandleView
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function setAsText($as_text) {
|
||||
$this->asText = $as_text;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setUseShortName($short) {
|
||||
$this->useShortName = $short;
|
||||
return $this;
|
||||
|
@ -55,6 +61,10 @@ final class PHUIHandleView
|
|||
return $tag;
|
||||
}
|
||||
|
||||
if ($this->asText) {
|
||||
return $handle->getLinkName();
|
||||
}
|
||||
|
||||
if ($this->useShortName) {
|
||||
$name = $handle->getName();
|
||||
} else {
|
||||
|
|
|
@ -59,7 +59,7 @@ final class PhabricatorPhrictionApplication extends PhabricatorApplication {
|
|||
'new/' => 'PhrictionNewController',
|
||||
'move/(?P<id>[1-9]\d*)/' => 'PhrictionMoveController',
|
||||
|
||||
'preview/(?P<slug>.+/)' => 'PhrictionMarkupPreviewController',
|
||||
'preview/(?P<slug>.*/)' => 'PhrictionMarkupPreviewController',
|
||||
'diff/(?P<id>[1-9]\d*)/' => 'PhrictionDiffController',
|
||||
),
|
||||
);
|
||||
|
|
|
@ -174,12 +174,13 @@ final class PhabricatorRepositoryDiscoveryEngine
|
|||
continue;
|
||||
}
|
||||
|
||||
// In Git, it's possible to tag a tag. We just skip these, we'll discover
|
||||
// them when we process the target tag. See T11180.
|
||||
// In Git, it's possible to tag anything. We just skip tags that don't
|
||||
// point to a commit. See T11301.
|
||||
$fields = $ref->getRawFields();
|
||||
$ref_type = idx($fields, 'objecttype');
|
||||
$tag_type = idx($fields, '*objecttype');
|
||||
if ($tag_type == 'tag') {
|
||||
$this->log(pht('Skipping, this is a tag of a tag.'));
|
||||
if ($ref_type != 'commit' && $tag_type != 'commit') {
|
||||
$this->log(pht('Skipping, this is not a commit.'));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -759,6 +760,13 @@ final class PhabricatorRepositoryDiscoveryEngine
|
|||
'repositoryPHID = %s',
|
||||
$repository->getPHID());
|
||||
|
||||
// If we don't have any refs to update, bail out before building a graph
|
||||
// stream. In particular, this improves behavior in empty repositories,
|
||||
// where `git log` exits with an error.
|
||||
if (!$old_refs) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We can share a single graph stream across all the checks we need to do.
|
||||
$stream = new PhabricatorGitGraphStream($repository);
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ final class PhabricatorWorkingCopyDiscoveryTestCase
|
|||
$this->assertEqual(
|
||||
array(
|
||||
'763d4ab372445551c95fb5cccd1a7a223f5b2ac8',
|
||||
'41fa35914aa19c1aa6e57004d9745c05929c3563',
|
||||
),
|
||||
mpull($refs, 'getIdentifier'));
|
||||
}
|
||||
|
|
|
@ -2400,6 +2400,12 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
|||
->setKey('status')
|
||||
->setType('string')
|
||||
->setDescription(pht('Active or inactive status.')),
|
||||
id(new PhabricatorConduitSearchFieldSpecification())
|
||||
->setKey('isImporting')
|
||||
->setType('bool')
|
||||
->setDescription(
|
||||
pht(
|
||||
'True if the repository is importing initial commits.')),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2410,6 +2416,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
|||
'callsign' => $this->getCallsign(),
|
||||
'shortName' => $this->getRepositorySlug(),
|
||||
'status' => $this->getStatus(),
|
||||
'isImporting' => (bool)$this->isImporting(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,6 @@ final class PhabricatorProfilePanelPHIDType
|
|||
$config = $objects[$phid];
|
||||
|
||||
$handle->setName(pht('Profile Panel'));
|
||||
$handle->setURI($config->getURI());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ final class PhabricatorUserPreferencesSearchEngine
|
|||
}
|
||||
|
||||
protected function getURI($path) {
|
||||
return '/settings/list/'.$path;
|
||||
return '/settings/'.$path;
|
||||
}
|
||||
|
||||
protected function getBuiltinQueryNames() {
|
||||
|
|
|
@ -90,7 +90,7 @@ final class PhabricatorTokenGiveController extends PhabricatorTokenController {
|
|||
$aural,
|
||||
$token->renderIcon(),
|
||||
));
|
||||
if ((++$ii % 4) == 0) {
|
||||
if ((++$ii % 6) == 0) {
|
||||
$buttons[] = phutil_tag('br');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,14 @@ final class PhabricatorTokenQuery
|
|||
array('misc-2', pht('Evil Spooky Haunted Tree')),
|
||||
array('misc-3', pht('Baby Tequila')),
|
||||
array('misc-4', pht('The World Burns')),
|
||||
array('emoji-1', pht('100')),
|
||||
array('emoji-2', pht('Party Time')),
|
||||
array('emoji-3', pht('Y So Serious')),
|
||||
array('emoji-4', pht('Dat Boi')),
|
||||
array('emoji-5', pht('Cup of Joe')),
|
||||
array('emoji-6', pht('Hungry Hippo')),
|
||||
array('emoji-7', pht('Burninate')),
|
||||
array('emoji-8', pht('Pirate Logo')),
|
||||
);
|
||||
|
||||
$type = PhabricatorTokenTokenPHIDType::TYPECONST;
|
||||
|
|
|
@ -98,6 +98,15 @@ abstract class PhabricatorModularTransaction
|
|||
return parent::getTitle();
|
||||
}
|
||||
|
||||
public function getTitleForMail() {
|
||||
$old_target = $this->getRenderingTarget();
|
||||
$new_target = self::TARGET_TEXT;
|
||||
$this->setRenderingTarget($new_target);
|
||||
$title = $this->getTitle();
|
||||
$this->setRenderingTarget($old_target);
|
||||
return $title;
|
||||
}
|
||||
|
||||
final public function getTitleForFeed() {
|
||||
$title = $this->getTransactionImplementation()->getTitleForFeed();
|
||||
if ($title !== null) {
|
||||
|
@ -116,6 +125,11 @@ abstract class PhabricatorModularTransaction
|
|||
return parent::getColor();
|
||||
}
|
||||
|
||||
public function attachViewer(PhabricatorUser $viewer) {
|
||||
$this->getTransactionImplementation()->setViewer($viewer);
|
||||
return parent::attachViewer($viewer);
|
||||
}
|
||||
|
||||
final public function hasChangeDetails() {
|
||||
if ($this->getTransactionImplementation()->hasChangeDetailView()) {
|
||||
return true;
|
||||
|
|
|
@ -129,6 +129,32 @@ abstract class PhabricatorModularTransactionType
|
|||
return $this->getStorage()->renderHandleLink($object_phid);
|
||||
}
|
||||
|
||||
final protected function renderHandle($phid) {
|
||||
$viewer = $this->getViewer();
|
||||
$display = $viewer->renderHandle($phid);
|
||||
|
||||
$rendering_target = $this->getStorage()->getRenderingTarget();
|
||||
if ($rendering_target == PhabricatorApplicationTransaction::TARGET_TEXT) {
|
||||
$display->setAsText(true);
|
||||
}
|
||||
|
||||
return $display;
|
||||
}
|
||||
|
||||
final protected function renderValue($value) {
|
||||
$rendering_target = $this->getStorage()->getRenderingTarget();
|
||||
if ($rendering_target == PhabricatorApplicationTransaction::TARGET_TEXT) {
|
||||
return sprintf('"%s"', $value);
|
||||
}
|
||||
|
||||
return phutil_tag(
|
||||
'span',
|
||||
array(
|
||||
'class' => 'phui-timeline-value',
|
||||
),
|
||||
$value);
|
||||
}
|
||||
|
||||
final protected function newError($title, $message, $xaction = null) {
|
||||
return new PhabricatorApplicationTransactionValidationError(
|
||||
$this->getTransactionTypeConstant(),
|
||||
|
|
|
@ -70,7 +70,8 @@ Beyond an operating system, you will need **a webserver**.
|
|||
You will also need:
|
||||
|
||||
- **MySQL**: You need MySQL. We strongly recommend MySQL 5.5 or newer.
|
||||
- **PHP**: You need PHP 5.2 or newer.
|
||||
- **PHP**: You need PHP 5.2 or newer, but note that PHP 7 is
|
||||
**not supported**.
|
||||
|
||||
You'll probably also need a **domain name**. In particular, you should read this
|
||||
note:
|
||||
|
|
68
src/docs/user/userguide/diffusion_existing.diviner
Normal file
|
@ -0,0 +1,68 @@
|
|||
@title Diffusion User Guide: Existing Repositories
|
||||
@group userguide
|
||||
|
||||
Quick guide for importing or observing existing repositories.
|
||||
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
If you have an existing repository, you can observe or import it into
|
||||
Diffusion.
|
||||
|
||||
Observing a repository creates a read-only copy in Phabricator that is kept
|
||||
up to date by continuously importing new changes.
|
||||
|
||||
Importing a repository creates a read-write copy.
|
||||
|
||||
This document is a quick guide to getting started. For an overview of
|
||||
Diffusion, see @{article:Diffusion User Guide}. For a more detailed guide
|
||||
about managing repositories and URIs in Diffusion, see
|
||||
@{article:Diffusion User Guide: URIs}.
|
||||
|
||||
|
||||
Observing Repositories
|
||||
======================
|
||||
|
||||
To observe an existing repository:
|
||||
|
||||
- Create a repository in Diffusion, but do not activate it yet.
|
||||
- Add the URI for the existing repository you wish to observe in the
|
||||
**URIs** section, in **Observe** mode.
|
||||
- Activate the repository in Diffusion.
|
||||
|
||||
This creates a read-only copy of the repository in Phabricator. Phabricator
|
||||
will keep its copy in sync with the remote by periodically polling the remote
|
||||
for changes.
|
||||
|
||||
For more details, see @{article:Diffusion User Guide: URIs}.
|
||||
|
||||
|
||||
Importing Repositories
|
||||
======================
|
||||
|
||||
There are two primary ways to import an existing repository:
|
||||
|
||||
**Observe First**: In Git or Mercurial, you can observe the repository first.
|
||||
Once the import completes, disable the **Observe** URI to automatically convert
|
||||
it into a hosted repository.
|
||||
|
||||
**Push to Empty Repository**: Create an activate an empty repository, then push
|
||||
all of your changes to empty the repository.
|
||||
|
||||
In Git and Mercurial, you can do this with `git push` or `hg push`.
|
||||
|
||||
In Subversion, you can do this with `svnsync`.
|
||||
|
||||
For more details, see @{article:Diffusion User Guide: URIs}.
|
||||
|
||||
|
||||
Next Steps
|
||||
==========
|
||||
|
||||
Continue by:
|
||||
|
||||
- reading an overview of Diffusion in
|
||||
@{article:Diffusion User Guide}; or
|
||||
- learning more about managing remote repository URIs in
|
||||
@{article:Diffusion User Guide: URIs}.
|
|
@ -135,10 +135,6 @@ Import a Repository
|
|||
If you have an existing repository that you want to move so it is hosted on
|
||||
Phabricator, there are three ways to do it:
|
||||
|
||||
**Push Everything**: //(Git, Mercurial)// Create a new empty hosted repository
|
||||
according to the instructions above. Once the empty repository initializes,
|
||||
push your entire existing repository to it.
|
||||
|
||||
**Observe First**: //(Git, Mercurial)// Observe the existing repository first,
|
||||
according to the instructions above. Once Phabricator's copy of the repository
|
||||
is fully synchronized, change the **I/O Type** for the **Observe** URI to
|
||||
|
@ -149,6 +145,12 @@ writable, and you can begin pushing to it. If you've adjusted URI
|
|||
configuration away from the defaults, you may need to set at least one URI
|
||||
to **Read/Write** mode so you can push to it.
|
||||
|
||||
**Push Everything**: //(Git, Mercurial, Subversion)// Create a new empty hosted
|
||||
repository according to the instructions above. Once the empty repository
|
||||
initializes, push your entire existing repository to it.
|
||||
|
||||
In Subversion, you can do this with the `svnsync` tool.
|
||||
|
||||
**Copy on Disk**: //(Git, Mercurial, Subversion)// Create a new empty hosted
|
||||
repository according to the instructions above, but do not activate it yet.
|
||||
|
||||
|
|
|
@ -1,59 +1,121 @@
|
|||
@title Phame User Guide
|
||||
@group userguide
|
||||
|
||||
Journal about your thoughts and feelings. Share with others. Profit.
|
||||
Phame is a blogging platform.
|
||||
|
||||
= Overview =
|
||||
Overview
|
||||
========
|
||||
|
||||
IMPORTANT: Phame is a prototype application.
|
||||
Phame is a simple platform for writing blogs and blog posts. Content published
|
||||
through Phame is integrated with other Phabricator applications (like Feed,
|
||||
Herald and Dashboards).
|
||||
|
||||
Phame is a simple blogging platform. You can write drafts which only you can
|
||||
see. Later, you can publish these drafts as posts which anyone who can access
|
||||
the Phabricator instance can see. You can also add posts to blogs to increase
|
||||
your distribution.
|
||||
You can use Phame to write and publish posts on any topic. You might use it to
|
||||
make announcements, hold discussions, or provide progress updates about a
|
||||
project.
|
||||
|
||||
Overall, Phame is intended to help an individual spread their message. As
|
||||
such, pertinent design decisions skew towards favoring the individual
|
||||
rather than the collective.
|
||||
In the upstream, we use several Phame blogs to discuss changes to Phabricator,
|
||||
make company announcements, photograph food, and provide visionary thought
|
||||
leadership.
|
||||
|
||||
= Drafts =
|
||||
|
||||
Drafts are completely private so draft away.
|
||||
Blogs
|
||||
=====
|
||||
|
||||
= Posts =
|
||||
To get started with Phame, create a blog. Blogs can be personal or edited
|
||||
by a group: the **Editable By** policy controls who is allowed to write new
|
||||
posts.
|
||||
|
||||
Posts are accessible to anyone who has access to the Phabricator instance.
|
||||
You can provide a title, subtitle, and description to help users understand
|
||||
the role and purpose of the blog.
|
||||
|
||||
= Blogs =
|
||||
After creating a blog, you can optionally provide a header image (a large
|
||||
image shown on the main blog page, like a beautiful photograph of food) and
|
||||
a picture (a small logo or profile image shown in various places in the UI to
|
||||
help identify the blog).
|
||||
|
||||
Blogs are collections of posts. Each blog has associated metadata like
|
||||
a name, description, and set of bloggers who can add posts to the blog.
|
||||
Each blogger can also edit metadata about the blog and delete the blog
|
||||
outright.
|
||||
Blogs can also be hosted externally. See "External Blogs", below, for
|
||||
more information.
|
||||
|
||||
NOTE: removing a blogger from a given blog does not remove their posts that
|
||||
are already associated with the blog. Rather, it removes their ability to edit
|
||||
metadata about and add posts to the blog.
|
||||
|
||||
Blogs can be useful for powering external websites, like
|
||||
Posts
|
||||
=====
|
||||
|
||||
blog.yourcompany.com
|
||||
After creating a blog, you're ready to write your first post. You can navigate
|
||||
to the blog and choose {nav Write Post} to get started.
|
||||
|
||||
by making pertinent configuration changes with your DNS authority and
|
||||
Phabricator instance. For the Phabricator instance, you must
|
||||
Posts have a **Visibility** field which controls who can see them. The options
|
||||
are:
|
||||
|
||||
- Enable `policy.allow-public` in Phabricator configuration.
|
||||
- Configure the blog to have the view policy `public`.
|
||||
- **Published**: Anyone who can see the blog will be able to read the post.
|
||||
- **Draft**: Allows you to work on posts before publishing them. Only users
|
||||
who can edit the blog will be able to see the post.
|
||||
- **Archived**: Allows you to remove old posts. Only users who can edit
|
||||
the blog will be able to see the post, and it won't appear in the pending
|
||||
drafts list.
|
||||
|
||||
For your DNS authority, simply point the pertinent domain name at your
|
||||
Phabricator instance. e.g. by IP address.
|
||||
After publishing a post, it will appear on the blog and on the Phame home page
|
||||
for all users who can see it.
|
||||
|
||||
= Comment Widgets =
|
||||
|
||||
Phame supports comment widgets from Facebook and Disqus. The administrator
|
||||
of the Phabricator instance must properly configure Phabricator to enable
|
||||
this functionality.
|
||||
Using Phame With Other Applications
|
||||
===================================
|
||||
|
||||
A given comment widget is tied 1:1 with a given post. This means the same
|
||||
instance of a given comment widget will appear for a given post regardless
|
||||
of whether that post is being viewed in the context of a blog.
|
||||
Phame integrates with other Phabricator applications, so you can do a few
|
||||
interesting things:
|
||||
|
||||
**Dashboards**: You can create a dashboard panel which shows posts on a
|
||||
particular blog, then put the panel on the homepage or a custom dashboard.
|
||||
|
||||
This is an easy way to create a list of recent announcements.
|
||||
|
||||
**Herald**: You can use Herald rules to make sure you get notified whenever
|
||||
your favorite author publishes a new post.
|
||||
|
||||
**Remarkup**: You can reference a blog post in any other application using the
|
||||
`J123` monogram for the post, or embed a more detailed link with `{J123}`.
|
||||
|
||||
(We ran out of letters a while ago, but thinking about **j**ournal may be
|
||||
helpful in remembering this.)
|
||||
|
||||
|
||||
External Blogs
|
||||
==============
|
||||
|
||||
WARNING: This feature is still a prototype and has some known issues.
|
||||
|
||||
You can host a Phame blog on an external domain, like `blog.mycompany.com`. The
|
||||
Phacility corporate blog is an example of an external Phame blog:
|
||||
|
||||
> https://blog.phacility.com/
|
||||
|
||||
External blogs are public (they do not require login) and are only supported if
|
||||
your Phabricator install is also public. You can make an install public by
|
||||
adjusting `policy.allow-public` in Config, but make sure you understand the
|
||||
effects of adjusting this setting before touching it.
|
||||
|
||||
Once you've made your install public, configure the blog that you want to host
|
||||
like this:
|
||||
|
||||
- **View Policy**: Set the "View Policy" for the blog to "Public". Blogs must
|
||||
have a public view policy to be served from an external domain.
|
||||
- **Full Domain URI**: Set this to the full URI of your external domain,
|
||||
like `https://blog.mycompany.com/`. When users visit this URI, Phabricator
|
||||
will serve the blog to them.
|
||||
|
||||
To configure the blog's navigation breadcrumbs so that it links back to the
|
||||
right parent site, set these options:
|
||||
|
||||
- **Parent Site Name**: Put the parent site name here (like "MyCompany").
|
||||
- **Parent Site URI**: Put the parent site URI here (like
|
||||
`https://www.mycompany.com`).
|
||||
|
||||
Configuring these options will add a new breadcrumb to the navigation to let
|
||||
users return to the blog's parent site. It will look something like this:
|
||||
|
||||
- {nav My Company > Blog Name}
|
||||
|
||||
Finally, configure DNS for `blog.mycompany.com` to point at Phabricator.
|
||||
|
||||
If everything is set up properly, visiting `blog.mycompany.com` should now
|
||||
serve your blog.
|
||||
|
|
|
@ -12,6 +12,7 @@ final class PhabricatorDatabaseRef
|
|||
const REPLICATION_MASTER_REPLICA = 'master-replica';
|
||||
const REPLICATION_REPLICA_NONE = 'replica-none';
|
||||
const REPLICATION_SLOW = 'replica-slow';
|
||||
const REPLICATION_NOT_REPLICATING = 'not-replicating';
|
||||
|
||||
const KEY_REFS = 'cluster.db.refs';
|
||||
const KEY_INDIVIDUAL = 'cluster.db.individual';
|
||||
|
@ -196,13 +197,18 @@ final class PhabricatorDatabaseRef
|
|||
self::REPLICATION_REPLICA_NONE => array(
|
||||
'icon' => 'fa-download',
|
||||
'color' => 'red',
|
||||
'label' => pht('Not Replicating'),
|
||||
'label' => pht('Not A Replica'),
|
||||
),
|
||||
self::REPLICATION_SLOW => array(
|
||||
'icon' => 'fa-hourglass',
|
||||
'color' => 'red',
|
||||
'label' => pht('Slow Replication'),
|
||||
),
|
||||
self::REPLICATION_NOT_REPLICATING => array(
|
||||
'icon' => 'fa-exclamation-triangle',
|
||||
'color' => 'red',
|
||||
'label' => pht('Not Replicating'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -330,14 +336,19 @@ final class PhabricatorDatabaseRef
|
|||
}
|
||||
|
||||
if ($is_replica) {
|
||||
$latency = (int)idx($replica_status, 'Seconds_Behind_Master');
|
||||
$ref->setReplicaDelay($latency);
|
||||
if ($latency > 30) {
|
||||
$ref->setReplicaStatus(self::REPLICATION_SLOW);
|
||||
$ref->setReplicaMessage(
|
||||
pht(
|
||||
'This replica is lagging far behind the master. Data is at '.
|
||||
'risk!'));
|
||||
$latency = idx($replica_status, 'Seconds_Behind_Master');
|
||||
if (!strlen($latency)) {
|
||||
$ref->setReplicaStatus(self::REPLICATION_NOT_REPLICATING);
|
||||
} else {
|
||||
$latency = (int)$latency;
|
||||
$ref->setReplicaDelay($latency);
|
||||
if ($latency > 30) {
|
||||
$ref->setReplicaStatus(self::REPLICATION_SLOW);
|
||||
$ref->setReplicaMessage(
|
||||
pht(
|
||||
'This replica is lagging far behind the master. Data is at '.
|
||||
'risk!'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,6 +54,8 @@ final class DifferentialRevisionGraph
|
|||
$link = $viewer->renderHandle($phid);
|
||||
}
|
||||
|
||||
$link = AphrontTableView::renderSingleDisplayLine($link);
|
||||
|
||||
return array(
|
||||
$trace,
|
||||
$status,
|
||||
|
|
|
@ -67,6 +67,8 @@ final class ManiphestTaskGraph
|
|||
$link = $viewer->renderHandle($phid);
|
||||
}
|
||||
|
||||
$link = AphrontTableView::renderSingleDisplayLine($link);
|
||||
|
||||
return array(
|
||||
$trace,
|
||||
$status,
|
||||
|
|
|
@ -3,9 +3,13 @@
|
|||
final class PhabricatorInternationalizationManagementExtractWorkflow
|
||||
extends PhabricatorInternationalizationManagementWorkflow {
|
||||
|
||||
const CACHE_VERSION = 1;
|
||||
|
||||
protected function didConstruct() {
|
||||
$this
|
||||
->setName('extract')
|
||||
->setExamples(
|
||||
'**extract** [__options__] __library__')
|
||||
->setSynopsis(pht('Extract translatable strings.'))
|
||||
->setArguments(
|
||||
array(
|
||||
|
@ -13,44 +17,138 @@ final class PhabricatorInternationalizationManagementExtractWorkflow
|
|||
'name' => 'paths',
|
||||
'wildcard' => true,
|
||||
),
|
||||
array(
|
||||
'name' => 'clean',
|
||||
'help' => pht('Drop caches before extracting strings. Slow!'),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
public function execute(PhutilArgumentParser $args) {
|
||||
$console = PhutilConsole::getConsole();
|
||||
$paths = $args->getArg('paths');
|
||||
|
||||
$futures = array();
|
||||
$paths = $args->getArg('paths');
|
||||
if (!$paths) {
|
||||
$paths = array(getcwd());
|
||||
}
|
||||
|
||||
$targets = array();
|
||||
foreach ($paths as $path) {
|
||||
$root = Filesystem::resolvePath($path);
|
||||
$path_files = id(new FileFinder($root))
|
||||
->withType('f')
|
||||
->withSuffix('php')
|
||||
->find();
|
||||
|
||||
foreach ($path_files as $file) {
|
||||
$full_path = $root.DIRECTORY_SEPARATOR.$file;
|
||||
$data = Filesystem::readFile($full_path);
|
||||
$futures[$full_path] = PhutilXHPASTBinary::getParserFuture($data);
|
||||
if (!Filesystem::pathExists($root) || !is_dir($root)) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Path "%s" does not exist, or is not a directory.',
|
||||
$path));
|
||||
}
|
||||
|
||||
$libraries = id(new FileFinder($path))
|
||||
->withPath('*/__phutil_library_init__.php')
|
||||
->find();
|
||||
if (!$libraries) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Path "%s" contains no libphutil libraries.',
|
||||
$path));
|
||||
}
|
||||
|
||||
foreach ($libraries as $library) {
|
||||
$targets[] = Filesystem::resolvePath(dirname($library)).'/';
|
||||
}
|
||||
}
|
||||
|
||||
$console->writeErr(
|
||||
"%s\n",
|
||||
pht('Found %s file(s)...', phutil_count($futures)));
|
||||
$targets = array_unique($targets);
|
||||
|
||||
$results = array();
|
||||
foreach ($targets as $library) {
|
||||
echo tsprintf(
|
||||
"**<bg:blue> %s </bg>** %s\n",
|
||||
pht('EXTRACT'),
|
||||
pht(
|
||||
'Extracting "%s"...',
|
||||
Filesystem::readablePath($library)));
|
||||
|
||||
$this->extractLibrary($library);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function extractLibrary($root) {
|
||||
$files = $this->loadLibraryFiles($root);
|
||||
$cache = $this->readCache($root);
|
||||
|
||||
$modified = $this->getModifiedFiles($files, $cache);
|
||||
$cache['files'] = $files;
|
||||
|
||||
if ($modified) {
|
||||
echo tsprintf(
|
||||
"**<bg:blue> %s </bg>** %s\n",
|
||||
pht('MODIFIED'),
|
||||
pht(
|
||||
'Found %s modified file(s) (of %s total).',
|
||||
phutil_count($modified),
|
||||
phutil_count($files)));
|
||||
|
||||
$old_strings = idx($cache, 'strings');
|
||||
$old_strings = array_select_keys($old_strings, $files);
|
||||
$new_strings = $this->extractFiles($root, $modified);
|
||||
$all_strings = $new_strings + $old_strings;
|
||||
$cache['strings'] = $all_strings;
|
||||
|
||||
$this->writeStrings($root, $all_strings);
|
||||
} else {
|
||||
echo tsprintf(
|
||||
"**<bg:blue> %s </bg>** %s\n",
|
||||
pht('NOT MODIFIED'),
|
||||
pht('Strings for this library are already up to date.'));
|
||||
}
|
||||
|
||||
$cache = id(new PhutilJSON())->encodeFormatted($cache);
|
||||
$this->writeCache($root, 'i18n_files.json', $cache);
|
||||
}
|
||||
|
||||
private function getModifiedFiles(array $files, array $cache) {
|
||||
$known = idx($cache, 'files', array());
|
||||
$known = array_fuse($known);
|
||||
|
||||
$modified = array();
|
||||
foreach ($files as $file => $hash) {
|
||||
|
||||
if (isset($known[$hash])) {
|
||||
continue;
|
||||
}
|
||||
$modified[$file] = $hash;
|
||||
}
|
||||
|
||||
return $modified;
|
||||
}
|
||||
|
||||
private function extractFiles($root_path, array $files) {
|
||||
$hashes = array();
|
||||
|
||||
$futures = array();
|
||||
foreach ($files as $file => $hash) {
|
||||
$full_path = $root_path.DIRECTORY_SEPARATOR.$file;
|
||||
$data = Filesystem::readFile($full_path);
|
||||
$futures[$full_path] = PhutilXHPASTBinary::getParserFuture($data);
|
||||
|
||||
$hashes[$full_path] = $hash;
|
||||
}
|
||||
|
||||
$bar = id(new PhutilConsoleProgressBar())
|
||||
->setTotal(count($futures));
|
||||
|
||||
$messages = array();
|
||||
$results = array();
|
||||
|
||||
$futures = id(new FutureIterator($futures))
|
||||
->limit(8);
|
||||
foreach ($futures as $full_path => $future) {
|
||||
$bar->update(1);
|
||||
|
||||
$hash = $hashes[$full_path];
|
||||
|
||||
try {
|
||||
$tree = XHPASTTree::newFromDataAndResolvedExecFuture(
|
||||
Filesystem::readFile($full_path),
|
||||
|
@ -67,24 +165,27 @@ final class PhabricatorInternationalizationManagementExtractWorkflow
|
|||
$calls = $root->selectDescendantsOfType('n_FUNCTION_CALL');
|
||||
foreach ($calls as $call) {
|
||||
$name = $call->getChildByIndex(0)->getConcreteString();
|
||||
if ($name == 'pht') {
|
||||
$params = $call->getChildByIndex(1, 'n_CALL_PARAMETER_LIST');
|
||||
$string_node = $params->getChildByIndex(0);
|
||||
$string_line = $string_node->getLineNumber();
|
||||
try {
|
||||
$string_value = $string_node->evalStatic();
|
||||
if ($name != 'pht') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$results[$string_value][] = array(
|
||||
'file' => Filesystem::readablePath($full_path),
|
||||
'line' => $string_line,
|
||||
);
|
||||
} catch (Exception $ex) {
|
||||
$messages[] = pht(
|
||||
'WARNING: Failed to evaluate pht() call on line %d in "%s": %s',
|
||||
$call->getLineNumber(),
|
||||
$full_path,
|
||||
$ex->getMessage());
|
||||
}
|
||||
$params = $call->getChildByIndex(1, 'n_CALL_PARAMETER_LIST');
|
||||
$string_node = $params->getChildByIndex(0);
|
||||
$string_line = $string_node->getLineNumber();
|
||||
try {
|
||||
$string_value = $string_node->evalStatic();
|
||||
|
||||
$results[$hash][] = array(
|
||||
'string' => $string_value,
|
||||
'file' => Filesystem::readablePath($full_path, $root_path),
|
||||
'line' => $string_line,
|
||||
);
|
||||
} catch (Exception $ex) {
|
||||
$messages[] = pht(
|
||||
'WARNING: Failed to evaluate pht() call on line %d in "%s": %s',
|
||||
$call->getLineNumber(),
|
||||
$full_path,
|
||||
$ex->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,28 +194,109 @@ final class PhabricatorInternationalizationManagementExtractWorkflow
|
|||
$bar->done();
|
||||
|
||||
foreach ($messages as $message) {
|
||||
$console->writeErr("%s\n", $message);
|
||||
echo tsprintf(
|
||||
"**<bg:yellow> %s </bg>** %s\n",
|
||||
pht('WARNING'),
|
||||
$message);
|
||||
}
|
||||
|
||||
ksort($results);
|
||||
return $results;
|
||||
}
|
||||
|
||||
$out = array();
|
||||
$out[] = '<?php';
|
||||
$out[] = '// @no'.'lint';
|
||||
$out[] = 'return array(';
|
||||
foreach ($results as $string => $locations) {
|
||||
foreach ($locations as $location) {
|
||||
$out[] = ' // '.$location['file'].':'.$location['line'];
|
||||
private function writeStrings($root, array $strings) {
|
||||
$map = array();
|
||||
foreach ($strings as $hash => $string_list) {
|
||||
foreach ($string_list as $string_info) {
|
||||
$map[$string_info['string']]['uses'][] = array(
|
||||
'file' => $string_info['file'],
|
||||
'line' => $string_info['line'],
|
||||
);
|
||||
}
|
||||
$out[] = " '".addcslashes($string, "\0..\37\\'\177..\377")."' => null,";
|
||||
$out[] = null;
|
||||
}
|
||||
$out[] = ');';
|
||||
$out[] = null;
|
||||
|
||||
echo implode("\n", $out);
|
||||
ksort($map);
|
||||
|
||||
return 0;
|
||||
$json = id(new PhutilJSON())->encodeFormatted($map);
|
||||
$this->writeCache($root, 'i18n_strings.json', $json);
|
||||
}
|
||||
|
||||
private function loadLibraryFiles($root) {
|
||||
$files = id(new FileFinder($root))
|
||||
->withType('f')
|
||||
->withSuffix('php')
|
||||
->excludePath('*/.*')
|
||||
->setGenerateChecksums(true)
|
||||
->find();
|
||||
|
||||
$map = array();
|
||||
foreach ($files as $file => $hash) {
|
||||
$file = Filesystem::readablePath($file, $root);
|
||||
$file = ltrim($file, '/');
|
||||
|
||||
if (dirname($file) == '.') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dirname($file) == 'extensions') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$map[$file] = md5($hash.$file);
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
private function readCache($root) {
|
||||
$path = $this->getCachePath($root, 'i18n_files.json');
|
||||
|
||||
$default = array(
|
||||
'version' => self::CACHE_VERSION,
|
||||
'files' => array(),
|
||||
'strings' => array(),
|
||||
);
|
||||
|
||||
if ($this->getArgv()->getArg('clean')) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
if (!Filesystem::pathExists($path)) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
try {
|
||||
$data = Filesystem::readFile($path);
|
||||
} catch (Exception $ex) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
try {
|
||||
$cache = phutil_json_decode($data);
|
||||
} catch (PhutilJSONParserException $e) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
$version = idx($cache, 'version');
|
||||
if ($version !== self::CACHE_VERSION) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
return $cache;
|
||||
}
|
||||
|
||||
private function writeCache($root, $file, $data) {
|
||||
$path = $this->getCachePath($root, $file);
|
||||
|
||||
$cache_dir = dirname($path);
|
||||
if (!Filesystem::pathExists($cache_dir)) {
|
||||
Filesystem::createDirectory($cache_dir, 0755, true);
|
||||
}
|
||||
|
||||
Filesystem::writeFile($path, $data);
|
||||
}
|
||||
|
||||
private function getCachePath($root, $to_file) {
|
||||
return $root.'/.cache/'.$to_file;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -186,9 +186,17 @@ final class PhabricatorMainMenuView extends AphrontView {
|
|||
}
|
||||
|
||||
$result = $search;
|
||||
$keyboard_config['searchID'] = $search->getID();
|
||||
}
|
||||
|
||||
Javelin::initBehavior('phabricator-keyboard-shortcuts', $keyboard_config);
|
||||
$keyboard_config['pht'] = array(
|
||||
'/' => pht('Give keyboard focus to the search box.'),
|
||||
'?' => pht('Show keyboard shortcut help for the current page.'),
|
||||
);
|
||||
|
||||
Javelin::initBehavior(
|
||||
'phabricator-keyboard-shortcuts',
|
||||
$keyboard_config);
|
||||
|
||||
if ($result) {
|
||||
$result = id(new PHUIListItemView())
|
||||
|
|
|
@ -17,6 +17,7 @@ final class PHUIFeedStoryView extends AphrontView {
|
|||
private $chronologicalKey;
|
||||
private $tags;
|
||||
private $authorIcon;
|
||||
private $showTimestamp = true;
|
||||
|
||||
public function setTags($tags) {
|
||||
$this->tags = $tags;
|
||||
|
@ -97,6 +98,15 @@ final class PHUIFeedStoryView extends AphrontView {
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function setShowTimestamp($show_timestamp) {
|
||||
$this->showTimestamp = $show_timestamp;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getShowTimestamp() {
|
||||
return $this->showTimestamp;
|
||||
}
|
||||
|
||||
public function addProject($project) {
|
||||
$this->projects[] = $project;
|
||||
return $this;
|
||||
|
@ -136,20 +146,25 @@ final class PHUIFeedStoryView extends AphrontView {
|
|||
if (!$this->viewed) {
|
||||
$classes[] = 'phabricator-notification-unread';
|
||||
}
|
||||
if ($this->epoch) {
|
||||
if ($user) {
|
||||
$foot = phabricator_datetime($this->epoch, $user);
|
||||
$foot = phutil_tag(
|
||||
'span',
|
||||
array(
|
||||
'class' => 'phabricator-notification-date',
|
||||
),
|
||||
$foot);
|
||||
|
||||
if ($this->getShowTimestamp()) {
|
||||
if ($this->epoch) {
|
||||
if ($user) {
|
||||
$foot = phabricator_datetime($this->epoch, $user);
|
||||
$foot = phutil_tag(
|
||||
'span',
|
||||
array(
|
||||
'class' => 'phabricator-notification-date',
|
||||
),
|
||||
$foot);
|
||||
} else {
|
||||
$foot = null;
|
||||
}
|
||||
} else {
|
||||
$foot = null;
|
||||
$foot = pht('No time specified.');
|
||||
}
|
||||
} else {
|
||||
$foot = pht('No time specified.');
|
||||
$foot = null;
|
||||
}
|
||||
|
||||
return javelin_tag(
|
||||
|
|
|
@ -29,8 +29,18 @@ final class PHUIListItemView extends AphrontTagView {
|
|||
private $indented;
|
||||
private $hideInApplicationMenu;
|
||||
private $icons = array();
|
||||
private $openInNewWindow = false;
|
||||
|
||||
public function setHideInApplicationMenu($hide) {
|
||||
public function setOpenInNewWindow($open_in_new_window) {
|
||||
$this->openInNewWindow = $open_in_new_window;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getOpenInNewWindow() {
|
||||
return $this->openInNewWindow;
|
||||
}
|
||||
|
||||
public function setHideInApplicationMenu($hide) {
|
||||
$this->hideInApplicationMenu = $hide;
|
||||
return $this;
|
||||
}
|
||||
|
@ -294,6 +304,7 @@ final class PHUIListItemView extends AphrontTagView {
|
|||
'class' => implode(' ', $classes),
|
||||
'meta' => $meta,
|
||||
'sigil' => $sigil,
|
||||
'target' => $this->getOpenInNewWindow() ? '_blank' : null,
|
||||
),
|
||||
array(
|
||||
$aural,
|
||||
|
|
|
@ -308,3 +308,7 @@
|
|||
font-family: 'Aleo', {$fontfamily};
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.phame-comment-view .aphront-form-control.aphront-form-control-select {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -256,6 +256,11 @@
|
|||
color: {$lightgreytext};
|
||||
}
|
||||
|
||||
.phui-timeline-title .phui-timeline-value {
|
||||
font-style: italic;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.device-desktop .phui-timeline-extra {
|
||||
float: right;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ only screen and (-webkit-min-device-pixel-ratio: 1.5),
|
|||
only screen and (min-resolution: 1.5dppx) {
|
||||
.sprite-tokens {
|
||||
background-image: url(/rsrc/image/sprite-tokens-X2.png);
|
||||
background-size: 76px 76px;
|
||||
background-size: 95px 95px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,50 +35,82 @@ only screen and (min-resolution: 1.5dppx) {
|
|||
background-position: -57px 0px;
|
||||
}
|
||||
|
||||
.tokens-heart-1 {
|
||||
.tokens-emoji-1 {
|
||||
background-position: -76px 0px;
|
||||
}
|
||||
|
||||
.tokens-emoji-2 {
|
||||
background-position: 0px -19px;
|
||||
}
|
||||
|
||||
.tokens-heart-2 {
|
||||
.tokens-emoji-3 {
|
||||
background-position: -19px -19px;
|
||||
}
|
||||
|
||||
.tokens-like-1 {
|
||||
.tokens-emoji-4 {
|
||||
background-position: -38px -19px;
|
||||
}
|
||||
|
||||
.tokens-like-2 {
|
||||
.tokens-emoji-5 {
|
||||
background-position: -57px -19px;
|
||||
}
|
||||
|
||||
.tokens-medal-1 {
|
||||
.tokens-emoji-6 {
|
||||
background-position: -76px -19px;
|
||||
}
|
||||
|
||||
.tokens-emoji-7 {
|
||||
background-position: 0px -38px;
|
||||
}
|
||||
|
||||
.tokens-medal-2 {
|
||||
.tokens-emoji-8 {
|
||||
background-position: -19px -38px;
|
||||
}
|
||||
|
||||
.tokens-medal-3 {
|
||||
.tokens-heart-1 {
|
||||
background-position: -38px -38px;
|
||||
}
|
||||
|
||||
.tokens-medal-4 {
|
||||
.tokens-heart-2 {
|
||||
background-position: -57px -38px;
|
||||
}
|
||||
|
||||
.tokens-misc-1 {
|
||||
.tokens-like-1 {
|
||||
background-position: -76px -38px;
|
||||
}
|
||||
|
||||
.tokens-like-2 {
|
||||
background-position: 0px -57px;
|
||||
}
|
||||
|
||||
.tokens-misc-2 {
|
||||
.tokens-medal-1 {
|
||||
background-position: -19px -57px;
|
||||
}
|
||||
|
||||
.tokens-misc-3 {
|
||||
.tokens-medal-2 {
|
||||
background-position: -38px -57px;
|
||||
}
|
||||
|
||||
.tokens-misc-4 {
|
||||
.tokens-medal-3 {
|
||||
background-position: -57px -57px;
|
||||
}
|
||||
|
||||
.tokens-medal-4 {
|
||||
background-position: -76px -57px;
|
||||
}
|
||||
|
||||
.tokens-misc-1 {
|
||||
background-position: 0px -76px;
|
||||
}
|
||||
|
||||
.tokens-misc-2 {
|
||||
background-position: -19px -76px;
|
||||
}
|
||||
|
||||
.tokens-misc-3 {
|
||||
background-position: -38px -76px;
|
||||
}
|
||||
|
||||
.tokens-misc-4 {
|
||||
background-position: -57px -76px;
|
||||
}
|
||||
|
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 10 KiB |
|
@ -32,6 +32,18 @@ JX.install('KeyboardShortcutManager', {
|
|||
down: 1
|
||||
},
|
||||
|
||||
/**
|
||||
* Some keys require Alt to be pressed in order to type them on certain
|
||||
* keyboard layouts.
|
||||
*/
|
||||
_altkeys: {
|
||||
// "Alt+L" on German layouts.
|
||||
'@': 1,
|
||||
|
||||
// "Alt+Shift+7" on German layouts.
|
||||
'\\': 1
|
||||
},
|
||||
|
||||
getInstance : function() {
|
||||
if (!JX.KeyboardShortcutManager._instance) {
|
||||
JX.KeyboardShortcutManager._instance = new JX.KeyboardShortcutManager();
|
||||
|
@ -119,14 +131,24 @@ JX.install('KeyboardShortcutManager', {
|
|||
}
|
||||
},
|
||||
_onkeyhit : function(e) {
|
||||
var self = JX.KeyboardShortcutManager;
|
||||
|
||||
var raw = e.getRawEvent();
|
||||
|
||||
if (raw.altKey || raw.ctrlKey || raw.metaKey) {
|
||||
if (raw.ctrlKey || raw.metaKey) {
|
||||
// Never activate keyboard shortcuts if modifier keys are also
|
||||
// depressed.
|
||||
return;
|
||||
}
|
||||
|
||||
// For most keystrokes, don't activate keyboard shortcuts if the Alt
|
||||
// key is depressed. However, we continue if the character requires the
|
||||
// use of Alt to type it on some keyboard layouts.
|
||||
var key = this._getKey(e);
|
||||
if (raw.altKey && !(key in self._altkeys)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var target = e.getTarget();
|
||||
var ignore = ['input', 'select', 'textarea', 'object', 'embed'];
|
||||
if (JX.DOM.isType(target, ignore)) {
|
||||
|
|
|
@ -11,10 +11,10 @@
|
|||
* Define global keyboard shortcuts.
|
||||
*/
|
||||
JX.behavior('phabricator-keyboard-shortcuts', function(config) {
|
||||
var pht = JX.phtize(config.pht);
|
||||
var workflow = null;
|
||||
|
||||
var desc = 'Show keyboard shortcut help for the current page.';
|
||||
new JX.KeyboardShortcut('?', desc)
|
||||
new JX.KeyboardShortcut('?', pht('?'))
|
||||
.setHandler(function(manager) {
|
||||
if (workflow) {
|
||||
// Already showing the dialog.
|
||||
|
@ -30,4 +30,14 @@ JX.behavior('phabricator-keyboard-shortcuts', function(config) {
|
|||
})
|
||||
.register();
|
||||
|
||||
if (config.searchID) {
|
||||
new JX.KeyboardShortcut('/', pht('/'))
|
||||
.setHandler(function() {
|
||||
var search = JX.$(config.searchID);
|
||||
search.focus();
|
||||
search.select();
|
||||
})
|
||||
.register();
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
@ -197,7 +197,11 @@ JX.install('PHUIXAutocomplete', {
|
|||
_onkeypress: function(e) {
|
||||
var r = e.getRawEvent();
|
||||
|
||||
if (r.metaKey || r.altKey || r.ctrlKey) {
|
||||
// NOTE: We allow events to continue with "altKey", because you need
|
||||
// to press Alt to type characters like "@" on a German keyboard layout.
|
||||
// The cost of misfiring autocompleters is very small since we do not
|
||||
// eat the keystroke. See T10252.
|
||||
if (r.metaKey || r.ctrlKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|