Promote 2023.32 to Stable
|
@ -9,7 +9,7 @@ return array(
|
|||
'names' => array(
|
||||
'conpherence.pkg.css' => '76ed87e3',
|
||||
'conpherence.pkg.js' => '020aebcf',
|
||||
'core.pkg.css' => '0cb47e9d',
|
||||
'core.pkg.css' => '1a5169fe',
|
||||
'core.pkg.js' => '2eeda9e0',
|
||||
'dark-console.pkg.js' => '187792c2',
|
||||
'differential.pkg.css' => '525f9a1d',
|
||||
|
@ -100,7 +100,7 @@ return array(
|
|||
'rsrc/css/application/policy/policy-edit.css' => '8794e2ed',
|
||||
'rsrc/css/application/policy/policy-transaction-detail.css' => 'c02b8384',
|
||||
'rsrc/css/application/policy/policy.css' => 'ceb56a08',
|
||||
'rsrc/css/application/ponder/ponder-view.css' => '05a09d0a',
|
||||
'rsrc/css/application/ponder/ponder-view.css' => 'b04bbaff',
|
||||
'rsrc/css/application/project/project-card-view.css' => 'c1200da7',
|
||||
'rsrc/css/application/project/project-triggers.css' => 'cd9c8bb9',
|
||||
'rsrc/css/application/project/project-view.css' => '2f7caa20',
|
||||
|
@ -134,7 +134,7 @@ return array(
|
|||
'rsrc/css/phui/object-item/phui-oi-drag-ui.css' => 'da15d3dc',
|
||||
'rsrc/css/phui/object-item/phui-oi-flush-ui.css' => '490e2e2e',
|
||||
'rsrc/css/phui/object-item/phui-oi-list-view.css' => '9275ff55',
|
||||
'rsrc/css/phui/object-item/phui-oi-simple-ui.css' => '6a30fa46',
|
||||
'rsrc/css/phui/object-item/phui-oi-simple-ui.css' => '9b03a61f',
|
||||
'rsrc/css/phui/phui-action-list.css' => '1b0085b2',
|
||||
'rsrc/css/phui/phui-action-panel.css' => '6c386cbf',
|
||||
'rsrc/css/phui/phui-badge.css' => '666e25ad',
|
||||
|
@ -154,25 +154,25 @@ return array(
|
|||
'rsrc/css/phui/phui-document.css' => '52b748a5',
|
||||
'rsrc/css/phui/phui-feed-story.css' => 'a0c05029',
|
||||
'rsrc/css/phui/phui-fontkit.css' => '1ec937e5',
|
||||
'rsrc/css/phui/phui-form-view.css' => '7536aef9',
|
||||
'rsrc/css/phui/phui-form-view.css' => '57edecb7',
|
||||
'rsrc/css/phui/phui-form.css' => 'd1adb52c',
|
||||
'rsrc/css/phui/phui-formation-view.css' => 'd2dec8ed',
|
||||
'rsrc/css/phui/phui-head-thing.css' => 'd7f293df',
|
||||
'rsrc/css/phui/phui-header-view.css' => '36c86a58',
|
||||
'rsrc/css/phui/phui-hovercard.css' => '6ca90fa0',
|
||||
'rsrc/css/phui/phui-hovercard.css' => '39fd2e14',
|
||||
'rsrc/css/phui/phui-icon-set-selector.css' => '7aa5f3ec',
|
||||
'rsrc/css/phui/phui-icon.css' => '4cbc684a',
|
||||
'rsrc/css/phui/phui-icon.css' => '084ac612',
|
||||
'rsrc/css/phui/phui-image-mask.css' => '62c7f4d2',
|
||||
'rsrc/css/phui/phui-info-view.css' => 'a10a909b',
|
||||
'rsrc/css/phui/phui-invisible-character-view.css' => 'c694c4a4',
|
||||
'rsrc/css/phui/phui-left-right.css' => '68513c34',
|
||||
'rsrc/css/phui/phui-lightbox.css' => '4ebf22da',
|
||||
'rsrc/css/phui/phui-list.css' => '0c04affd',
|
||||
'rsrc/css/phui/phui-list.css' => 'ccf73664',
|
||||
'rsrc/css/phui/phui-object-box.css' => 'fdffed5c',
|
||||
'rsrc/css/phui/phui-pager.css' => 'd022c7ad',
|
||||
'rsrc/css/phui/phui-pinboard-view.css' => '1f08f5d8',
|
||||
'rsrc/css/phui/phui-policy-section-view.css' => '139fdc64',
|
||||
'rsrc/css/phui/phui-property-list-view.css' => '5adf7078',
|
||||
'rsrc/css/phui/phui-property-list-view.css' => '118db252',
|
||||
'rsrc/css/phui/phui-remarkup-preview.css' => '91767007',
|
||||
'rsrc/css/phui/phui-segment-bar-view.css' => '5166b370',
|
||||
'rsrc/css/phui/phui-spacing.css' => 'b05cadc3',
|
||||
|
@ -184,7 +184,7 @@ return array(
|
|||
'rsrc/css/phui/workboards/phui-workboard.css' => '74fc9d98',
|
||||
'rsrc/css/phui/workboards/phui-workcard.css' => '62056e3b',
|
||||
'rsrc/css/phui/workboards/phui-workpanel.css' => 'bc06f022',
|
||||
'rsrc/css/sprite-login.css' => '35d1510c',
|
||||
'rsrc/css/sprite-login.css' => '07052ee0',
|
||||
'rsrc/css/sprite-tokens.css' => 'f1896dc5',
|
||||
'rsrc/css/syntax/syntax-default.css' => '055fc231',
|
||||
'rsrc/externals/d3/d3.min.js' => '9d068042',
|
||||
|
@ -342,8 +342,8 @@ return array(
|
|||
'rsrc/image/phrequent_active.png' => 'de66dc50',
|
||||
'rsrc/image/phrequent_inactive.png' => '79c61baf',
|
||||
'rsrc/image/resize.png' => '9cc83373',
|
||||
'rsrc/image/sprite-login-X2.png' => '269800ec',
|
||||
'rsrc/image/sprite-login.png' => 'a843f146',
|
||||
'rsrc/image/sprite-login-X2.png' => '02896524',
|
||||
'rsrc/image/sprite-login.png' => 'e0508107',
|
||||
'rsrc/image/sprite-tokens-X2.png' => '21621dd9',
|
||||
'rsrc/image/sprite-tokens.png' => 'bede2580',
|
||||
'rsrc/image/texture/card-gradient.png' => 'e6892cb4',
|
||||
|
@ -387,7 +387,7 @@ return array(
|
|||
'rsrc/js/application/diff/DiffTreeView.js' => '5d83623b',
|
||||
'rsrc/js/application/differential/behavior-diff-radios.js' => '925fe8cd',
|
||||
'rsrc/js/application/differential/behavior-populate.js' => 'b86ef6c2',
|
||||
'rsrc/js/application/diffusion/DiffusionLocateFileSource.js' => '94243d89',
|
||||
'rsrc/js/application/diffusion/DiffusionLocateFileSource.js' => '6c798a10',
|
||||
'rsrc/js/application/diffusion/ExternalEditorLinkEngine.js' => '48a8641f',
|
||||
'rsrc/js/application/diffusion/behavior-audit-preview.js' => 'b7b73831',
|
||||
'rsrc/js/application/diffusion/behavior-commit-branches.js' => '4b671572',
|
||||
|
@ -707,7 +707,7 @@ return array(
|
|||
'javelin-chart-function-label' => '81de1dab',
|
||||
'javelin-color' => '78f811c9',
|
||||
'javelin-cookie' => '05d290ef',
|
||||
'javelin-diffusion-locate-file-source' => '94243d89',
|
||||
'javelin-diffusion-locate-file-source' => '6c798a10',
|
||||
'javelin-dom' => 'e4c7622a',
|
||||
'javelin-dynval' => '202a2e85',
|
||||
'javelin-event' => 'c03f2fb4',
|
||||
|
@ -849,33 +849,33 @@ return array(
|
|||
'phui-font-icon-base-css' => '303c9b87',
|
||||
'phui-fontkit-css' => '1ec937e5',
|
||||
'phui-form-css' => 'd1adb52c',
|
||||
'phui-form-view-css' => '7536aef9',
|
||||
'phui-form-view-css' => '57edecb7',
|
||||
'phui-formation-view-css' => 'd2dec8ed',
|
||||
'phui-head-thing-view-css' => 'd7f293df',
|
||||
'phui-header-view-css' => '36c86a58',
|
||||
'phui-hovercard' => '6199f752',
|
||||
'phui-hovercard-list' => 'de4b4919',
|
||||
'phui-hovercard-view-css' => '6ca90fa0',
|
||||
'phui-hovercard-view-css' => '39fd2e14',
|
||||
'phui-icon-set-selector-css' => '7aa5f3ec',
|
||||
'phui-icon-view-css' => '4cbc684a',
|
||||
'phui-icon-view-css' => '084ac612',
|
||||
'phui-image-mask-css' => '62c7f4d2',
|
||||
'phui-info-view-css' => 'a10a909b',
|
||||
'phui-inline-comment-view-css' => 'a864426f',
|
||||
'phui-invisible-character-view-css' => 'c694c4a4',
|
||||
'phui-left-right-css' => '68513c34',
|
||||
'phui-lightbox-css' => '4ebf22da',
|
||||
'phui-list-view-css' => '0c04affd',
|
||||
'phui-list-view-css' => 'ccf73664',
|
||||
'phui-object-box-css' => 'fdffed5c',
|
||||
'phui-oi-big-ui-css' => 'fa74cc35',
|
||||
'phui-oi-color-css' => 'b517bfa0',
|
||||
'phui-oi-drag-ui-css' => 'da15d3dc',
|
||||
'phui-oi-flush-ui-css' => '490e2e2e',
|
||||
'phui-oi-list-view-css' => '9275ff55',
|
||||
'phui-oi-simple-ui-css' => '6a30fa46',
|
||||
'phui-oi-simple-ui-css' => '9b03a61f',
|
||||
'phui-pager-css' => 'd022c7ad',
|
||||
'phui-pinboard-view-css' => '1f08f5d8',
|
||||
'phui-policy-section-view-css' => '139fdc64',
|
||||
'phui-property-list-view-css' => '5adf7078',
|
||||
'phui-property-list-view-css' => '118db252',
|
||||
'phui-remarkup-preview-css' => '91767007',
|
||||
'phui-segment-bar-view-css' => '5166b370',
|
||||
'phui-spacing-css' => 'b05cadc3',
|
||||
|
@ -901,12 +901,12 @@ return array(
|
|||
'policy-css' => 'ceb56a08',
|
||||
'policy-edit-css' => '8794e2ed',
|
||||
'policy-transaction-detail-css' => 'c02b8384',
|
||||
'ponder-view-css' => '05a09d0a',
|
||||
'ponder-view-css' => 'b04bbaff',
|
||||
'project-card-view-css' => 'c1200da7',
|
||||
'project-triggers-css' => 'cd9c8bb9',
|
||||
'project-view-css' => '2f7caa20',
|
||||
'setup-issue-css' => '5eed85b2',
|
||||
'sprite-login-css' => '35d1510c',
|
||||
'sprite-login-css' => '07052ee0',
|
||||
'sprite-tokens-css' => 'f1896dc5',
|
||||
'syntax-default-css' => '055fc231',
|
||||
'syntax-highlighting-css' => '548567f6',
|
||||
|
@ -1538,9 +1538,6 @@ return array(
|
|||
'6a18c42e' => array(
|
||||
'javelin-install',
|
||||
),
|
||||
'6a30fa46' => array(
|
||||
'phui-oi-list-view-css',
|
||||
),
|
||||
'6a85bc5a' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
|
@ -1555,6 +1552,12 @@ return array(
|
|||
'phabricator-textareautils',
|
||||
'phabricator-remarkup-metadata',
|
||||
),
|
||||
'6c798a10' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
'javelin-typeahead-preloaded-source',
|
||||
'javelin-util',
|
||||
),
|
||||
'6cfa0008' => array(
|
||||
'javelin-dom',
|
||||
'javelin-dynval',
|
||||
|
@ -1762,12 +1765,6 @@ return array(
|
|||
'phabricator-prefab',
|
||||
'javelin-json',
|
||||
),
|
||||
'94243d89' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
'javelin-typeahead-preloaded-source',
|
||||
'javelin-util',
|
||||
),
|
||||
'9623adc1' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
|
@ -1791,6 +1788,9 @@ return array(
|
|||
'javelin-install',
|
||||
'javelin-util',
|
||||
),
|
||||
'9b03a61f' => array(
|
||||
'phui-oi-list-view-css',
|
||||
),
|
||||
'9c01e364' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
|
|
Before Width: | Height: | Size: 773 B After Width: | Height: | Size: 746 B |
Before Width: | Height: | Size: 899 B After Width: | Height: | Size: 894 B |
BIN
resources/sprite/login_1x/Phorge.png
Normal file
After Width: | Height: | Size: 664 B |
Before Width: | Height: | Size: 860 B After Width: | Height: | Size: 822 B |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 1.4 KiB |
BIN
resources/sprite/login_2x/Phorge.png
Normal file
After Width: | Height: | Size: 996 B |
|
@ -24,7 +24,7 @@
|
|||
"login-Facebook": {
|
||||
"name": "login-Facebook",
|
||||
"rule": ".login-Facebook",
|
||||
"hash": "1b12a5a5cfe103d4d96213cf7d1ce18a"
|
||||
"hash": "ad9b8fcc4c338bfe4196a7f636c7d4c1"
|
||||
},
|
||||
"login-Generic": {
|
||||
"name": "login-Generic",
|
||||
|
@ -39,7 +39,7 @@
|
|||
"login-Google": {
|
||||
"name": "login-Google",
|
||||
"rule": ".login-Google",
|
||||
"hash": "72e7b0e1005c92f059f4d5881592ee72"
|
||||
"hash": "9a25ee35dfde4135db2e321e01b82c61"
|
||||
},
|
||||
"login-HTTP": {
|
||||
"name": "login-HTTP",
|
||||
|
@ -71,6 +71,11 @@
|
|||
"rule": ".login-Phabricator",
|
||||
"hash": "54f5ddae4b9d138c438ec00ed42544d2"
|
||||
},
|
||||
"login-Phorge": {
|
||||
"name": "login-Phorge",
|
||||
"rule": ".login-Phorge",
|
||||
"hash": "0cc36ea8b7b98c57c23e547400b955f1"
|
||||
},
|
||||
"login-Slack": {
|
||||
"name": "login-Slack",
|
||||
"rule": ".login-Slack",
|
||||
|
|
|
@ -466,6 +466,7 @@ phutil_register_library_map(array(
|
|||
'DifferentialBlockHeraldAction' => 'applications/differential/herald/DifferentialBlockHeraldAction.php',
|
||||
'DifferentialBlockingReviewerDatasource' => 'applications/differential/typeahead/DifferentialBlockingReviewerDatasource.php',
|
||||
'DifferentialBranchField' => 'applications/differential/customfield/DifferentialBranchField.php',
|
||||
'DifferentialBranchFieldTestCase' => 'applications/differential/customfield/__tests__/DifferentialBranchFieldTestCase.php',
|
||||
'DifferentialBuildableEngine' => 'applications/differential/harbormaster/DifferentialBuildableEngine.php',
|
||||
'DifferentialChangeDetailMailView' => 'applications/differential/mail/DifferentialChangeDetailMailView.php',
|
||||
'DifferentialChangeHeraldFieldGroup' => 'applications/differential/herald/DifferentialChangeHeraldFieldGroup.php',
|
||||
|
@ -1052,6 +1053,7 @@ phutil_register_library_map(array(
|
|||
'DiffusionSSHWorkflow' => 'applications/diffusion/ssh/DiffusionSSHWorkflow.php',
|
||||
'DiffusionSearchQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionSearchQueryConduitAPIMethod.php',
|
||||
'DiffusionServeController' => 'applications/diffusion/controller/DiffusionServeController.php',
|
||||
'DiffusionServeControllerTestCase' => 'applications/diffusion/controller/__tests__/DiffusionServeControllerTestCase.php',
|
||||
'DiffusionServiceRef' => 'applications/diffusion/ref/DiffusionServiceRef.php',
|
||||
'DiffusionSetPasswordSettingsPanel' => 'applications/diffusion/panel/DiffusionSetPasswordSettingsPanel.php',
|
||||
'DiffusionSetupException' => 'applications/diffusion/exception/DiffusionSetupException.php',
|
||||
|
@ -3081,6 +3083,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorDashboardColumn' => 'applications/dashboard/layoutconfig/PhabricatorDashboardColumn.php',
|
||||
'PhabricatorDashboardConsoleController' => 'applications/dashboard/controller/PhabricatorDashboardConsoleController.php',
|
||||
'PhabricatorDashboardController' => 'applications/dashboard/controller/PhabricatorDashboardController.php',
|
||||
'PhabricatorDashboardCreateCapability' => 'applications/countdown/capability/PhabricatorDashboardCreateCapability.php',
|
||||
'PhabricatorDashboardDAO' => 'applications/dashboard/storage/PhabricatorDashboardDAO.php',
|
||||
'PhabricatorDashboardDashboardPHIDType' => 'applications/dashboard/phid/PhabricatorDashboardDashboardPHIDType.php',
|
||||
'PhabricatorDashboardDatasource' => 'applications/dashboard/typeahead/PhabricatorDashboardDatasource.php',
|
||||
|
@ -5641,6 +5644,7 @@ phutil_register_library_map(array(
|
|||
'PhrictionEditEngineController' => 'applications/phriction/controller/PhrictionEditEngineController.php',
|
||||
'PhrictionHistoryConduitAPIMethod' => 'applications/phriction/conduit/PhrictionHistoryConduitAPIMethod.php',
|
||||
'PhrictionHistoryController' => 'applications/phriction/controller/PhrictionHistoryController.php',
|
||||
'PhrictionHovercardEngineExtension' => 'applications/phriction/engineextension/PhrictionHovercardEngineExtension.php',
|
||||
'PhrictionInfoConduitAPIMethod' => 'applications/phriction/conduit/PhrictionInfoConduitAPIMethod.php',
|
||||
'PhrictionListController' => 'applications/phriction/controller/PhrictionListController.php',
|
||||
'PhrictionMarkupPreviewController' => 'applications/phriction/controller/PhrictionMarkupPreviewController.php',
|
||||
|
@ -6470,6 +6474,7 @@ phutil_register_library_map(array(
|
|||
'DifferentialBlockHeraldAction' => 'HeraldAction',
|
||||
'DifferentialBlockingReviewerDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||
'DifferentialBranchField' => 'DifferentialCustomField',
|
||||
'DifferentialBranchFieldTestCase' => 'PhabricatorTestCase',
|
||||
'DifferentialBuildableEngine' => 'HarbormasterBuildableEngine',
|
||||
'DifferentialChangeDetailMailView' => 'DifferentialMailView',
|
||||
'DifferentialChangeHeraldFieldGroup' => 'HeraldFieldGroup',
|
||||
|
@ -6559,7 +6564,7 @@ phutil_register_library_map(array(
|
|||
'DifferentialDiffRepositoryProjectsHeraldField' => 'DifferentialDiffHeraldField',
|
||||
'DifferentialDiffSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
|
||||
'DifferentialDiffSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'DifferentialDiffTestCase' => 'PhutilTestCase',
|
||||
'DifferentialDiffTestCase' => 'PhabricatorTestCase',
|
||||
'DifferentialDiffTransaction' => 'PhabricatorApplicationTransaction',
|
||||
'DifferentialDiffTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||
'DifferentialDiffViewController' => 'DifferentialController',
|
||||
|
@ -7102,6 +7107,7 @@ phutil_register_library_map(array(
|
|||
'DiffusionSSHWorkflow' => 'PhabricatorSSHWorkflow',
|
||||
'DiffusionSearchQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
|
||||
'DiffusionServeController' => 'DiffusionController',
|
||||
'DiffusionServeControllerTestCase' => 'PhabricatorTestCase',
|
||||
'DiffusionServiceRef' => 'Phobject',
|
||||
'DiffusionSetPasswordSettingsPanel' => 'PhabricatorSettingsPanel',
|
||||
'DiffusionSetupException' => 'Exception',
|
||||
|
@ -9496,6 +9502,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorDashboardColumn' => 'Phobject',
|
||||
'PhabricatorDashboardConsoleController' => 'PhabricatorDashboardController',
|
||||
'PhabricatorDashboardController' => 'PhabricatorController',
|
||||
'PhabricatorDashboardCreateCapability' => 'PhabricatorPolicyCapability',
|
||||
'PhabricatorDashboardDAO' => 'PhabricatorLiskDAO',
|
||||
'PhabricatorDashboardDashboardPHIDType' => 'PhabricatorPHIDType',
|
||||
'PhabricatorDashboardDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||
|
@ -12539,6 +12546,7 @@ phutil_register_library_map(array(
|
|||
'PhrictionEditEngineController' => 'PhrictionController',
|
||||
'PhrictionHistoryConduitAPIMethod' => 'PhrictionConduitAPIMethod',
|
||||
'PhrictionHistoryController' => 'PhrictionController',
|
||||
'PhrictionHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension',
|
||||
'PhrictionInfoConduitAPIMethod' => 'PhrictionConduitAPIMethod',
|
||||
'PhrictionListController' => 'PhrictionController',
|
||||
'PhrictionMarkupPreviewController' => 'PhabricatorController',
|
||||
|
|
|
@ -30,16 +30,16 @@ abstract class PhabricatorAphlictManagementWorkflow
|
|||
$full_path = Filesystem::resolvePath($config_file);
|
||||
$show_path = $full_path;
|
||||
} else {
|
||||
$root = dirname(dirname(phutil_get_library_root('phabricator')));
|
||||
$root = dirname(phutil_get_library_root('phorge'));
|
||||
|
||||
$try = array(
|
||||
'phabricator/conf/aphlict/aphlict.custom.json',
|
||||
'phabricator/conf/aphlict/aphlict.default.json',
|
||||
'conf/aphlict/aphlict.custom.json',
|
||||
'conf/aphlict/aphlict.default.json',
|
||||
);
|
||||
|
||||
foreach ($try as $config) {
|
||||
$full_path = $root.'/'.$config;
|
||||
$show_path = $config;
|
||||
$show_path = '<phorge>/'.$config;
|
||||
if (Filesystem::pathExists($full_path)) {
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ final class PhabricatorAuthNeedsApprovalController
|
|||
$viewer,
|
||||
PhabricatorAuthWaitForApprovalMessageType::MESSAGEKEY);
|
||||
|
||||
if (!strlen($text)) {
|
||||
if (!phutil_nonempty_string($text)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -150,6 +150,8 @@ final class PhabricatorAuthPasswordEngine
|
|||
$base_uri = new PhutilURI($base_uri);
|
||||
$blocklist[] = $base_uri->getDomain();
|
||||
|
||||
$blocklist = array_filter($blocklist);
|
||||
|
||||
// Generate additional subterms by splitting the raw blocklist on
|
||||
// characters like "@", " " (space), and "." to break up email addresses,
|
||||
// readable names, and domain names into components.
|
||||
|
|
|
@ -264,7 +264,7 @@ final class PhabricatorLDAPAuthProvider extends PhabricatorAuthProvider {
|
|||
'talk to LDAP. Usually you can install it with '.
|
||||
'`%s`, `%s`, or a similar package manager command.',
|
||||
'yum install php-ldap',
|
||||
'apt-get install php5-ldap'));
|
||||
'apt-get install php-ldap'));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -427,7 +427,7 @@ final class PhabricatorLDAPAuthProvider extends PhabricatorAuthProvider {
|
|||
}
|
||||
|
||||
$instruction_text = idx($instructions, $key);
|
||||
if (strlen($instruction_text)) {
|
||||
if (phutil_nonempty_string($instruction_text)) {
|
||||
$form->appendRemarkupInstructions($instruction_text);
|
||||
}
|
||||
|
||||
|
|
|
@ -289,7 +289,7 @@ final class PhabricatorConduitAPIController
|
|||
);
|
||||
}
|
||||
|
||||
$token_string = idx($metadata, 'token');
|
||||
$token_string = idx($metadata, 'token', '');
|
||||
if (strlen($token_string)) {
|
||||
|
||||
if (strlen($token_string) != 32) {
|
||||
|
@ -683,7 +683,7 @@ final class PhabricatorConduitAPIController
|
|||
// Otherwise, look for a single parameter called 'params' which has the
|
||||
// entire param dictionary JSON encoded.
|
||||
$params_json = $request->getStr('params');
|
||||
if (strlen($params_json)) {
|
||||
if (phutil_nonempty_string($params_json)) {
|
||||
$params = null;
|
||||
try {
|
||||
$params = phutil_json_decode($params_json);
|
||||
|
|
|
@ -83,7 +83,7 @@ final class PhabricatorPHPConfigSetupCheck extends PhabricatorSetupCheck {
|
|||
|
||||
// NOTE: We're intentionally telling you to install "mysqlnd" here; on
|
||||
// Ubuntu, there's no separate "mysqli" package.
|
||||
phutil_tag('tt', array(), 'sudo apt-get install php5-mysqlnd'));
|
||||
phutil_tag('tt', array(), 'sudo apt-get install php-mysqlnd'));
|
||||
|
||||
$this->newIssue('php.mysqli')
|
||||
->setName(pht('MySQLi Extension Not Available'))
|
||||
|
@ -103,7 +103,7 @@ final class PhabricatorPHPConfigSetupCheck extends PhabricatorSetupCheck {
|
|||
'native driver is recommended.'.
|
||||
"\n\n".
|
||||
'You may be able to install the native driver with a command like: %s',
|
||||
phutil_tag('tt', array(), 'sudo apt-get install php5-mysqlnd'));
|
||||
phutil_tag('tt', array(), 'sudo apt-get install php-mysqlnd'));
|
||||
|
||||
|
||||
$this->newIssue('php.myqlnd')
|
||||
|
|
|
@ -189,9 +189,10 @@ final class PhabricatorConfigConsoleController
|
|||
foreach ($specs as $lib) {
|
||||
$remote_future = $remote_futures[$lib];
|
||||
|
||||
list($err, $stdout) = $remote_future->resolve();
|
||||
if ($err) {
|
||||
// If this fails for whatever reason, just move on.
|
||||
try {
|
||||
list($stdout, $err) = $remote_future->resolvex();
|
||||
} catch (CommandException $e) {
|
||||
$this->logGitErrorWithPotentialTips($e, $lib);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -258,13 +259,14 @@ final class PhabricatorConfigConsoleController
|
|||
|
||||
$results = array();
|
||||
foreach ($log_futures as $lib => $future) {
|
||||
list($err, $stdout) = $future->resolve();
|
||||
if (!$err) {
|
||||
try {
|
||||
list($stdout, $err) = $future->resolvex();
|
||||
list($hash, $epoch) = explode(' ', $stdout);
|
||||
} else {
|
||||
} catch (CommandException $e) {
|
||||
$hash = null;
|
||||
$epoch = null;
|
||||
}
|
||||
$this->logGitErrorWithPotentialTips($e, $lib);
|
||||
}
|
||||
|
||||
$result = array(
|
||||
'hash' => $hash,
|
||||
|
@ -275,7 +277,7 @@ final class PhabricatorConfigConsoleController
|
|||
|
||||
$upstream_future = idx($upstream_futures, $lib);
|
||||
if ($upstream_future) {
|
||||
list($err, $stdout) = $upstream_future->resolve();
|
||||
list($stdout, $err) = $upstream_future->resolvex();
|
||||
if (!$err) {
|
||||
$branchpoint = trim($stdout);
|
||||
if (strlen($branchpoint)) {
|
||||
|
@ -340,5 +342,36 @@ final class PhabricatorConfigConsoleController
|
|||
->appendChild($table_view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Help in better troubleshooting git errors.
|
||||
* @param CommandException $e Exception
|
||||
* @param string $lib Library name involved
|
||||
*/
|
||||
private function logGitErrorWithPotentialTips($e, $lib) {
|
||||
|
||||
// First, detect this specific error message related to [safe] stuff.
|
||||
$expected_error_msg_part = 'detected dubious ownership in repository';
|
||||
$stderr = $e->getStderr();
|
||||
if (strpos($stderr, $expected_error_msg_part) !== false) {
|
||||
|
||||
// Found! Let's show a nice resolution tip.
|
||||
|
||||
// Complete path of the problematic repository.
|
||||
$lib_root = dirname(phutil_get_library_root($lib));
|
||||
|
||||
phlog(pht(
|
||||
"Cannot identify the version of the %s repository because ".
|
||||
"the webserver does not trust it (more info on Task %s).\n".
|
||||
"Try this system resolution:\n".
|
||||
"sudo git config --system --add safe.directory %s",
|
||||
$lib,
|
||||
'https://we.phorge.it/T15282',
|
||||
$lib_root));
|
||||
} else {
|
||||
|
||||
// Otherwise show a generic error message
|
||||
phlog($e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -227,7 +227,7 @@ EODOC
|
|||
pht(
|
||||
'Controls whether email for multiple recipients is sent by '.
|
||||
'creating one message with everyone in the "To:" line, or '.
|
||||
'multiple messages that each have a single recipeint in the '.
|
||||
'multiple messages that each have a single recipient in the '.
|
||||
'"To:" line.'))
|
||||
->setDescription($one_mail_per_recipient_desc),
|
||||
$this->newOption('metamta.can-send-as-user', 'bool', false)
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorDashboardCreateCapability
|
||||
extends PhabricatorPolicyCapability {
|
||||
|
||||
const CAPABILITY = 'dashboard.create';
|
||||
|
||||
public function getCapabilityName() {
|
||||
return pht('Can Create Dashboards');
|
||||
}
|
||||
|
||||
public function describeCapabilityRejection() {
|
||||
return pht('You do not have permission to create a dashboard.');
|
||||
}
|
||||
|
||||
}
|
|
@ -83,4 +83,12 @@ final class PhabricatorDashboardApplication extends PhabricatorApplication {
|
|||
);
|
||||
}
|
||||
|
||||
protected function getCustomCapabilities() {
|
||||
return array(
|
||||
PhabricatorDashboardCreateCapability::CAPABILITY => array(
|
||||
'default' => PhabricatorPolicies::POLICY_USER,
|
||||
'caption' => pht('Default create policy for Dashboards.'),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ final class PhabricatorDashboardAdjustController
|
|||
}
|
||||
|
||||
$column_key = $request->getStr('columnKey');
|
||||
if (strlen($column_key)) {
|
||||
if (phutil_nonempty_string($column_key)) {
|
||||
$columns = $ref_list->getColumns();
|
||||
if (!isset($columns[$column_key])) {
|
||||
return new Aphront404Response();
|
||||
|
@ -52,7 +52,7 @@ final class PhabricatorDashboardAdjustController
|
|||
|
||||
$after_ref = null;
|
||||
$after_key = $request->getStr('afterKey');
|
||||
if (strlen($after_key)) {
|
||||
if (phutil_nonempty_string($after_key)) {
|
||||
$after_ref = $ref_list->getPanelRef($after_key);
|
||||
if (!$after_ref) {
|
||||
return new Aphront404Response();
|
||||
|
|
|
@ -327,7 +327,7 @@ final class PhabricatorDashboardPanelTabsController
|
|||
'This is already the last tab. It can not move any farther to '.
|
||||
'the right.'))
|
||||
->addCancelButton($cancel_uri);
|
||||
} else if ((string)head_key($old_config) === $target) {
|
||||
} else if (!$is_next && (string)head_key($old_config) === $target) {
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Impossible!'))
|
||||
->appendParagraph(
|
||||
|
|
|
@ -66,6 +66,11 @@ final class PhabricatorDashboardEditEngine
|
|||
return $object->getURI();
|
||||
}
|
||||
|
||||
protected function getCreateNewObjectPolicy() {
|
||||
return $this->getApplication()->getPolicy(
|
||||
PhabricatorDashboardCreateCapability::CAPABILITY);
|
||||
}
|
||||
|
||||
protected function buildCustomEditFields($object) {
|
||||
$layout_options = PhabricatorDashboardLayoutMode::getLayoutModeMap();
|
||||
|
||||
|
|
|
@ -273,6 +273,11 @@ final class PhabricatorDashboardPanelRenderingEngine extends Phobject {
|
|||
->addClass('dashboard-box')
|
||||
->addSigil('dashboard-panel');
|
||||
|
||||
// Allow to style Archived Panels differently.
|
||||
if ($panel && $panel->getIsArchived()) {
|
||||
$box->addClass('dashboard-panel-disabled');
|
||||
}
|
||||
|
||||
if ($this->getMovable()) {
|
||||
$box->addSigil('panel-movable');
|
||||
}
|
||||
|
@ -302,6 +307,16 @@ final class PhabricatorDashboardPanelRenderingEngine extends Phobject {
|
|||
$header = id(new PHUIHeaderView())
|
||||
->setHeader($header_text);
|
||||
$header = $this->addPanelHeaderActions($header);
|
||||
|
||||
// If the Panel is Archived, show in edit mode as such.
|
||||
if ($panel && $panel->getIsArchived()) {
|
||||
$header->setSubheader(
|
||||
id(new PHUITagView())
|
||||
->setType(PHUITagView::TYPE_SHADE)
|
||||
->setColor(PHUITagView::COLOR_RED)
|
||||
->setIcon('fa-ban')
|
||||
->setName(pht('Archived')));
|
||||
}
|
||||
break;
|
||||
case self::HEADER_MODE_NORMAL:
|
||||
default:
|
||||
|
|
|
@ -71,6 +71,13 @@ final class PhabricatorDashboardRenderingEngine extends Phobject {
|
|||
foreach ($column->getPanelRefs() as $panel_ref) {
|
||||
$panel_phid = $panel_ref->getPanelPHID();
|
||||
|
||||
$panel = idx($panels, $panel_phid);
|
||||
|
||||
// Do not render Archived panels in view mode.
|
||||
if ($panel && $panel->getIsArchived() && !$is_editable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$panel_engine = id(new PhabricatorDashboardPanelRenderingEngine())
|
||||
->setViewer($viewer)
|
||||
->setEnableAsyncRendering(true)
|
||||
|
@ -83,7 +90,6 @@ final class PhabricatorDashboardRenderingEngine extends Phobject {
|
|||
->setMovable(true)
|
||||
->setPanelHandle($handles[$panel_phid]);
|
||||
|
||||
$panel = idx($panels, $panel_phid);
|
||||
if ($panel) {
|
||||
$panel_engine->setPanel($panel);
|
||||
}
|
||||
|
|
|
@ -181,7 +181,7 @@ final class PhabricatorDashboardTabsPanelType
|
|||
->setIcon('fa-chevron-left')
|
||||
->setHref($prev_uri)
|
||||
->setWorkflow(true)
|
||||
->setDisabled(($prev_key === null) || !$can_edit));
|
||||
->setDisabled($prev_key === null));
|
||||
|
||||
$dropdown_menu->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
|
@ -189,7 +189,7 @@ final class PhabricatorDashboardTabsPanelType
|
|||
->setIcon('fa-chevron-right')
|
||||
->setHref($next_uri)
|
||||
->setWorkflow(true)
|
||||
->setDisabled(($next_key === null) || !$can_edit));
|
||||
->setDisabled($next_key === null));
|
||||
|
||||
$dropdown_menu->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
|
@ -282,6 +282,8 @@ final class PhabricatorDashboardTabsPanelType
|
|||
$panel_content = pht('(Invalid Panel)');
|
||||
}
|
||||
|
||||
$is_selected = (string)$idx === (string)$selected;
|
||||
|
||||
$content_id = celerity_generate_unique_node_id();
|
||||
|
||||
$content[] = phutil_tag(
|
||||
|
|
|
@ -19,6 +19,13 @@ final class PhabricatorDashboardPanelQuery
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to get only the Archived (`true`), only the not
|
||||
* Archived (`false`) or all (`null`). Default to `null` (no filter).
|
||||
*
|
||||
* @param null|bool $archived
|
||||
* @return self
|
||||
*/
|
||||
public function withArchived($archived) {
|
||||
$this->archived = $archived;
|
||||
return $this;
|
||||
|
|
|
@ -56,7 +56,7 @@ final class DifferentialGetCommitMessageConduitAPIMethod
|
|||
// show "Field:" templates for some fields even if they are empty.
|
||||
$edit_mode = $request->getValue('edit');
|
||||
|
||||
$is_any_edit = (bool)strlen($edit_mode);
|
||||
$is_any_edit = (bool)$edit_mode;
|
||||
$is_create = ($edit_mode == 'create');
|
||||
|
||||
$field_list = DifferentialCommitMessageField::newEnabledFields($viewer);
|
||||
|
@ -115,7 +115,7 @@ final class DifferentialGetCommitMessageConduitAPIMethod
|
|||
|
||||
$is_title = ($field_key == $key_title);
|
||||
|
||||
if (!strlen($value)) {
|
||||
if ($value === null || $value === '') {
|
||||
if ($is_template) {
|
||||
$commit_message[] = $label.': ';
|
||||
}
|
||||
|
|
|
@ -451,10 +451,9 @@ final class DifferentialChangesetViewController extends DifferentialController {
|
|||
continue;
|
||||
}
|
||||
$coverage_data = idx($test_coverage, $changeset->getFileName());
|
||||
if (!strlen($coverage_data)) {
|
||||
continue;
|
||||
if (phutil_nonempty_string($coverage_data)) {
|
||||
$coverage[] = $coverage_data;
|
||||
}
|
||||
$coverage[] = $coverage_data;
|
||||
}
|
||||
|
||||
if (!$coverage) {
|
||||
|
|
|
@ -197,7 +197,6 @@ final class DifferentialDiffViewController extends DifferentialController {
|
|||
if (empty($revisions[$selected_id])) {
|
||||
$selected = id(new DifferentialRevisionQuery())
|
||||
->setViewer($viewer)
|
||||
->withAuthors(array($viewer->getPHID()))
|
||||
->withIDs(array($selected_id))
|
||||
->requireCapabilities(
|
||||
array(
|
||||
|
|
|
@ -45,7 +45,7 @@ final class DifferentialBranchField
|
|||
return pht('%s (bookmark)', $bookmark);
|
||||
} else if (strlen($branch)) {
|
||||
$onto = $diff->loadTargetBranch();
|
||||
if (strlen($onto) && ($onto !== $branch)) {
|
||||
if (phutil_nonempty_string($onto) && ($onto !== $branch)) {
|
||||
return pht(
|
||||
'%s (branched from %s)',
|
||||
$branch,
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
final class DifferentialBranchFieldTestCase extends PhabricatorTestCase {
|
||||
|
||||
protected function getPhabricatorTestCaseConfiguration() {
|
||||
return array(
|
||||
self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES => true,
|
||||
);
|
||||
}
|
||||
|
||||
private function getTestDiff() {
|
||||
$parser = new ArcanistDiffParser();
|
||||
$raw_diff = <<<EODIFF
|
||||
diff --git a/src b/src
|
||||
index 123457..bb216b1 100644
|
||||
--- a/src
|
||||
+++ b/src
|
||||
@@ -1,5 +1,5 @@
|
||||
Line a
|
||||
-Line b
|
||||
+Line 2
|
||||
Line c
|
||||
Line d
|
||||
Line e
|
||||
EODIFF;
|
||||
|
||||
return DifferentialDiff::newFromRawChanges(
|
||||
PhabricatorUser::getOmnipotentUser(),
|
||||
$parser->parseDiff($raw_diff));
|
||||
}
|
||||
|
||||
public function testRenderDiffPropertyViewValue() {
|
||||
$test_object = new DifferentialBranchField();
|
||||
$diff = $this->getTestDiff();
|
||||
$diff->setBranch('test');
|
||||
$this->assertEqual('test',
|
||||
$test_object->renderDiffPropertyViewValue($diff));
|
||||
}
|
||||
}
|
|
@ -189,7 +189,7 @@ final class DifferentialRevisionEditEngine
|
|||
|
||||
// Don't show the "Author" field when creating a revision using the web
|
||||
// workflow, since it adds more noise than signal to this workflow.
|
||||
if ($this->getIsCreate()) {
|
||||
if ($is_create) {
|
||||
$author_field->setIsHidden(true);
|
||||
}
|
||||
|
||||
|
@ -239,6 +239,12 @@ final class DifferentialRevisionEditEngine
|
|||
->setConduitTypeDescription(pht('New reviewers.'))
|
||||
->setValue($object->getReviewerPHIDsForEdit());
|
||||
|
||||
// Prefill Repository for example when coming from "Attach To".
|
||||
$repository_phid = $object->getRepositoryPHID();
|
||||
if ($is_create && !$repository_phid && $diff) {
|
||||
$repository_phid = $diff->getRepositoryPHID();
|
||||
}
|
||||
|
||||
$fields[] = id(new PhabricatorDatasourceEditField())
|
||||
->setKey('repositoryPHID')
|
||||
->setLabel(pht('Repository'))
|
||||
|
@ -248,7 +254,7 @@ final class DifferentialRevisionEditEngine
|
|||
->setDescription(pht('The repository the revision belongs to.'))
|
||||
->setConduitDescription(pht('Change the repository for this revision.'))
|
||||
->setConduitTypeDescription(pht('New repository.'))
|
||||
->setSingleValue($object->getRepositoryPHID());
|
||||
->setSingleValue($repository_phid);
|
||||
|
||||
// This is a little flimsy, but allows "Maniphest Tasks: ..." to continue
|
||||
// working properly in commit messages until we fully sort out T5873.
|
||||
|
|
|
@ -60,7 +60,7 @@ abstract class DifferentialCommitMessageField
|
|||
}
|
||||
|
||||
public function renderFieldValue($value) {
|
||||
if (!strlen($value)) {
|
||||
if (!phutil_nonempty_string($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ final class DifferentialRevisionIDCommitMessageField
|
|||
}
|
||||
|
||||
public function renderFieldValue($value) {
|
||||
if (!strlen($value)) {
|
||||
if (!phutil_nonempty_string($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -28,4 +28,21 @@ final class DifferentialCommitMessageFieldTestCase
|
|||
unset($env);
|
||||
}
|
||||
|
||||
public function testRenderFieldValue() {
|
||||
$test_object = new DifferentialRevertPlanCommitMessageField();
|
||||
$this->assertEqual('foo', $test_object->renderFieldValue('foo'),
|
||||
'Normal strings should be rendered unaltered');
|
||||
|
||||
$this->assertEqual(null, $test_object->renderFieldValue(''),
|
||||
'Empty strings should be returned as null');
|
||||
|
||||
$this->assertEqual(null, $test_object->renderFieldValue(null),
|
||||
'null values strings should be returned as null');
|
||||
|
||||
$test_object = new DifferentialRevisionIDCommitMessageField();
|
||||
$expected = 'http://phabricator.example.com/D123';
|
||||
$this->assertEqual($expected, $test_object->renderFieldValue('123'));
|
||||
$this->assertEqual(null, $test_object->renderFieldValue(null));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -780,7 +780,7 @@ final class DifferentialDiff
|
|||
$refs = array();
|
||||
|
||||
$branch = $this->getBranch();
|
||||
if (strlen($branch)) {
|
||||
if (phutil_nonempty_string($branch)) {
|
||||
$refs[] = array(
|
||||
'type' => 'branch',
|
||||
'name' => $branch,
|
||||
|
@ -788,7 +788,7 @@ final class DifferentialDiff
|
|||
}
|
||||
|
||||
$onto = $this->loadTargetBranch();
|
||||
if (strlen($onto)) {
|
||||
if (phutil_nonempty_string($onto)) {
|
||||
$refs[] = array(
|
||||
'type' => 'onto',
|
||||
'name' => $onto,
|
||||
|
@ -796,7 +796,7 @@ final class DifferentialDiff
|
|||
}
|
||||
|
||||
$base = $this->getSourceControlBaseRevision();
|
||||
if (strlen($base)) {
|
||||
if ($base !== null && strlen($base)) {
|
||||
$refs[] = array(
|
||||
'type' => 'base',
|
||||
'identifier' => $base,
|
||||
|
@ -804,7 +804,7 @@ final class DifferentialDiff
|
|||
}
|
||||
|
||||
$bookmark = $this->getBookmark();
|
||||
if (strlen($bookmark)) {
|
||||
if (phutil_nonempty_string($bookmark)) {
|
||||
$refs[] = array(
|
||||
'type' => 'bookmark',
|
||||
'name' => $bookmark,
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
<?php
|
||||
|
||||
final class DifferentialDiffTestCase extends PhutilTestCase {
|
||||
final class DifferentialDiffTestCase extends PhabricatorTestCase {
|
||||
|
||||
protected function getPhabricatorTestCaseConfiguration() {
|
||||
return array(
|
||||
self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES => true,
|
||||
);
|
||||
}
|
||||
|
||||
public function testDetectCopiedCode() {
|
||||
$copies = $this->detectCopiesIn('lint_engine.diff');
|
||||
|
@ -73,5 +79,35 @@ EODIFF;
|
|||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
public function testGetFieldValuesForConduit() {
|
||||
|
||||
$parser = new ArcanistDiffParser();
|
||||
$raw_diff = <<<EODIFF
|
||||
diff --git a/src b/src
|
||||
index 123457..bb216b1 100644
|
||||
--- a/src
|
||||
+++ b/src
|
||||
@@ -1,5 +1,5 @@
|
||||
Line a
|
||||
-Line b
|
||||
+Line 2
|
||||
Line c
|
||||
Line d
|
||||
Line e
|
||||
EODIFF;
|
||||
|
||||
$diff = DifferentialDiff::newFromRawChanges(
|
||||
PhabricatorUser::getOmnipotentUser(),
|
||||
$parser->parseDiff($raw_diff));
|
||||
$this->assertTrue(true);
|
||||
|
||||
$field_values = $diff->getFieldValuesForConduit();
|
||||
$this->assertTrue(is_array($field_values));
|
||||
foreach (['revisionPHID', 'authorPHID', 'repositoryPHID', 'refs']
|
||||
as $key) {
|
||||
$this->assertTrue(array_key_exists($key, $field_values));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ final class DiffusionBrowseQueryConduitAPIMethod
|
|||
$repository = $drequest->getRepository();
|
||||
|
||||
$path = $request->getValue('path');
|
||||
if (!strlen($path) || $path === '/') {
|
||||
if (!phutil_nonempty_string($path) || $path === '/') {
|
||||
$path = null;
|
||||
}
|
||||
|
||||
|
|
|
@ -47,14 +47,14 @@ final class DiffusionHistoryQueryConduitAPIMethod
|
|||
$against_hash = $request->getValue('against');
|
||||
|
||||
$path = $request->getValue('path');
|
||||
if (!strlen($path)) {
|
||||
if (!phutil_nonempty_string($path)) {
|
||||
$path = null;
|
||||
}
|
||||
|
||||
$offset = $request->getValue('offset');
|
||||
$limit = $request->getValue('limit');
|
||||
|
||||
if (strlen($against_hash)) {
|
||||
if (phutil_nonempty_string($against_hash)) {
|
||||
$commit_range = "{$against_hash}..{$commit_hash}";
|
||||
} else {
|
||||
$commit_range = $commit_hash;
|
||||
|
|
|
@ -22,7 +22,7 @@ final class DiffusionBrowseController extends DiffusionController {
|
|||
// list.
|
||||
|
||||
$grep = $request->getStr('grep');
|
||||
if (strlen($grep)) {
|
||||
if (phutil_nonempty_string($grep)) {
|
||||
return $this->browseSearch();
|
||||
}
|
||||
|
||||
|
|
|
@ -27,8 +27,9 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
|
||||
// If this page is being accessed via "/source/xyz/commit/...", redirect
|
||||
// to the canonical URI.
|
||||
$has_callsign = strlen($request->getURIData('repositoryCallsign'));
|
||||
$has_id = strlen($request->getURIData('repositoryID'));
|
||||
$has_callsign =
|
||||
phutil_nonempty_string($request->getURIData('repositoryCallsign'));
|
||||
$has_id = phutil_nonempty_string($request->getURIData('repositoryID'));
|
||||
if (!$has_callsign && !$has_id) {
|
||||
$canonical_uri = $repository->getCommitURI($commit_identifier);
|
||||
return id(new AphrontRedirectResponse())
|
||||
|
@ -922,7 +923,7 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
|
||||
private function linkBugtraq($corpus) {
|
||||
$url = PhabricatorEnv::getEnvConfig('bugtraq.url');
|
||||
if (!strlen($url)) {
|
||||
if (!phutil_nonempty_string($url)) {
|
||||
return $corpus;
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ final class DiffusionHistoryController extends DiffusionController {
|
|||
// ancestors appropriately, but this would currrently be prohibitively
|
||||
// expensive in the general case.
|
||||
|
||||
$show_graph = !strlen($drequest->getPath());
|
||||
$show_graph = !phutil_nonempty_string($drequest->getPath());
|
||||
if ($show_graph) {
|
||||
$history_list
|
||||
->setParents($history_results['parents'])
|
||||
|
@ -98,11 +98,10 @@ final class DiffusionHistoryController extends DiffusionController {
|
|||
$viewer = $this->getViewer();
|
||||
$repository = $drequest->getRepository();
|
||||
|
||||
$no_path = !strlen($drequest->getPath());
|
||||
if ($no_path) {
|
||||
$header_text = pht('History');
|
||||
} else {
|
||||
if (phutil_nonempty_string($drequest->getPath())) {
|
||||
$header_text = $this->renderPathLinks($drequest, $mode = 'history');
|
||||
} else {
|
||||
$header_text = pht('History');
|
||||
}
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
|
|
|
@ -183,8 +183,8 @@ final class DiffusionServeController extends DiffusionController {
|
|||
// won't prompt users who provide a username but no password otherwise.
|
||||
// See T10797 for discussion.
|
||||
|
||||
$have_user = strlen(idx($_SERVER, 'PHP_AUTH_USER'));
|
||||
$have_pass = strlen(idx($_SERVER, 'PHP_AUTH_PW'));
|
||||
$have_user = strlen(idx($_SERVER, 'PHP_AUTH_USER', ''));
|
||||
$have_pass = strlen(idx($_SERVER, 'PHP_AUTH_PW', ''));
|
||||
if ($have_user && $have_pass) {
|
||||
$username = $_SERVER['PHP_AUTH_USER'];
|
||||
$password = new PhutilOpaqueEnvelope($_SERVER['PHP_AUTH_PW']);
|
||||
|
|
|
@ -25,7 +25,7 @@ final class DiffusionTagListController extends DiffusionController {
|
|||
'offset' => $pager->getOffset(),
|
||||
);
|
||||
|
||||
if (strlen($drequest->getSymbolicCommit())) {
|
||||
if (phutil_nonempty_string($drequest->getSymbolicCommit())) {
|
||||
$is_commit = true;
|
||||
$params['commit'] = $drequest->getSymbolicCommit();
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
final class DiffusionServeControllerTestCase extends PhabricatorTestCase {
|
||||
protected function getPhabricatorTestCaseConfiguration() {
|
||||
return array(
|
||||
self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES => true,
|
||||
);
|
||||
}
|
||||
|
||||
public function testHandleRequest() {
|
||||
$aphront_request = new AphrontRequest('example.com', '/');
|
||||
$diffusion_serve_controller = new DiffusionServeController();
|
||||
|
||||
$diffusion_serve_controller->setRequest($aphront_request);
|
||||
$result = $diffusion_serve_controller->handleRequest($aphront_request);
|
||||
$this->assertTrue(true, 'handleRequest did not throw an error');
|
||||
$this->assertTrue($result instanceof PhabricatorVCSResponse,
|
||||
'handleRequest() returns PhabricatorVCSResponse object');
|
||||
}
|
||||
|
||||
}
|
|
@ -87,7 +87,7 @@ final class DiffusionDocumentRenderingEngine
|
|||
$ref->setSymbolMetadata($this->getSymbolMetadata());
|
||||
|
||||
$coverage = $drequest->loadCoverage();
|
||||
if (strlen($coverage)) {
|
||||
if (phutil_nonempty_string($coverage)) {
|
||||
$ref->addCoverage($coverage);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,4 +25,45 @@ final class DiffusionCommitHasRevisionEdgeType extends PhabricatorEdgeType {
|
|||
'The source commit is associated with the destination revision.');
|
||||
}
|
||||
|
||||
public function getTransactionAddString(
|
||||
$actor,
|
||||
$add_count,
|
||||
$add_edges) {
|
||||
|
||||
return pht(
|
||||
'%s added %s revision(s): %s.',
|
||||
$actor,
|
||||
$add_count,
|
||||
$add_edges);
|
||||
}
|
||||
|
||||
public function getTransactionRemoveString(
|
||||
$actor,
|
||||
$rem_count,
|
||||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s removed %s revision(s): %s.',
|
||||
$actor,
|
||||
$rem_count,
|
||||
$rem_edges);
|
||||
}
|
||||
|
||||
public function getTransactionEditString(
|
||||
$actor,
|
||||
$total_count,
|
||||
$add_count,
|
||||
$add_edges,
|
||||
$rem_count,
|
||||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s edited revision(s), added %s: %s; removed %s: %s.',
|
||||
$actor,
|
||||
$add_count,
|
||||
$add_edges,
|
||||
$rem_count,
|
||||
$rem_edges);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1133,7 +1133,7 @@ final class DiffusionCommitHookEngine extends Phobject {
|
|||
->setHookWait(phutil_microseconds_since($hook_start));
|
||||
|
||||
$identifier = $this->getRequestIdentifier();
|
||||
if (strlen($identifier)) {
|
||||
if ($identifier !== null && strlen($identifier)) {
|
||||
$event->setRequestIdentifier($identifier);
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,9 @@ final class DiffusionPathIDQuery extends Phobject {
|
|||
*/
|
||||
public static function normalizePath($path) {
|
||||
|
||||
// Ensure we have a string, not a null.
|
||||
$path = coalesce($path, '');
|
||||
|
||||
// Normalize to single slashes, e.g. "///" => "/".
|
||||
$path = preg_replace('@[/]{2,}@', '/', $path);
|
||||
|
||||
|
|
|
@ -254,7 +254,7 @@ abstract class DiffusionRequest extends Phobject {
|
|||
}
|
||||
|
||||
public function getPath() {
|
||||
return $this->path;
|
||||
return coalesce($this->path, '');
|
||||
}
|
||||
|
||||
public function getLine() {
|
||||
|
|
|
@ -71,7 +71,7 @@ abstract class DiffusionView extends AphrontView {
|
|||
$display_name = idx($details, 'name');
|
||||
unset($details['name']);
|
||||
|
||||
if (strlen($display_name)) {
|
||||
if (phutil_nonempty_string($display_name)) {
|
||||
$display_name = phutil_tag(
|
||||
'span',
|
||||
array(
|
||||
|
|
|
@ -346,7 +346,7 @@ final class DivinerAtom extends Phobject {
|
|||
->setContext(idx($dictionary, 'context'))
|
||||
->setLanguage(idx($dictionary, 'language'))
|
||||
->setParentHash(idx($dictionary, 'parentHash'))
|
||||
->setDocblockRaw(idx($dictionary, 'docblockRaw'))
|
||||
->setDocblockRaw(coalesce(idx($dictionary, 'docblockRaw'), ''))
|
||||
->setProperties(idx($dictionary, 'properties'));
|
||||
|
||||
foreach (idx($dictionary, 'warnings', array()) as $warning) {
|
||||
|
|
|
@ -50,7 +50,7 @@ final class DivinerMainController extends DivinerController {
|
|||
$text = pht(
|
||||
"(NOTE) **Looking for documentation?** ".
|
||||
"If you're looking for help and information about %s, ".
|
||||
"you can [[https://secure.phabricator.com/diviner/ | ".
|
||||
"you can [[https://we.phorge.it/diviner/ | ".
|
||||
"browse the public %s documentation]] on the live site.\n\n".
|
||||
"Diviner is the documentation generator used to build this ".
|
||||
"documentation.\n\n".
|
||||
|
|
|
@ -31,8 +31,10 @@ final class FileUploadConduitAPIMethod extends FileConduitAPIMethod {
|
|||
$view_policy = $request->getValue('viewPolicy');
|
||||
|
||||
$data = $request->getValue('data_base64');
|
||||
if (!phutil_nonempty_string($data)) {
|
||||
throw new Exception(pht('Field "data_base64" must be non-empty.'));
|
||||
}
|
||||
$data = $this->decodeBase64($data);
|
||||
|
||||
$params = array(
|
||||
'authorPHID' => $viewer->getPHID(),
|
||||
'canCDN' => $can_cdn,
|
||||
|
|
|
@ -20,7 +20,7 @@ final class PhabricatorFileLightboxController
|
|||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
if (strlen($comment)) {
|
||||
if (phutil_nonempty_string($comment)) {
|
||||
$xactions = array();
|
||||
$xactions[] = id(new PhabricatorFileTransaction())
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
|
||||
|
|
|
@ -287,7 +287,7 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||
// NOTE: Once we receive the first chunk, we'll detect its MIME type and
|
||||
// update the parent file if a MIME type hasn't been provided. This matters
|
||||
// for large media files like video.
|
||||
$mime_type = idx($params, 'mime-type');
|
||||
$mime_type = idx($params, 'mime-type', '');
|
||||
if (!strlen($mime_type)) {
|
||||
$file->setMimeType('application/octet-stream');
|
||||
}
|
||||
|
|
|
@ -532,4 +532,11 @@ final class PhabricatorFileTestCase extends PhabricatorTestCase {
|
|||
$this->assertEqual(array(), $alternate_c);
|
||||
}
|
||||
|
||||
public function testNewChunkedFile() {
|
||||
$engine = new PhabricatorTestStorageEngine();
|
||||
$file = PhabricatorFile::newChunkedFile($engine, 10, []);
|
||||
$this->assertTrue($file instanceof PhabricatorFile,
|
||||
pht('newChunkedFile returns a PhabricatorFile'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -102,7 +102,7 @@ final class HarbormasterBuildUnitMessage
|
|||
$obj->setDuration((float)idx($dict, 'duration'));
|
||||
|
||||
$path = idx($dict, 'path');
|
||||
if (strlen($path)) {
|
||||
if ($path !== null && strlen($path)) {
|
||||
$obj->setProperty('path', $path);
|
||||
}
|
||||
|
||||
|
|
|
@ -717,8 +717,8 @@ final class HeraldTranscriptController extends HeraldController {
|
|||
->setName(pht('Field Values'))
|
||||
->setIcon('fa-file-text-o');
|
||||
|
||||
$xaction_phids = $this->getTranscriptTransactionPHIDs($xscript);
|
||||
$has_xactions = (bool)$xaction_phids;
|
||||
$has_xactions = $xscript->getObjectTranscript()
|
||||
&& $this->getTranscriptTransactionPHIDs($xscript);
|
||||
|
||||
$nav->newLink('xactions')
|
||||
->setName(pht('Transactions'))
|
||||
|
|
|
@ -182,7 +182,8 @@ final class PhabricatorMemeEngine extends Phobject {
|
|||
// changes to the image.
|
||||
$above_text = $this->getAboveText();
|
||||
$below_text = $this->getBelowText();
|
||||
if (!strlen(trim($above_text)) && !strlen(trim($below_text))) {
|
||||
if (($above_text === null || !phutil_nonempty_string(trim($above_text))) &&
|
||||
($below_text === null || !phutil_nonempty_string(trim($below_text)))) {
|
||||
return $template_data;
|
||||
}
|
||||
|
||||
|
|
|
@ -63,6 +63,7 @@ final class PhabricatorMailManagementSendTestWorkflow
|
|||
array(
|
||||
'name' => 'type',
|
||||
'param' => 'message-type',
|
||||
'default' => PhabricatorMailEmailMessage::MESSAGETYPE,
|
||||
'help' => pht(
|
||||
'Send the specified type of message (email, sms, ...).'),
|
||||
),
|
||||
|
@ -74,10 +75,6 @@ final class PhabricatorMailManagementSendTestWorkflow
|
|||
$viewer = $this->getViewer();
|
||||
|
||||
$type = $args->getArg('type');
|
||||
if (!strlen($type)) {
|
||||
$type = PhabricatorMailEmailMessage::MESSAGETYPE;
|
||||
}
|
||||
|
||||
$type_map = PhabricatorMailExternalMessage::getAllMessageTypes();
|
||||
if (!isset($type_map[$type])) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
|
|
|
@ -65,7 +65,7 @@ final class PhabricatorMailManagementShowOutboundWorkflow
|
|||
foreach ($messages as $message_key => $message) {
|
||||
if ($args->getArg('dump-html')) {
|
||||
$html_body = $message->getHTMLBody();
|
||||
if (strlen($html_body)) {
|
||||
if (phutil_nonempty_string($html_body)) {
|
||||
$template =
|
||||
"<!doctype html><html><body>{$html_body}</body></html>";
|
||||
$console->writeOut("%s\n", $html_body);
|
||||
|
@ -188,7 +188,7 @@ final class PhabricatorMailManagementShowOutboundWorkflow
|
|||
|
||||
$info[] = null;
|
||||
$info[] = $this->newSectionHeader(pht('TEXT BODY'));
|
||||
if (strlen($message->getBody())) {
|
||||
if (phutil_nonempty_string($message->getBody())) {
|
||||
$info[] = tsprintf('%B', $message->getBody());
|
||||
} else {
|
||||
$info[] = pht('(This message has no text body.)');
|
||||
|
@ -203,7 +203,7 @@ final class PhabricatorMailManagementShowOutboundWorkflow
|
|||
|
||||
$info[] = null;
|
||||
$info[] = $this->newSectionHeader(pht('HTML BODY'));
|
||||
if (strlen($message->getHTMLBody())) {
|
||||
if (phutil_nonempty_string($message->getHTMLBody())) {
|
||||
$info[] = $message->getHTMLBody();
|
||||
$info[] = null;
|
||||
} else {
|
||||
|
|
|
@ -92,7 +92,7 @@ final class PhabricatorNotificationServersConfigType
|
|||
}
|
||||
|
||||
$path = idx($spec, 'path');
|
||||
if ($type == 'admin' && strlen($path)) {
|
||||
if ($type == 'admin' && phutil_nonempty_string($path)) {
|
||||
throw $this->newException(
|
||||
pht(
|
||||
'Notification server configuration describes an invalid host '.
|
||||
|
|
|
@ -40,34 +40,41 @@ final class PhabricatorNotificationStatusView extends AphrontTagView {
|
|||
protected function getTagContent() {
|
||||
$have = PhabricatorEnv::getEnvConfig('notification.servers');
|
||||
if ($have) {
|
||||
$icon = id(new PHUIIconView())
|
||||
->setIcon('fa-circle-o yellow');
|
||||
$text = pht('Connecting...');
|
||||
return phutil_tag(
|
||||
'span',
|
||||
array(
|
||||
'class' => 'connection-status-text '.
|
||||
'aphlict-connection-status-connecting',
|
||||
),
|
||||
array(
|
||||
$icon,
|
||||
$text,
|
||||
));
|
||||
return $this->buildMessageView(
|
||||
'aphlict-connection-status-connecting',
|
||||
'fa-circle-o yellow',
|
||||
pht('Connecting...'));
|
||||
} else {
|
||||
$text = pht('Notification server not enabled');
|
||||
$icon = id(new PHUIIconView())
|
||||
->setIcon('fa-circle-o grey');
|
||||
return phutil_tag(
|
||||
'span',
|
||||
array(
|
||||
'class' => 'connection-status-text '.
|
||||
'aphlict-connection-status-notenabled',
|
||||
),
|
||||
array(
|
||||
$icon,
|
||||
$text,
|
||||
));
|
||||
return $this->buildMessageView(
|
||||
'aphlict-connection-status-notenabled',
|
||||
'fa-circle-o grey',
|
||||
pht('Notification server not enabled'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an icon and a message.
|
||||
*
|
||||
* @param string $class_name Raw CSS class name(s) space separated
|
||||
* @param string $icon_name Icon name
|
||||
* @param string $text Text to be shown
|
||||
* @return array
|
||||
*/
|
||||
private function buildMessageView($class_name, $icon_name, $text) {
|
||||
$icon = id(new PHUIIconView())
|
||||
->setIcon($icon_name);
|
||||
|
||||
$message = phutil_tag(
|
||||
'span',
|
||||
array(
|
||||
'class' => 'connection-status-text '.$class_name,
|
||||
),
|
||||
$text);
|
||||
|
||||
return array(
|
||||
$icon,
|
||||
$message,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ final class PhabricatorUserLogView extends AphrontView {
|
|||
|
||||
$rows = array();
|
||||
foreach ($logs as $log) {
|
||||
$session = substr($log->getSession(), 0, 6);
|
||||
$session = substr(coalesce($log->getSession(), ''), 0, 6);
|
||||
|
||||
$actor_phid = $log->getActorPHID();
|
||||
$user_phid = $log->getUserPHID();
|
||||
|
|
|
@ -56,9 +56,15 @@ final class PhabricatorUserEmpowerTransaction
|
|||
'%s empowered this user as an administrator.',
|
||||
$this->renderAuthor());
|
||||
} else {
|
||||
return pht(
|
||||
'%s defrocked this user.',
|
||||
$this->renderAuthor());
|
||||
if (PhabricatorEnv::getEnvConfig('phabricator.serious-business')) {
|
||||
return pht(
|
||||
'%s removed the administrator role from this user.',
|
||||
$this->renderAuthor());
|
||||
} else {
|
||||
return pht(
|
||||
'%s defrocked this user.',
|
||||
$this->renderAuthor());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,10 +76,17 @@ final class PhabricatorUserEmpowerTransaction
|
|||
$this->renderAuthor(),
|
||||
$this->renderObject());
|
||||
} else {
|
||||
return pht(
|
||||
'%s defrocked %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject());
|
||||
if (PhabricatorEnv::getEnvConfig('phabricator.serious-business')) {
|
||||
return pht(
|
||||
'%s removed the administrator role from %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject());
|
||||
} else {
|
||||
return pht(
|
||||
'%s defrocked %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -117,6 +117,15 @@ final class PhameBlogViewController extends PhameLiveController {
|
|||
$about,
|
||||
));
|
||||
|
||||
$page->addHeadItem(phutil_tag(
|
||||
'link',
|
||||
array(
|
||||
'rel' => 'alternate',
|
||||
'type' => 'application/atom+xml',
|
||||
'href' => $blog->getFeedURI(),
|
||||
'title' => $blog->getName(),
|
||||
)));
|
||||
|
||||
return $page;
|
||||
}
|
||||
|
||||
|
|
|
@ -200,6 +200,15 @@ final class PhameBlog extends PhameDAO
|
|||
return '/phame/blog/manage/'.$this->getID().'/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get relative URI of Phame blog feed.
|
||||
*
|
||||
* @return string Relative URI of Phame blog feed
|
||||
*/
|
||||
public function getFeedURI() {
|
||||
return '/phame/blog/feed/'.$this->getID().'/';
|
||||
}
|
||||
|
||||
public function getProfileImageURI() {
|
||||
return $this->getProfileImageFile()->getBestURI();
|
||||
}
|
||||
|
|
|
@ -102,6 +102,7 @@ final class PholioInlineController extends PholioController {
|
|||
->addCancelButton($mock_uri, pht('Close'));
|
||||
}
|
||||
|
||||
$error = null;
|
||||
if ($request->isFormPost()) {
|
||||
$v_content = $request->getStr('content');
|
||||
|
||||
|
@ -112,9 +113,13 @@ final class PholioInlineController extends PholioController {
|
|||
} else if ($inline->getID()) {
|
||||
$inline->delete();
|
||||
$dictionary = array();
|
||||
} else {
|
||||
$error = pht('Comment cannot be empty.');
|
||||
}
|
||||
|
||||
return id(new AphrontAjaxResponse())->setContent($dictionary);
|
||||
if ($error === null) {
|
||||
return id(new AphrontAjaxResponse())->setContent($dictionary);
|
||||
}
|
||||
}
|
||||
|
||||
switch ($mode) {
|
||||
|
@ -151,6 +156,7 @@ final class PholioInlineController extends PholioController {
|
|||
->setUser($viewer)
|
||||
->setName('content')
|
||||
->setLabel(pht('Comment'))
|
||||
->setError($error)
|
||||
->setValue($v_content));
|
||||
|
||||
return $this->newDialog()
|
||||
|
|
|
@ -0,0 +1,169 @@
|
|||
<?php
|
||||
|
||||
final class PhrictionHovercardEngineExtension
|
||||
extends PhabricatorHovercardEngineExtension {
|
||||
|
||||
const EXTENSIONKEY = 'phriction';
|
||||
|
||||
public function isExtensionEnabled() {
|
||||
return PhabricatorApplication::isClassInstalled(
|
||||
'PhabricatorPhrictionApplication');
|
||||
}
|
||||
|
||||
public function getExtensionName() {
|
||||
return pht('Wiki Documents');
|
||||
}
|
||||
|
||||
public function canRenderObjectHovercard($object) {
|
||||
return ($object instanceof PhrictionDocument);
|
||||
}
|
||||
|
||||
public function willRenderHovercards(array $objects) {
|
||||
return array(
|
||||
'projects' => $this->getProjectHandlesOfDocuments($objects),
|
||||
'ancestors' => $this->getAncestorHandlesOfDocuments($objects),
|
||||
);
|
||||
}
|
||||
|
||||
public function renderHovercard(
|
||||
PHUIHovercardView $hovercard,
|
||||
PhabricatorObjectHandle $handle,
|
||||
$object,
|
||||
$data) {
|
||||
|
||||
$viewer = $this->getViewer();
|
||||
$phid = $object->getPHID();
|
||||
|
||||
$detail_content = array(
|
||||
id(new PHUIIconView())->setIcon('fa-book'),
|
||||
);
|
||||
|
||||
$ancestor_handles = $data['ancestors'][$phid];
|
||||
if ($ancestor_handles) {
|
||||
foreach ($ancestor_handles as $ancestor_handle) {
|
||||
$detail_content[] = phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $ancestor_handle->getUri(),
|
||||
),
|
||||
$ancestor_handle->getName());
|
||||
|
||||
$detail_content[] = id(new PHUIIconView())
|
||||
->setIcon('fa-angle-right')
|
||||
->addClass('phui-crumb-divider');
|
||||
}
|
||||
array_pop($detail_content);
|
||||
} else {
|
||||
$detail_content[] = pht('Wiki Document');
|
||||
}
|
||||
|
||||
$project_handles = $data['projects'][$phid];
|
||||
if ($project_handles) {
|
||||
$list = id(new PHUIHandleTagListView())
|
||||
->setHandles($project_handles)
|
||||
->setSlim(true)
|
||||
->setShowHovercards(false);
|
||||
|
||||
$detail_content[] = $list;
|
||||
}
|
||||
|
||||
$hovercard->setDetail(
|
||||
phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'phui-hovercard-object-type',
|
||||
),
|
||||
$detail_content));
|
||||
|
||||
$content = $object->getContent();
|
||||
|
||||
if ($content) {
|
||||
$hovercard->addField(
|
||||
pht('Last Author'),
|
||||
$viewer->renderHandle($content->getAuthorPHID()));
|
||||
|
||||
$hovercard->addField(
|
||||
pht('Last Edited'),
|
||||
phabricator_dual_datetime($content->getDateCreated(), $viewer));
|
||||
}
|
||||
}
|
||||
|
||||
private function getProjectHandlesOfDocuments($documents) {
|
||||
$viewer = $this->getViewer();
|
||||
$project_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
|
||||
$project_phids = array();
|
||||
$project_map = array();
|
||||
|
||||
$project_edges = id(new PhabricatorEdgeQuery())
|
||||
->withSourcePHIDs(mpull($documents, 'getPHID'))
|
||||
->withEdgeTypes(array($project_edge_type))
|
||||
->execute();
|
||||
|
||||
foreach ($project_edges as $document_phid => $edge_types) {
|
||||
$document_project_phids = array_keys($edge_types[$project_edge_type]);
|
||||
|
||||
$project_map[$document_phid] = array_reverse($document_project_phids);
|
||||
foreach ($document_project_phids as $project_phid) {
|
||||
if (!in_array($project_phid, $project_phids, true)) {
|
||||
$project_phids[] = $project_phid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($project_phids) {
|
||||
$project_handles = $viewer->loadHandles($project_phids);
|
||||
$project_handles = iterator_to_array($project_handles);
|
||||
$project_handles = mpull($project_handles, null, 'getPHID');
|
||||
|
||||
foreach ($project_map as $key => $document_project_phids) {
|
||||
$project_map[$key] = array_select_keys(
|
||||
$project_handles,
|
||||
$document_project_phids);
|
||||
}
|
||||
}
|
||||
|
||||
return $project_map;
|
||||
}
|
||||
|
||||
private function getAncestorHandlesOfDocuments($documents) {
|
||||
$viewer = $this->getViewer();
|
||||
$ancestor_slugs = array();
|
||||
$ancestor_map = array();
|
||||
|
||||
foreach ($documents as $document) {
|
||||
$document_phid = $document->getPHID();
|
||||
$document_ancestor_slugs = PhabricatorSlug::getAncestry(
|
||||
$document->getSlug());
|
||||
|
||||
$ancestor_map[$document_phid] = $document_ancestor_slugs;
|
||||
foreach ($document_ancestor_slugs as $slug) {
|
||||
if (!in_array($slug, $ancestor_slugs, true)) {
|
||||
$ancestor_slugs[] = $slug;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($ancestor_slugs) {
|
||||
$ancestors = id(new PhrictionDocumentQuery())
|
||||
->setViewer($viewer)
|
||||
->withSlugs($ancestor_slugs)
|
||||
->execute();
|
||||
$ancestor_phids = mpull($ancestors, 'getPHID', 'getSlug');
|
||||
$ancestor_handles = $viewer->loadHandles($ancestor_phids);
|
||||
$ancestor_handles = iterator_to_array($ancestor_handles);
|
||||
$ancestor_handles = mpull($ancestor_handles, null, 'getPHID');
|
||||
|
||||
foreach ($ancestor_map as $key => $document_ancestor_slugs) {
|
||||
$document_ancestor_phids = array_select_keys(
|
||||
$ancestor_phids,
|
||||
$document_ancestor_slugs);
|
||||
$ancestor_map[$key] = array_select_keys(
|
||||
$ancestor_handles,
|
||||
$document_ancestor_phids);
|
||||
}
|
||||
}
|
||||
|
||||
return $ancestor_map;
|
||||
}
|
||||
|
||||
}
|
|
@ -136,7 +136,6 @@ final class PhabricatorProjectColumnHideController
|
|||
->setWidth(AphrontDialogView::WIDTH_FORM)
|
||||
->setTitle($title)
|
||||
->appendChild($body)
|
||||
->setDisableWorkflowOnCancel(true)
|
||||
->addCancelButton($view_uri)
|
||||
->addSubmitButton($button);
|
||||
|
||||
|
|
|
@ -28,6 +28,26 @@ final class PhabricatorProjectHovercardEngineExtension
|
|||
->execute();
|
||||
$projects = mpull($projects, null, 'getPHID');
|
||||
|
||||
$custom_fields = array();
|
||||
foreach ($projects as $project) {
|
||||
$field = PhabricatorCustomField::getObjectField(
|
||||
$project,
|
||||
PhabricatorCustomField::ROLE_VIEW,
|
||||
'std:project:internal:description');
|
||||
if ($field === null) {
|
||||
// This means the field is disabled, it would always be null.
|
||||
break;
|
||||
}
|
||||
$field
|
||||
->setViewer($viewer)
|
||||
->readValueFromObject($project);
|
||||
$custom_fields[] = $field;
|
||||
}
|
||||
|
||||
id(new PhabricatorCustomFieldStorageQuery())
|
||||
->addFields($custom_fields)
|
||||
->execute();
|
||||
|
||||
return array(
|
||||
'projects' => $projects,
|
||||
);
|
||||
|
|
|
@ -271,6 +271,19 @@ final class PhabricatorProjectColumn
|
|||
pht(
|
||||
'For columns that proxy another object (like a subproject or '.
|
||||
'milestone), the PHID of the object they proxy.')),
|
||||
id(new PhabricatorConduitSearchFieldSpecification())
|
||||
->setKey('isHidden')
|
||||
->setType('bool')
|
||||
->setDescription(pht('True if this column is hidden.')),
|
||||
id(new PhabricatorConduitSearchFieldSpecification())
|
||||
->setKey('isDefaultColumn')
|
||||
->setType('bool')
|
||||
->setDescription(pht('True if this is the default column.')),
|
||||
id(new PhabricatorConduitSearchFieldSpecification())
|
||||
->setKey('sequence')
|
||||
->setType('int')
|
||||
->setDescription(
|
||||
pht('The sequence in which this column appears on the workboard.')),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -279,6 +292,9 @@ final class PhabricatorProjectColumn
|
|||
'name' => $this->getDisplayName(),
|
||||
'proxyPHID' => $this->getProxyPHID(),
|
||||
'project' => $this->getProject()->getRefForConduit(),
|
||||
'isHidden' => $this->isHidden(),
|
||||
'isDefaultColumn' => $this->isDefaultColumn(),
|
||||
'sequence' => (int)$this->getSequence(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -68,6 +68,28 @@ final class PhabricatorProjectCardView extends AphrontTagView {
|
|||
|
||||
$description = null;
|
||||
|
||||
// This getProxy() feels hacky - see also PhabricatorProjectDatasource:67
|
||||
$description_field = PhabricatorCustomField::getObjectField(
|
||||
$project,
|
||||
PhabricatorCustomField::ROLE_VIEW,
|
||||
'std:project:internal:description');
|
||||
|
||||
if ($description_field !== null) {
|
||||
$description_field = $description_field->getProxy();
|
||||
|
||||
$description = $description_field->getFieldValue();
|
||||
if (phutil_nonempty_string($description)) {
|
||||
$description = PhabricatorMarkupEngine::summarizeSentence($description);
|
||||
$description = id(new PHUIRemarkupView($viewer, $description))
|
||||
->setContextObject($project);
|
||||
|
||||
$description = phutil_tag(
|
||||
'div',
|
||||
array('class' => 'project-card-body phui-header-shell'),
|
||||
$description);
|
||||
}
|
||||
}
|
||||
|
||||
$card = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
|
|
|
@ -42,7 +42,7 @@ final class PhabricatorRepositoryManagementMaintenanceWorkflow
|
|||
}
|
||||
|
||||
$message = $args->getArg('start');
|
||||
$is_start = (bool)strlen($message);
|
||||
$is_start = $message !== null;
|
||||
$is_stop = $args->getArg('stop');
|
||||
|
||||
if (!$is_start && !$is_stop) {
|
||||
|
|
|
@ -779,15 +779,15 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
|||
break;
|
||||
case 'compare':
|
||||
$uri = $this->getPathURI("/{$action}/");
|
||||
if (strlen($head)) {
|
||||
if (phutil_nonempty_scalar($head)) {
|
||||
$query['head'] = $head;
|
||||
} else if (strlen($raw_commit)) {
|
||||
} else if (phutil_nonempty_scalar($raw_commit)) {
|
||||
$query['commit'] = $raw_commit;
|
||||
} else if (strlen($raw_branch)) {
|
||||
} else if (phutil_nonempty_scalar($raw_branch)) {
|
||||
$query['head'] = $raw_branch;
|
||||
}
|
||||
|
||||
if (strlen($against)) {
|
||||
if (phutil_nonempty_scalar($against)) {
|
||||
$query['against'] = $against;
|
||||
}
|
||||
break;
|
||||
|
@ -1160,7 +1160,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
|||
*/
|
||||
public function getRemoteURIObject() {
|
||||
$raw_uri = $this->getDetail('remote-uri');
|
||||
if (!strlen($raw_uri)) {
|
||||
if (!phutil_nonempty_string($raw_uri)) {
|
||||
return new PhutilURI('');
|
||||
}
|
||||
|
||||
|
|
|
@ -25,12 +25,12 @@ final class PhabricatorRepositoryCallsignTransaction
|
|||
$old = $this->getOldValue();
|
||||
$new = $this->getNewValue();
|
||||
|
||||
if (!strlen($old)) {
|
||||
if ($old === null) {
|
||||
return pht(
|
||||
'%s set the callsign for this repository to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderNewValue());
|
||||
} else if (!strlen($new)) {
|
||||
} else if ($new === null) {
|
||||
return pht(
|
||||
'%s removed the callsign (%s) for this repository.',
|
||||
$this->renderAuthor(),
|
||||
|
|
|
@ -17,12 +17,12 @@ final class PhabricatorRepositoryEncodingTransaction
|
|||
$old = $this->getOldValue();
|
||||
$new = $this->getNewValue();
|
||||
|
||||
if (strlen($old) && !strlen($new)) {
|
||||
if ($old !== null && $new === null) {
|
||||
return pht(
|
||||
'%s removed the %s encoding configured for this repository.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderOldValue());
|
||||
} else if (strlen($new) && !strlen($old)) {
|
||||
} else if ($new !== null && $old === null) {
|
||||
return pht(
|
||||
'%s set the encoding for this repository to %s.',
|
||||
$this->renderAuthor(),
|
||||
|
|
|
@ -25,11 +25,14 @@ final class PhabricatorRepositoryMaintenanceTransaction
|
|||
$old = $this->getOldValue();
|
||||
$new = $this->getNewValue();
|
||||
|
||||
if (strlen($old) && !strlen($new)) {
|
||||
$old_nonempty = phutil_nonempty_string($old);
|
||||
$new_nonempty = phutil_nonempty_string($new);
|
||||
|
||||
if ($old_nonempty && !$new_nonempty) {
|
||||
return pht(
|
||||
'%s took this repository out of maintenance mode.',
|
||||
$this->renderAuthor());
|
||||
} else if (!strlen($old) && strlen($new)) {
|
||||
} else if (!$old_nonempty && $new_nonempty) {
|
||||
return pht(
|
||||
'%s put this repository into maintenance mode.',
|
||||
$this->renderAuthor());
|
||||
|
|
|
@ -17,12 +17,12 @@ final class PhabricatorRepositoryStagingURITransaction
|
|||
$old = $this->getOldValue();
|
||||
$new = $this->getNewValue();
|
||||
|
||||
if (!strlen($old)) {
|
||||
if ($old === null || !strlen($old)) {
|
||||
return pht(
|
||||
'%s set %s as the staging area for this repository.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderNewValue());
|
||||
} else if (!strlen($new)) {
|
||||
} else if ($new === null || !strlen($new)) {
|
||||
return pht(
|
||||
'%s removed %s as the staging area for this repository.',
|
||||
$this->renderAuthor(),
|
||||
|
|
|
@ -13,7 +13,7 @@ final class PhabricatorSearchController
|
|||
$viewer = $this->getViewer();
|
||||
$query = $request->getStr('query');
|
||||
|
||||
if ($request->getStr('jump') != 'no' && strlen($query)) {
|
||||
if ($request->getStr('jump') != 'no' && phutil_nonempty_string($query)) {
|
||||
$jump_uri = id(new PhabricatorDatasourceEngine())
|
||||
->setViewer($viewer)
|
||||
->newJumpURI($query);
|
||||
|
|
|
@ -1033,12 +1033,22 @@ abstract class PhabricatorApplicationTransaction
|
|||
count($rem),
|
||||
$this->renderSubscriberList($rem, 'rem'));
|
||||
} else if ($add) {
|
||||
if ($this->isSelfSubscription()) {
|
||||
return pht(
|
||||
'%s subscribed.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
}
|
||||
return pht(
|
||||
'%s added %d subscriber(s): %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
count($add),
|
||||
$this->renderSubscriberList($add, 'add'));
|
||||
} else if ($rem) {
|
||||
if ($this->isSelfSubscription()) {
|
||||
return pht(
|
||||
'%s unsubscribed.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
}
|
||||
return pht(
|
||||
'%s removed %d subscriber(s): %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
|
|
|
@ -139,7 +139,7 @@ server.modules list:
|
|||
|
||||
Finally, you should run the following commands to enable php support:
|
||||
|
||||
$ sudo apt-get install php5-cgi # for Ubuntu; other distros should be similar
|
||||
$ sudo apt-get install php-cgi # for Ubuntu; other distros should be similar
|
||||
$ sudo lighty-enable-mod fastcgi-php
|
||||
|
||||
Restart lighttpd after making your edits, then continue below.
|
||||
|
|
|
@ -46,6 +46,62 @@ If you want finer-grained control, you can use:
|
|||
NOTE: When you upgrade Phorge or change configuration, you should restart
|
||||
the daemons by running `phd restart`.
|
||||
|
||||
Automatically start phd
|
||||
=======================
|
||||
|
||||
NOTE: If you are opinionated against systemd, cover the eyes of your children
|
||||
right now!1!
|
||||
|
||||
Computers are good in automatically starting stuff, thanks to the invention
|
||||
of the "init system".
|
||||
|
||||
Phorge virtually supports any init system. Which one is yours? Don't worry.
|
||||
If you don't know, it's systemd.
|
||||
|
||||
We propose a minimal systemd configuration file, following some assumptions:
|
||||
|
||||
- your lovely Phorge is installed `/somewhere`
|
||||
- you have a database service called `mariadb`
|
||||
- you have a dedicated Unix user called `daemon-user` - coming from
|
||||
@{article:Diffusion User Guide}
|
||||
|
||||
With the above assumptions, create this configuration file as super-user:
|
||||
|
||||
```lang=ini,name=/etc/systemd/system/phorge-phd.service
|
||||
[Unit]
|
||||
Description=Phorge Daemons
|
||||
Documentation=https://we.phorge.it/book/phorge/article/managing_daemons/
|
||||
After=syslog.target network.target mariadb.service
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
User=daemon-user
|
||||
Group=daemon-user
|
||||
ExecStart=/somewhere/phorge/bin/phd start
|
||||
ExecStop=/somewhere/phorge/bin/phd stop
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
To install this new systemd configuration, execute these commands as
|
||||
super-user:
|
||||
|
||||
```
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now phorge-phd
|
||||
```
|
||||
|
||||
Now the process has started and will survive after any reboot.
|
||||
|
||||
To check if everything is OK:
|
||||
|
||||
```
|
||||
systemctl status phorge-phd
|
||||
```
|
||||
|
||||
Anything else can be explored in depth by reading the systemd documentation.
|
||||
|
||||
= Daemon Console =
|
||||
|
||||
You can view status and debugging information for daemons in the Daemon Console
|
||||
|
|
|
@ -112,5 +112,5 @@ like one of these on your system, or a different command:
|
|||
|
||||
```
|
||||
$ sudo /etc/init.d/php-fpm restart
|
||||
$ sudo service php5-fpm reload
|
||||
$ sudo service php-fpm reload
|
||||
```
|
||||
|
|
|
@ -123,7 +123,7 @@ Here's a general description of what you need to install:
|
|||
- MySQL Server (usually "mysqld" or "mysql-server" or "mariadb-server")
|
||||
- PHP (usually "php")
|
||||
- Required PHP extensions: mbstring, iconv, mysql (or mysqli), curl, pcntl
|
||||
(these might be something like "php-mysql" or "php5-mysqlnd")
|
||||
(these might be something like "php-mysql" or "php-mysqlnd")
|
||||
- Optional PHP extensions: gd, zip
|
||||
|
||||
If you already have LAMP setup, you've probably already got everything you need.
|
||||
|
@ -137,6 +137,12 @@ dependencies:
|
|||
somewhere/ $ git clone https://we.phorge.it/source/arcanist.git
|
||||
somewhere/ $ git clone https://we.phorge.it/source/phorge.git
|
||||
|
||||
After cloning, flag them as safe (to avoid the error //"fatal:
|
||||
detected dubious ownership in repository at ..."//):
|
||||
|
||||
$ sudo git config --system --add safe.directory somewhere/arcanist
|
||||
$ sudo git config --system --add safe.directory somewhere/phorge
|
||||
|
||||
Next Steps
|
||||
==========
|
||||
|
||||
|
|
|
@ -393,7 +393,7 @@ final class PhabricatorDatabaseRef
|
|||
|
||||
if ($is_replica) {
|
||||
$latency = idx($replica_status, 'Seconds_Behind_Master');
|
||||
if (!strlen($latency)) {
|
||||
if (!phutil_nonempty_string($latency)) {
|
||||
$ref->setReplicaStatus(self::REPLICATION_NOT_REPLICATING);
|
||||
} else {
|
||||
$latency = (int)$latency;
|
||||
|
|
|
@ -301,10 +301,14 @@ abstract class PhabricatorStandardCustomField
|
|||
}
|
||||
|
||||
public function renderPropertyViewValue(array $handles) {
|
||||
if (!strlen($this->getFieldValue())) {
|
||||
return null;
|
||||
// If your field needs to render anything more complicated then a string,
|
||||
// then you should override this method.
|
||||
$value_str = phutil_string_cast($this->getFieldValue());
|
||||
|
||||
if (phutil_nonempty_string($value_str)) {
|
||||
return $value_str;
|
||||
}
|
||||
return $this->getFieldValue();
|
||||
return null;
|
||||
}
|
||||
|
||||
public function shouldAppearInApplicationSearch() {
|
||||
|
@ -389,7 +393,7 @@ abstract class PhabricatorStandardCustomField
|
|||
if (is_array($value)) {
|
||||
return empty($value);
|
||||
}
|
||||
return !strlen($value);
|
||||
return $value === null || !strlen($value);
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTitle(
|
||||
|
|
|
@ -33,7 +33,7 @@ final class PhabricatorStandardCustomFieldInt
|
|||
}
|
||||
|
||||
public function setValueFromStorage($value) {
|
||||
if (strlen($value)) {
|
||||
if (phutil_nonempty_scalar($value)) {
|
||||
$value = (int)$value;
|
||||
} else {
|
||||
$value = null;
|
||||
|
|
|
@ -16,7 +16,7 @@ final class PhabricatorEditorURIEngine
|
|||
|
||||
$pattern = $viewer->getUserSetting(PhabricatorEditorSetting::SETTINGKEY);
|
||||
|
||||
if (!strlen(trim($pattern))) {
|
||||
if ($pattern === null || trim($pattern) === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,12 @@
|
|||
final class PhabricatorEditorURIEngineTestCase
|
||||
extends PhabricatorTestCase {
|
||||
|
||||
protected function getPhabricatorTestCaseConfiguration() {
|
||||
return array(
|
||||
self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES => true,
|
||||
);
|
||||
}
|
||||
|
||||
public function testPatternParsing() {
|
||||
$map = array(
|
||||
'' => array(),
|
||||
|
@ -129,4 +135,15 @@ final class PhabricatorEditorURIEngineTestCase
|
|||
}
|
||||
}
|
||||
|
||||
public function testNewForViewer() {
|
||||
$phabricator_user = $this->generateNewTestUser();
|
||||
try {
|
||||
$engine = PhabricatorEditorURIEngine::newForViewer($phabricator_user);
|
||||
$this->assertTrue(true, 'newForViewer did not throw an error');
|
||||
} catch (Throwable $ex) {
|
||||
$this->assertTrue(false,
|
||||
'newForViewer threw an exception:'.$ex->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ function phabricator_form(PhabricatorUser $user, $attributes, $content) {
|
|||
$body = array();
|
||||
|
||||
$http_method = idx($attributes, 'method');
|
||||
$is_post = (strcasecmp($http_method, 'POST') === 0);
|
||||
$is_post = $http_method && (strcasecmp($http_method, 'POST') === 0);
|
||||
|
||||
$http_action = idx($attributes, 'action');
|
||||
|
||||
|
|
|
@ -44,7 +44,18 @@ final class PhutilRemarkupCodeBlockRule extends PhutilRemarkupBlockRule {
|
|||
}
|
||||
|
||||
public function markupText($text, $children) {
|
||||
if (preg_match('/^\s*```/', $text)) {
|
||||
// Header/footer eventually useful to be nice with "flavored markdown".
|
||||
// When it starts with ```stuff the header is 'stuff' (->language)
|
||||
// When it ends with stuff``` the footer is 'stuff' (->garbage)
|
||||
$header_line = null;
|
||||
$footer_line = null;
|
||||
|
||||
$matches = null;
|
||||
if (preg_match('/^\s*```(.*)/', $text, $matches)) {
|
||||
if (isset($matches[1])) {
|
||||
$header_line = $matches[1];
|
||||
}
|
||||
|
||||
// If this is a ```-style block, trim off the backticks and any leading
|
||||
// blank line.
|
||||
$text = preg_replace('/^\s*```(\s*\n)?/', '', $text);
|
||||
|
@ -52,6 +63,13 @@ final class PhutilRemarkupCodeBlockRule extends PhutilRemarkupBlockRule {
|
|||
}
|
||||
|
||||
$lines = explode("\n", $text);
|
||||
|
||||
// If we have a flavored header, it has sense to look for the footer.
|
||||
if ($header_line !== null && $lines) {
|
||||
$footer_line = $lines[last_key($lines)];
|
||||
}
|
||||
|
||||
// Strip final empty lines
|
||||
while ($lines && !strlen(last($lines))) {
|
||||
unset($lines[last_key($lines)]);
|
||||
}
|
||||
|
@ -65,20 +83,39 @@ final class PhutilRemarkupCodeBlockRule extends PhutilRemarkupBlockRule {
|
|||
|
||||
$parser = new PhutilSimpleOptions();
|
||||
$custom = $parser->parse(head($lines));
|
||||
$valid_options = null;
|
||||
if ($custom) {
|
||||
$valid = true;
|
||||
$valid_options = true;
|
||||
foreach ($custom as $key => $value) {
|
||||
if (!array_key_exists($key, $options)) {
|
||||
$valid = false;
|
||||
$valid_options = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($valid) {
|
||||
if ($valid_options) {
|
||||
array_shift($lines);
|
||||
$options = $custom + $options;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse flavored markdown strictly to don't eat legitimate Remarkup.
|
||||
// Proceed only if we tried to parse options and we failed
|
||||
// (no options also mean no language).
|
||||
// For example this is not a valid option: ```php
|
||||
// Proceed only if the footer exists and it is not: blabla```
|
||||
// Accept only 2 lines or more. First line: header; then content.
|
||||
if (
|
||||
$valid_options === false &&
|
||||
$header_line !== null &&
|
||||
$footer_line === '' &&
|
||||
count($lines) > 1
|
||||
) {
|
||||
if (self::isKnownLanguageCode($header_line)) {
|
||||
array_shift($lines);
|
||||
$options['lang'] = $header_line;
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize the text back to a 0-level indent.
|
||||
$min_indent = 80;
|
||||
foreach ($lines as $line) {
|
||||
|
@ -249,4 +286,61 @@ final class PhutilRemarkupCodeBlockRule extends PhutilRemarkupBlockRule {
|
|||
$engine->highlightSource($options['lang'], $text)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a language code can be used in a generic flavored markdown.
|
||||
* @param string $lang Language code
|
||||
* @return bool
|
||||
*/
|
||||
private static function isKnownLanguageCode($lang) {
|
||||
$languages = self::knownLanguageCodes();
|
||||
return isset($languages[$lang]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the available languages for a generic flavored markdown.
|
||||
* @return array Languages as array keys. Ignore the value.
|
||||
*/
|
||||
private static function knownLanguageCodes() {
|
||||
// This is a friendly subset from https://pygments.org/languages/
|
||||
static $map = array(
|
||||
'arduino' => 1,
|
||||
'assembly' => 1,
|
||||
'awk' => 1,
|
||||
'bash' => 1,
|
||||
'bat' => 1,
|
||||
'c' => 1,
|
||||
'cmake' => 1,
|
||||
'cobol' => 1,
|
||||
'cpp' => 1,
|
||||
'css' => 1,
|
||||
'csharp' => 1,
|
||||
'dart' => 1,
|
||||
'delphi' => 1,
|
||||
'fortran' => 1,
|
||||
'go' => 1,
|
||||
'groovy' => 1,
|
||||
'haskell' => 1,
|
||||
'java' => 1,
|
||||
'javascript' => 1,
|
||||
'kotlin' => 1,
|
||||
'lisp' => 1,
|
||||
'lua' => 1,
|
||||
'matlab' => 1,
|
||||
'make' => 1,
|
||||
'perl' => 1,
|
||||
'php' => 1,
|
||||
'powershell' => 1,
|
||||
'python' => 1,
|
||||
'r' => 1,
|
||||
'ruby' => 1,
|
||||
'rust' => 1,
|
||||
'scala' => 1,
|
||||
'sh' => 1,
|
||||
'sql' => 1,
|
||||
'typescript' => 1,
|
||||
'vba' => 1,
|
||||
);
|
||||
return $map;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -114,7 +114,7 @@ final class PhutilRemarkupTableBlockRule extends PhutilRemarkupBlockRule {
|
|||
if ($cell->isContentNode()) {
|
||||
$content = $node->getContent();
|
||||
|
||||
if (!strlen(trim($content))) {
|
||||
if ($content === null || trim($content) === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
/**
|
||||
* Test cases for @{class:PhutilRemarkupEngine}.
|
||||
* @TODO: This unit is not always triggered when you need it.
|
||||
* https://we.phorge.it/T15500
|
||||
*/
|
||||
final class PhutilRemarkupEngineTestCase extends PhutilTestCase {
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
```cpp
|
||||
code
|
||||
```
|
||||
~~~~~~~~~~
|
||||
<div class="remarkup-code-block" data-code-lang="cpp" data-sigil="remarkup-code-block"><pre class="remarkup-code">code</pre></div>
|
||||
~~~~~~~~~~
|
||||
code
|
|
@ -0,0 +1,18 @@
|
|||
```#comment
|
||||
code
|
||||
|
||||
#more comment
|
||||
more code```
|
||||
|
||||
~~~~~~~~~~
|
||||
<div class="remarkup-code-block" data-code-lang="text" data-sigil="remarkup-code-block"><pre class="remarkup-code">#comment
|
||||
code
|
||||
|
||||
#more comment
|
||||
more code</pre></div>
|
||||
~~~~~~~~~~
|
||||
#comment
|
||||
code
|
||||
|
||||
#more comment
|
||||
more code
|
|
@ -0,0 +1,9 @@
|
|||
```
|
||||
cpp
|
||||
second line```
|
||||
~~~~~~~~~~
|
||||
<div class="remarkup-code-block" data-code-lang="text" data-sigil="remarkup-code-block"><pre class="remarkup-code">cpp
|
||||
second line</pre></div>
|
||||
~~~~~~~~~~
|
||||
cpp
|
||||
second line
|
|
@ -0,0 +1,20 @@
|
|||
```cpp
|
||||
code
|
||||
|
||||
more code
|
||||
|
||||
more code
|
||||
```
|
||||
|
||||
~~~~~~~~~~
|
||||
<div class="remarkup-code-block" data-code-lang="cpp" data-sigil="remarkup-code-block"><pre class="remarkup-code">code
|
||||
|
||||
more code
|
||||
|
||||
more code</pre></div>
|
||||
~~~~~~~~~~
|
||||
code
|
||||
|
||||
more code
|
||||
|
||||
more code
|
|
@ -23,6 +23,7 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView
|
|||
private $crumbs;
|
||||
private $navigation;
|
||||
private $footer;
|
||||
private $headItems = array();
|
||||
|
||||
public function setShowFooter($show_footer) {
|
||||
$this->showFooter = $show_footer;
|
||||
|
@ -375,6 +376,18 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Insert a HTML element into <head> of the page to render.
|
||||
* Used by PhameBlogViewController.
|
||||
*
|
||||
* @param PhutilSafeHTML HTML header to add
|
||||
*/
|
||||
public function addHeadItem($html) {
|
||||
if ($html instanceof PhutilSafeHTML) {
|
||||
$this->headItems[] = $html;
|
||||
}
|
||||
}
|
||||
|
||||
protected function getHead() {
|
||||
$monospaced = null;
|
||||
|
||||
|
@ -407,9 +420,10 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView
|
|||
}
|
||||
|
||||
return hsprintf(
|
||||
'%s%s%s',
|
||||
'%s%s%s%s',
|
||||
parent::getHead(),
|
||||
$font_css,
|
||||
phutil_implode_html('', $this->headItems),
|
||||
$response->renderSingleResource('javelin-magical-init', 'phabricator'));
|
||||
}
|
||||
|
||||
|
|