diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 84d27ab4aa..3fc6387e77 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -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', diff --git a/resources/sprite/login_1x/Facebook.png b/resources/sprite/login_1x/Facebook.png index 8c7201b05e..397046ffba 100644 Binary files a/resources/sprite/login_1x/Facebook.png and b/resources/sprite/login_1x/Facebook.png differ diff --git a/resources/sprite/login_1x/Google.png b/resources/sprite/login_1x/Google.png index e0989b9c56..4e476c811d 100644 Binary files a/resources/sprite/login_1x/Google.png and b/resources/sprite/login_1x/Google.png differ diff --git a/resources/sprite/login_1x/Phorge.png b/resources/sprite/login_1x/Phorge.png new file mode 100644 index 0000000000..20b6c1b215 Binary files /dev/null and b/resources/sprite/login_1x/Phorge.png differ diff --git a/resources/sprite/login_2x/Facebook.png b/resources/sprite/login_2x/Facebook.png index b3cac2bee5..c5b04bdc8e 100644 Binary files a/resources/sprite/login_2x/Facebook.png and b/resources/sprite/login_2x/Facebook.png differ diff --git a/resources/sprite/login_2x/Google.png b/resources/sprite/login_2x/Google.png index f16ad8c55b..6a206317ad 100644 Binary files a/resources/sprite/login_2x/Google.png and b/resources/sprite/login_2x/Google.png differ diff --git a/resources/sprite/login_2x/Phorge.png b/resources/sprite/login_2x/Phorge.png new file mode 100644 index 0000000000..9bcb696250 Binary files /dev/null and b/resources/sprite/login_2x/Phorge.png differ diff --git a/resources/sprite/manifest/login.json b/resources/sprite/manifest/login.json index 59312820e1..e82fd25fdb 100644 --- a/resources/sprite/manifest/login.json +++ b/resources/sprite/manifest/login.json @@ -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", diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 17560e11d1..213b6cf6b1 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -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', diff --git a/src/applications/aphlict/management/PhabricatorAphlictManagementWorkflow.php b/src/applications/aphlict/management/PhabricatorAphlictManagementWorkflow.php index 3d1a26c91d..16bdc69bc1 100644 --- a/src/applications/aphlict/management/PhabricatorAphlictManagementWorkflow.php +++ b/src/applications/aphlict/management/PhabricatorAphlictManagementWorkflow.php @@ -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 = '/'.$config; if (Filesystem::pathExists($full_path)) { break; } diff --git a/src/applications/auth/controller/PhabricatorAuthNeedsApprovalController.php b/src/applications/auth/controller/PhabricatorAuthNeedsApprovalController.php index 1121dfb14a..b098a07ddd 100644 --- a/src/applications/auth/controller/PhabricatorAuthNeedsApprovalController.php +++ b/src/applications/auth/controller/PhabricatorAuthNeedsApprovalController.php @@ -51,7 +51,7 @@ final class PhabricatorAuthNeedsApprovalController $viewer, PhabricatorAuthWaitForApprovalMessageType::MESSAGEKEY); - if (!strlen($text)) { + if (!phutil_nonempty_string($text)) { return null; } diff --git a/src/applications/auth/engine/PhabricatorAuthPasswordEngine.php b/src/applications/auth/engine/PhabricatorAuthPasswordEngine.php index a1fec4a6d2..0efb1fe4fc 100644 --- a/src/applications/auth/engine/PhabricatorAuthPasswordEngine.php +++ b/src/applications/auth/engine/PhabricatorAuthPasswordEngine.php @@ -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. diff --git a/src/applications/auth/provider/PhabricatorLDAPAuthProvider.php b/src/applications/auth/provider/PhabricatorLDAPAuthProvider.php index d1db832aa2..36a83f3678 100644 --- a/src/applications/auth/provider/PhabricatorLDAPAuthProvider.php +++ b/src/applications/auth/provider/PhabricatorLDAPAuthProvider.php @@ -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); } diff --git a/src/applications/conduit/controller/PhabricatorConduitAPIController.php b/src/applications/conduit/controller/PhabricatorConduitAPIController.php index 3ae402cf56..994f282f44 100644 --- a/src/applications/conduit/controller/PhabricatorConduitAPIController.php +++ b/src/applications/conduit/controller/PhabricatorConduitAPIController.php @@ -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); diff --git a/src/applications/config/check/PhabricatorPHPConfigSetupCheck.php b/src/applications/config/check/PhabricatorPHPConfigSetupCheck.php index 09b96d05cf..1862308052 100644 --- a/src/applications/config/check/PhabricatorPHPConfigSetupCheck.php +++ b/src/applications/config/check/PhabricatorPHPConfigSetupCheck.php @@ -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') diff --git a/src/applications/config/controller/PhabricatorConfigConsoleController.php b/src/applications/config/controller/PhabricatorConfigConsoleController.php index f70398bc5f..e545019666 100644 --- a/src/applications/config/controller/PhabricatorConfigConsoleController.php +++ b/src/applications/config/controller/PhabricatorConfigConsoleController.php @@ -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); + } + } } diff --git a/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php b/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php index 36cbd9bd51..7499c978d3 100644 --- a/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php +++ b/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php @@ -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) diff --git a/src/applications/countdown/capability/PhabricatorDashboardCreateCapability.php b/src/applications/countdown/capability/PhabricatorDashboardCreateCapability.php new file mode 100644 index 0000000000..c610df069e --- /dev/null +++ b/src/applications/countdown/capability/PhabricatorDashboardCreateCapability.php @@ -0,0 +1,16 @@ + array( + 'default' => PhabricatorPolicies::POLICY_USER, + 'caption' => pht('Default create policy for Dashboards.'), + ), + ); + } } diff --git a/src/applications/dashboard/controller/dashboard/PhabricatorDashboardAdjustController.php b/src/applications/dashboard/controller/dashboard/PhabricatorDashboardAdjustController.php index 86a3ff5805..fc04b94603 100644 --- a/src/applications/dashboard/controller/dashboard/PhabricatorDashboardAdjustController.php +++ b/src/applications/dashboard/controller/dashboard/PhabricatorDashboardAdjustController.php @@ -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(); diff --git a/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelTabsController.php b/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelTabsController.php index 2b36d3f8a2..193e4580d6 100644 --- a/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelTabsController.php +++ b/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelTabsController.php @@ -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( diff --git a/src/applications/dashboard/editor/PhabricatorDashboardEditEngine.php b/src/applications/dashboard/editor/PhabricatorDashboardEditEngine.php index 06b0a3a6fb..84b36fe546 100644 --- a/src/applications/dashboard/editor/PhabricatorDashboardEditEngine.php +++ b/src/applications/dashboard/editor/PhabricatorDashboardEditEngine.php @@ -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(); diff --git a/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php b/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php index 52e8cc70d5..cce6ee768f 100644 --- a/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php +++ b/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php @@ -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: diff --git a/src/applications/dashboard/engine/PhabricatorDashboardRenderingEngine.php b/src/applications/dashboard/engine/PhabricatorDashboardRenderingEngine.php index 9b63fcac55..935e85f8bb 100644 --- a/src/applications/dashboard/engine/PhabricatorDashboardRenderingEngine.php +++ b/src/applications/dashboard/engine/PhabricatorDashboardRenderingEngine.php @@ -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); } diff --git a/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php b/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php index 54c26dd9d3..9bc84d820e 100644 --- a/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php +++ b/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php @@ -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( diff --git a/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php b/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php index c67b756262..54f627eb00 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php +++ b/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php @@ -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; diff --git a/src/applications/differential/conduit/DifferentialGetCommitMessageConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialGetCommitMessageConduitAPIMethod.php index 51225023ff..7f659ab729 100644 --- a/src/applications/differential/conduit/DifferentialGetCommitMessageConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialGetCommitMessageConduitAPIMethod.php @@ -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.': '; } diff --git a/src/applications/differential/controller/DifferentialChangesetViewController.php b/src/applications/differential/controller/DifferentialChangesetViewController.php index 0e263d013c..c4722e8d37 100644 --- a/src/applications/differential/controller/DifferentialChangesetViewController.php +++ b/src/applications/differential/controller/DifferentialChangesetViewController.php @@ -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) { diff --git a/src/applications/differential/controller/DifferentialDiffViewController.php b/src/applications/differential/controller/DifferentialDiffViewController.php index e56eb27d99..ff6623efe3 100644 --- a/src/applications/differential/controller/DifferentialDiffViewController.php +++ b/src/applications/differential/controller/DifferentialDiffViewController.php @@ -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( diff --git a/src/applications/differential/customfield/DifferentialBranchField.php b/src/applications/differential/customfield/DifferentialBranchField.php index 60291b6be3..9d5d8f81a9 100644 --- a/src/applications/differential/customfield/DifferentialBranchField.php +++ b/src/applications/differential/customfield/DifferentialBranchField.php @@ -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, diff --git a/src/applications/differential/customfield/__tests__/DifferentialBranchFieldTestCase.php b/src/applications/differential/customfield/__tests__/DifferentialBranchFieldTestCase.php new file mode 100644 index 0000000000..e96b4bd245 --- /dev/null +++ b/src/applications/differential/customfield/__tests__/DifferentialBranchFieldTestCase.php @@ -0,0 +1,39 @@ + true, + ); + } + + private function getTestDiff() { + $parser = new ArcanistDiffParser(); + $raw_diff = <<parseDiff($raw_diff)); + } + + public function testRenderDiffPropertyViewValue() { + $test_object = new DifferentialBranchField(); + $diff = $this->getTestDiff(); + $diff->setBranch('test'); + $this->assertEqual('test', + $test_object->renderDiffPropertyViewValue($diff)); + } +} diff --git a/src/applications/differential/editor/DifferentialRevisionEditEngine.php b/src/applications/differential/editor/DifferentialRevisionEditEngine.php index 995179a864..bd1cee7c26 100644 --- a/src/applications/differential/editor/DifferentialRevisionEditEngine.php +++ b/src/applications/differential/editor/DifferentialRevisionEditEngine.php @@ -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. diff --git a/src/applications/differential/field/DifferentialCommitMessageField.php b/src/applications/differential/field/DifferentialCommitMessageField.php index ebe10bcf88..55c5ce0dad 100644 --- a/src/applications/differential/field/DifferentialCommitMessageField.php +++ b/src/applications/differential/field/DifferentialCommitMessageField.php @@ -60,7 +60,7 @@ abstract class DifferentialCommitMessageField } public function renderFieldValue($value) { - if (!strlen($value)) { + if (!phutil_nonempty_string($value)) { return null; } diff --git a/src/applications/differential/field/DifferentialRevisionIDCommitMessageField.php b/src/applications/differential/field/DifferentialRevisionIDCommitMessageField.php index ac8ba4ebd4..b39145e9df 100644 --- a/src/applications/differential/field/DifferentialRevisionIDCommitMessageField.php +++ b/src/applications/differential/field/DifferentialRevisionIDCommitMessageField.php @@ -72,7 +72,7 @@ final class DifferentialRevisionIDCommitMessageField } public function renderFieldValue($value) { - if (!strlen($value)) { + if (!phutil_nonempty_string($value)) { return null; } diff --git a/src/applications/differential/field/__tests__/DifferentialCommitMessageFieldTestCase.php b/src/applications/differential/field/__tests__/DifferentialCommitMessageFieldTestCase.php index 41fd2992f4..bd2fb6f29d 100644 --- a/src/applications/differential/field/__tests__/DifferentialCommitMessageFieldTestCase.php +++ b/src/applications/differential/field/__tests__/DifferentialCommitMessageFieldTestCase.php @@ -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)); + } + } diff --git a/src/applications/differential/storage/DifferentialDiff.php b/src/applications/differential/storage/DifferentialDiff.php index dfa6d1f791..8f537cff77 100644 --- a/src/applications/differential/storage/DifferentialDiff.php +++ b/src/applications/differential/storage/DifferentialDiff.php @@ -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, diff --git a/src/applications/differential/storage/__tests__/DifferentialDiffTestCase.php b/src/applications/differential/storage/__tests__/DifferentialDiffTestCase.php index 45547aed42..6959b33e27 100644 --- a/src/applications/differential/storage/__tests__/DifferentialDiffTestCase.php +++ b/src/applications/differential/storage/__tests__/DifferentialDiffTestCase.php @@ -1,6 +1,12 @@ 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 = <<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)); + } + + } } diff --git a/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php index 86ec7b7466..80c3f7b27d 100644 --- a/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php @@ -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; } diff --git a/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php index 5eb20d2f7e..ba0ee3bd0b 100644 --- a/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php @@ -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; diff --git a/src/applications/diffusion/controller/DiffusionBrowseController.php b/src/applications/diffusion/controller/DiffusionBrowseController.php index 2b81ab89cc..b81c42db8c 100644 --- a/src/applications/diffusion/controller/DiffusionBrowseController.php +++ b/src/applications/diffusion/controller/DiffusionBrowseController.php @@ -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(); } diff --git a/src/applications/diffusion/controller/DiffusionCommitController.php b/src/applications/diffusion/controller/DiffusionCommitController.php index 7b893b1e52..ce3ef41c9e 100644 --- a/src/applications/diffusion/controller/DiffusionCommitController.php +++ b/src/applications/diffusion/controller/DiffusionCommitController.php @@ -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; } diff --git a/src/applications/diffusion/controller/DiffusionHistoryController.php b/src/applications/diffusion/controller/DiffusionHistoryController.php index fb35d9b6ad..eeb8f13118 100644 --- a/src/applications/diffusion/controller/DiffusionHistoryController.php +++ b/src/applications/diffusion/controller/DiffusionHistoryController.php @@ -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()) diff --git a/src/applications/diffusion/controller/DiffusionServeController.php b/src/applications/diffusion/controller/DiffusionServeController.php index 89430eef37..93656ea607 100644 --- a/src/applications/diffusion/controller/DiffusionServeController.php +++ b/src/applications/diffusion/controller/DiffusionServeController.php @@ -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']); diff --git a/src/applications/diffusion/controller/DiffusionTagListController.php b/src/applications/diffusion/controller/DiffusionTagListController.php index 15b8dc93ae..ffc6a1a9ef 100644 --- a/src/applications/diffusion/controller/DiffusionTagListController.php +++ b/src/applications/diffusion/controller/DiffusionTagListController.php @@ -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 { diff --git a/src/applications/diffusion/controller/__tests__/DiffusionServeControllerTestCase.php b/src/applications/diffusion/controller/__tests__/DiffusionServeControllerTestCase.php new file mode 100644 index 0000000000..67c5e63a57 --- /dev/null +++ b/src/applications/diffusion/controller/__tests__/DiffusionServeControllerTestCase.php @@ -0,0 +1,21 @@ + 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'); + } + +} diff --git a/src/applications/diffusion/document/DiffusionDocumentRenderingEngine.php b/src/applications/diffusion/document/DiffusionDocumentRenderingEngine.php index 800d195726..2c33a82ad5 100644 --- a/src/applications/diffusion/document/DiffusionDocumentRenderingEngine.php +++ b/src/applications/diffusion/document/DiffusionDocumentRenderingEngine.php @@ -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); } } diff --git a/src/applications/diffusion/edge/DiffusionCommitHasRevisionEdgeType.php b/src/applications/diffusion/edge/DiffusionCommitHasRevisionEdgeType.php index ce7a899bda..f59cb8e564 100644 --- a/src/applications/diffusion/edge/DiffusionCommitHasRevisionEdgeType.php +++ b/src/applications/diffusion/edge/DiffusionCommitHasRevisionEdgeType.php @@ -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); + } + } diff --git a/src/applications/diffusion/engine/DiffusionCommitHookEngine.php b/src/applications/diffusion/engine/DiffusionCommitHookEngine.php index c172f27466..e4e8fd2293 100644 --- a/src/applications/diffusion/engine/DiffusionCommitHookEngine.php +++ b/src/applications/diffusion/engine/DiffusionCommitHookEngine.php @@ -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); } diff --git a/src/applications/diffusion/query/pathid/DiffusionPathIDQuery.php b/src/applications/diffusion/query/pathid/DiffusionPathIDQuery.php index 7c9e721431..d994410034 100644 --- a/src/applications/diffusion/query/pathid/DiffusionPathIDQuery.php +++ b/src/applications/diffusion/query/pathid/DiffusionPathIDQuery.php @@ -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); diff --git a/src/applications/diffusion/request/DiffusionRequest.php b/src/applications/diffusion/request/DiffusionRequest.php index 4bcb4d3bc5..c301b89b28 100644 --- a/src/applications/diffusion/request/DiffusionRequest.php +++ b/src/applications/diffusion/request/DiffusionRequest.php @@ -254,7 +254,7 @@ abstract class DiffusionRequest extends Phobject { } public function getPath() { - return $this->path; + return coalesce($this->path, ''); } public function getLine() { diff --git a/src/applications/diffusion/view/DiffusionView.php b/src/applications/diffusion/view/DiffusionView.php index 795b47c773..268514ff53 100644 --- a/src/applications/diffusion/view/DiffusionView.php +++ b/src/applications/diffusion/view/DiffusionView.php @@ -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( diff --git a/src/applications/diviner/atom/DivinerAtom.php b/src/applications/diviner/atom/DivinerAtom.php index f7f8d2c4ac..93e24d5d03 100644 --- a/src/applications/diviner/atom/DivinerAtom.php +++ b/src/applications/diviner/atom/DivinerAtom.php @@ -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) { diff --git a/src/applications/diviner/controller/DivinerMainController.php b/src/applications/diviner/controller/DivinerMainController.php index 21450698e6..791e7520b6 100644 --- a/src/applications/diviner/controller/DivinerMainController.php +++ b/src/applications/diviner/controller/DivinerMainController.php @@ -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". diff --git a/src/applications/files/conduit/FileUploadConduitAPIMethod.php b/src/applications/files/conduit/FileUploadConduitAPIMethod.php index 8b3b9faaaa..cafe842596 100644 --- a/src/applications/files/conduit/FileUploadConduitAPIMethod.php +++ b/src/applications/files/conduit/FileUploadConduitAPIMethod.php @@ -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, diff --git a/src/applications/files/controller/PhabricatorFileLightboxController.php b/src/applications/files/controller/PhabricatorFileLightboxController.php index 59a826dd42..28123d9002 100644 --- a/src/applications/files/controller/PhabricatorFileLightboxController.php +++ b/src/applications/files/controller/PhabricatorFileLightboxController.php @@ -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) diff --git a/src/applications/files/storage/PhabricatorFile.php b/src/applications/files/storage/PhabricatorFile.php index 44e35c6b72..6782a5a90e 100644 --- a/src/applications/files/storage/PhabricatorFile.php +++ b/src/applications/files/storage/PhabricatorFile.php @@ -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'); } diff --git a/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php b/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php index 21cb2daf11..21ad929c3f 100644 --- a/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php +++ b/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php @@ -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')); + } + } diff --git a/src/applications/harbormaster/storage/build/HarbormasterBuildUnitMessage.php b/src/applications/harbormaster/storage/build/HarbormasterBuildUnitMessage.php index f2632153c3..0f373babf1 100644 --- a/src/applications/harbormaster/storage/build/HarbormasterBuildUnitMessage.php +++ b/src/applications/harbormaster/storage/build/HarbormasterBuildUnitMessage.php @@ -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); } diff --git a/src/applications/herald/controller/HeraldTranscriptController.php b/src/applications/herald/controller/HeraldTranscriptController.php index dfc0dfc0b1..4bc569538f 100644 --- a/src/applications/herald/controller/HeraldTranscriptController.php +++ b/src/applications/herald/controller/HeraldTranscriptController.php @@ -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')) diff --git a/src/applications/macro/engine/PhabricatorMemeEngine.php b/src/applications/macro/engine/PhabricatorMemeEngine.php index afee0f9b18..e1befc20a2 100644 --- a/src/applications/macro/engine/PhabricatorMemeEngine.php +++ b/src/applications/macro/engine/PhabricatorMemeEngine.php @@ -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; } diff --git a/src/applications/metamta/management/PhabricatorMailManagementSendTestWorkflow.php b/src/applications/metamta/management/PhabricatorMailManagementSendTestWorkflow.php index f390ff27df..6ea2c9de5a 100644 --- a/src/applications/metamta/management/PhabricatorMailManagementSendTestWorkflow.php +++ b/src/applications/metamta/management/PhabricatorMailManagementSendTestWorkflow.php @@ -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( diff --git a/src/applications/metamta/management/PhabricatorMailManagementShowOutboundWorkflow.php b/src/applications/metamta/management/PhabricatorMailManagementShowOutboundWorkflow.php index f29a63c2eb..1f4f5a3e2a 100644 --- a/src/applications/metamta/management/PhabricatorMailManagementShowOutboundWorkflow.php +++ b/src/applications/metamta/management/PhabricatorMailManagementShowOutboundWorkflow.php @@ -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 = "{$html_body}"; $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 { diff --git a/src/applications/notification/config/PhabricatorNotificationServersConfigType.php b/src/applications/notification/config/PhabricatorNotificationServersConfigType.php index f13105a249..8fbac5806c 100644 --- a/src/applications/notification/config/PhabricatorNotificationServersConfigType.php +++ b/src/applications/notification/config/PhabricatorNotificationServersConfigType.php @@ -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 '. diff --git a/src/applications/notification/view/PhabricatorNotificationStatusView.php b/src/applications/notification/view/PhabricatorNotificationStatusView.php index e962395bba..69939525cc 100644 --- a/src/applications/notification/view/PhabricatorNotificationStatusView.php +++ b/src/applications/notification/view/PhabricatorNotificationStatusView.php @@ -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, + ); + } + } diff --git a/src/applications/people/view/PhabricatorUserLogView.php b/src/applications/people/view/PhabricatorUserLogView.php index cf728af973..ef4fa6f10c 100644 --- a/src/applications/people/view/PhabricatorUserLogView.php +++ b/src/applications/people/view/PhabricatorUserLogView.php @@ -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(); diff --git a/src/applications/people/xaction/PhabricatorUserEmpowerTransaction.php b/src/applications/people/xaction/PhabricatorUserEmpowerTransaction.php index d17418636f..3f3440ede0 100644 --- a/src/applications/people/xaction/PhabricatorUserEmpowerTransaction.php +++ b/src/applications/people/xaction/PhabricatorUserEmpowerTransaction.php @@ -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()); + } } } diff --git a/src/applications/phame/controller/blog/PhameBlogViewController.php b/src/applications/phame/controller/blog/PhameBlogViewController.php index 758fe955ad..d8b61d8352 100644 --- a/src/applications/phame/controller/blog/PhameBlogViewController.php +++ b/src/applications/phame/controller/blog/PhameBlogViewController.php @@ -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; } diff --git a/src/applications/phame/storage/PhameBlog.php b/src/applications/phame/storage/PhameBlog.php index 717658ec03..587f5daea3 100644 --- a/src/applications/phame/storage/PhameBlog.php +++ b/src/applications/phame/storage/PhameBlog.php @@ -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(); } diff --git a/src/applications/pholio/controller/PholioInlineController.php b/src/applications/pholio/controller/PholioInlineController.php index ddced1f9eb..2201e97f74 100644 --- a/src/applications/pholio/controller/PholioInlineController.php +++ b/src/applications/pholio/controller/PholioInlineController.php @@ -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() diff --git a/src/applications/phriction/engineextension/PhrictionHovercardEngineExtension.php b/src/applications/phriction/engineextension/PhrictionHovercardEngineExtension.php new file mode 100644 index 0000000000..14e63b9521 --- /dev/null +++ b/src/applications/phriction/engineextension/PhrictionHovercardEngineExtension.php @@ -0,0 +1,169 @@ + $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; + } + +} diff --git a/src/applications/project/controller/PhabricatorProjectColumnHideController.php b/src/applications/project/controller/PhabricatorProjectColumnHideController.php index 254beab78c..09217064e2 100644 --- a/src/applications/project/controller/PhabricatorProjectColumnHideController.php +++ b/src/applications/project/controller/PhabricatorProjectColumnHideController.php @@ -136,7 +136,6 @@ final class PhabricatorProjectColumnHideController ->setWidth(AphrontDialogView::WIDTH_FORM) ->setTitle($title) ->appendChild($body) - ->setDisableWorkflowOnCancel(true) ->addCancelButton($view_uri) ->addSubmitButton($button); diff --git a/src/applications/project/engineextension/PhabricatorProjectHovercardEngineExtension.php b/src/applications/project/engineextension/PhabricatorProjectHovercardEngineExtension.php index d2d1f9ab82..ef8964463f 100644 --- a/src/applications/project/engineextension/PhabricatorProjectHovercardEngineExtension.php +++ b/src/applications/project/engineextension/PhabricatorProjectHovercardEngineExtension.php @@ -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, ); diff --git a/src/applications/project/storage/PhabricatorProjectColumn.php b/src/applications/project/storage/PhabricatorProjectColumn.php index 49d7f28a9f..e4359acd1b 100644 --- a/src/applications/project/storage/PhabricatorProjectColumn.php +++ b/src/applications/project/storage/PhabricatorProjectColumn.php @@ -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(), ); } diff --git a/src/applications/project/view/PhabricatorProjectCardView.php b/src/applications/project/view/PhabricatorProjectCardView.php index a56697ba7e..03d342b666 100644 --- a/src/applications/project/view/PhabricatorProjectCardView.php +++ b/src/applications/project/view/PhabricatorProjectCardView.php @@ -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( diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementMaintenanceWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementMaintenanceWorkflow.php index 6c2f64b31c..513891c5c6 100644 --- a/src/applications/repository/management/PhabricatorRepositoryManagementMaintenanceWorkflow.php +++ b/src/applications/repository/management/PhabricatorRepositoryManagementMaintenanceWorkflow.php @@ -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) { diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php index b05be618c0..4a1c923622 100644 --- a/src/applications/repository/storage/PhabricatorRepository.php +++ b/src/applications/repository/storage/PhabricatorRepository.php @@ -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(''); } diff --git a/src/applications/repository/xaction/PhabricatorRepositoryCallsignTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryCallsignTransaction.php index ab13c6a571..418acf476e 100644 --- a/src/applications/repository/xaction/PhabricatorRepositoryCallsignTransaction.php +++ b/src/applications/repository/xaction/PhabricatorRepositoryCallsignTransaction.php @@ -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(), diff --git a/src/applications/repository/xaction/PhabricatorRepositoryEncodingTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryEncodingTransaction.php index ef129da832..203b98abec 100644 --- a/src/applications/repository/xaction/PhabricatorRepositoryEncodingTransaction.php +++ b/src/applications/repository/xaction/PhabricatorRepositoryEncodingTransaction.php @@ -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(), diff --git a/src/applications/repository/xaction/PhabricatorRepositoryMaintenanceTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryMaintenanceTransaction.php index caf9e84527..c1259aa983 100644 --- a/src/applications/repository/xaction/PhabricatorRepositoryMaintenanceTransaction.php +++ b/src/applications/repository/xaction/PhabricatorRepositoryMaintenanceTransaction.php @@ -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()); diff --git a/src/applications/repository/xaction/PhabricatorRepositoryStagingURITransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryStagingURITransaction.php index 4297d5e244..ed50f65e4e 100644 --- a/src/applications/repository/xaction/PhabricatorRepositoryStagingURITransaction.php +++ b/src/applications/repository/xaction/PhabricatorRepositoryStagingURITransaction.php @@ -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(), diff --git a/src/applications/search/controller/PhabricatorSearchController.php b/src/applications/search/controller/PhabricatorSearchController.php index b3d3ab18fa..336604d228 100644 --- a/src/applications/search/controller/PhabricatorSearchController.php +++ b/src/applications/search/controller/PhabricatorSearchController.php @@ -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); diff --git a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php index 56079efaf2..c03cefeed0 100644 --- a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php +++ b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php @@ -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), diff --git a/src/docs/user/configuration/configuration_guide.diviner b/src/docs/user/configuration/configuration_guide.diviner index 1fde967a95..c5aa82af86 100644 --- a/src/docs/user/configuration/configuration_guide.diviner +++ b/src/docs/user/configuration/configuration_guide.diviner @@ -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. diff --git a/src/docs/user/configuration/managing_daemons.diviner b/src/docs/user/configuration/managing_daemons.diviner index 0e56a95995..d4af3162a4 100644 --- a/src/docs/user/configuration/managing_daemons.diviner +++ b/src/docs/user/configuration/managing_daemons.diviner @@ -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 diff --git a/src/docs/user/field/restarting.diviner b/src/docs/user/field/restarting.diviner index 638e1f1490..8e21c2ac77 100644 --- a/src/docs/user/field/restarting.diviner +++ b/src/docs/user/field/restarting.diviner @@ -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 ``` diff --git a/src/docs/user/installation_guide.diviner b/src/docs/user/installation_guide.diviner index 1a09a2a3d6..ec7aa186d2 100644 --- a/src/docs/user/installation_guide.diviner +++ b/src/docs/user/installation_guide.diviner @@ -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 ========== diff --git a/src/infrastructure/cluster/PhabricatorDatabaseRef.php b/src/infrastructure/cluster/PhabricatorDatabaseRef.php index e526eb1639..0fe2aabbcb 100644 --- a/src/infrastructure/cluster/PhabricatorDatabaseRef.php +++ b/src/infrastructure/cluster/PhabricatorDatabaseRef.php @@ -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; diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php index 4c0bce861b..373aa9b66a 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php @@ -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( diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php index e8a5b99e0d..0ea7233768 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php @@ -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; diff --git a/src/infrastructure/editor/PhabricatorEditorURIEngine.php b/src/infrastructure/editor/PhabricatorEditorURIEngine.php index d4821c4ffd..76c95dead2 100644 --- a/src/infrastructure/editor/PhabricatorEditorURIEngine.php +++ b/src/infrastructure/editor/PhabricatorEditorURIEngine.php @@ -16,7 +16,7 @@ final class PhabricatorEditorURIEngine $pattern = $viewer->getUserSetting(PhabricatorEditorSetting::SETTINGKEY); - if (!strlen(trim($pattern))) { + if ($pattern === null || trim($pattern) === '') { return null; } diff --git a/src/infrastructure/editor/__tests__/PhabricatorEditorURIEngineTestCase.php b/src/infrastructure/editor/__tests__/PhabricatorEditorURIEngineTestCase.php index 43cebff2e6..b6a2b4d8db 100644 --- a/src/infrastructure/editor/__tests__/PhabricatorEditorURIEngineTestCase.php +++ b/src/infrastructure/editor/__tests__/PhabricatorEditorURIEngineTestCase.php @@ -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()); + } + } + } diff --git a/src/infrastructure/javelin/markup.php b/src/infrastructure/javelin/markup.php index 8a040031cc..285f027d3d 100644 --- a/src/infrastructure/javelin/markup.php +++ b/src/infrastructure/javelin/markup.php @@ -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'); diff --git a/src/infrastructure/markup/blockrule/PhutilRemarkupCodeBlockRule.php b/src/infrastructure/markup/blockrule/PhutilRemarkupCodeBlockRule.php index b8fef85b98..8b23fe6541 100644 --- a/src/infrastructure/markup/blockrule/PhutilRemarkupCodeBlockRule.php +++ b/src/infrastructure/markup/blockrule/PhutilRemarkupCodeBlockRule.php @@ -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; + } + } diff --git a/src/infrastructure/markup/blockrule/PhutilRemarkupTableBlockRule.php b/src/infrastructure/markup/blockrule/PhutilRemarkupTableBlockRule.php index 72e61881ce..f02b9b7422 100644 --- a/src/infrastructure/markup/blockrule/PhutilRemarkupTableBlockRule.php +++ b/src/infrastructure/markup/blockrule/PhutilRemarkupTableBlockRule.php @@ -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; } diff --git a/src/infrastructure/markup/remarkup/__tests__/PhutilRemarkupEngineTestCase.php b/src/infrastructure/markup/remarkup/__tests__/PhutilRemarkupEngineTestCase.php index c3b4960d0c..38831d034a 100644 --- a/src/infrastructure/markup/remarkup/__tests__/PhutilRemarkupEngineTestCase.php +++ b/src/infrastructure/markup/remarkup/__tests__/PhutilRemarkupEngineTestCase.php @@ -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 { diff --git a/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-flavored.txt b/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-flavored.txt new file mode 100644 index 0000000000..f224942b1d --- /dev/null +++ b/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-flavored.txt @@ -0,0 +1,7 @@ +```cpp +code +``` +~~~~~~~~~~ +
code
+~~~~~~~~~~ + code diff --git a/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-multi-flavored-comment.txt b/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-multi-flavored-comment.txt new file mode 100644 index 0000000000..bcdaca8063 --- /dev/null +++ b/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-multi-flavored-comment.txt @@ -0,0 +1,18 @@ +```#comment +code + +#more comment +more code``` + +~~~~~~~~~~ +
#comment
+code
+
+#more comment
+more code
+~~~~~~~~~~ + #comment + code + + #more comment + more code diff --git a/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-multi-flavored-empty.txt b/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-multi-flavored-empty.txt new file mode 100644 index 0000000000..c05d44ec0f --- /dev/null +++ b/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-multi-flavored-empty.txt @@ -0,0 +1,9 @@ +``` +cpp +second line``` +~~~~~~~~~~ +
cpp
+second line
+~~~~~~~~~~ + cpp + second line diff --git a/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-multi-flavored.txt b/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-multi-flavored.txt new file mode 100644 index 0000000000..5dc2cef421 --- /dev/null +++ b/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-multi-flavored.txt @@ -0,0 +1,20 @@ +```cpp +code + +more code + +more code +``` + +~~~~~~~~~~ +
code
+
+more code
+
+more code
+~~~~~~~~~~ + code + + more code + + more code diff --git a/src/view/page/PhabricatorStandardPageView.php b/src/view/page/PhabricatorStandardPageView.php index 17ce1db8bb..a81820de3c 100644 --- a/src/view/page/PhabricatorStandardPageView.php +++ b/src/view/page/PhabricatorStandardPageView.php @@ -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 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')); } diff --git a/src/view/phui/PHUIIconView.php b/src/view/phui/PHUIIconView.php index a5897e02d6..5a6bba71b1 100644 --- a/src/view/phui/PHUIIconView.php +++ b/src/view/phui/PHUIIconView.php @@ -780,7 +780,7 @@ final class PHUIIconView extends AphrontTagView { 'fa-reddit-alien', 'fa-edge', 'fa-credit-card-alt', - 'fa-codiepie:before', + 'fa-codiepie', 'fa-modx', 'fa-fort-awesome', 'fa-usb', @@ -843,7 +843,7 @@ final class PHUIIconView extends AphrontTagView { 'fa-address-card-o', 'fa-user-circle', 'fa-user-circle-o', - 'fa-user-o:before', + 'fa-user-o', 'fa-id-badge', 'fa-drivers-license', 'fa-id-card', @@ -861,7 +861,7 @@ final class PHUIIconView extends AphrontTagView { 'fa-thermometer-half', 'fa-thermometer-1', 'fa-thermometer-quarter', - 'fa-thermometer-0:', + 'fa-thermometer-0', 'fa-thermometer-empty', 'fa-shower', 'fa-bathtub', diff --git a/src/view/widget/AphrontStackTraceView.php b/src/view/widget/AphrontStackTraceView.php index ff8cac43bc..15b39be878 100644 --- a/src/view/widget/AphrontStackTraceView.php +++ b/src/view/widget/AphrontStackTraceView.php @@ -15,7 +15,7 @@ final class AphrontStackTraceView extends AphrontView { $libraries = PhutilBootloader::getInstance()->getAllLibraries(); // TODO: Make this configurable? - $path = 'https://secure.phabricator.com/diffusion/%s/browse/master/src/'; + $path = 'https://we.phorge.it/diffusion/%s/browse/master/src/'; $callsigns = array( 'arcanist' => 'ARC', diff --git a/webroot/rsrc/css/application/ponder/ponder-view.css b/webroot/rsrc/css/application/ponder/ponder-view.css index 966fb6a1fb..567d7ae99b 100644 --- a/webroot/rsrc/css/application/ponder/ponder-view.css +++ b/webroot/rsrc/css/application/ponder/ponder-view.css @@ -44,10 +44,17 @@ margin-top: 16px; } +/* Indent comments so that are more related to the answer */ .device-desktop .ponder-answer-view .phui-timeline-view { margin-left: 32px; } +/* Indent input box so that it's more related to answer's comments */ +.device-desktop .ponder-answer-view +.phui-comment-form-view:not(.remarkup-assist-pinned) { + margin-left: 94px; +} + .ponder-answer-view .phui-header-subheader { display: inline; margin-left: 12px; diff --git a/webroot/rsrc/css/phui/object-item/phui-oi-simple-ui.css b/webroot/rsrc/css/phui/object-item/phui-oi-simple-ui.css index e838f1ce3e..fcf2e7ad1b 100644 --- a/webroot/rsrc/css/phui/object-item/phui-oi-simple-ui.css +++ b/webroot/rsrc/css/phui/object-item/phui-oi-simple-ui.css @@ -4,7 +4,7 @@ */ .phui-oi-list-simple .phui-oi-with-image .phui-oi-frame { - min-height: 26px; + min-height: 36px; } .phui-oi-list-simple .phui-oi-image { diff --git a/webroot/rsrc/css/phui/phui-form-view.css b/webroot/rsrc/css/phui/phui-form-view.css index 47f7d1bb2c..0c5331551e 100644 --- a/webroot/rsrc/css/phui/phui-form-view.css +++ b/webroot/rsrc/css/phui/phui-form-view.css @@ -63,6 +63,11 @@ color:{$greytext} !important; } +.aphront-form-input > input[type="file"] { + max-width: stretch; + max-width: -moz-available; + max-width: -webkit-fill-available; +} .aphront-form-error { width: 18%; diff --git a/webroot/rsrc/css/phui/phui-hovercard.css b/webroot/rsrc/css/phui/phui-hovercard.css index 876b0b6e53..ba8057d08d 100644 --- a/webroot/rsrc/css/phui/phui-hovercard.css +++ b/webroot/rsrc/css/phui/phui-hovercard.css @@ -115,3 +115,24 @@ .hovercard-task-view .phui-oi-disabled.phui-workcard { background-color: #fff; } + +.phui-hovercard-object-type { + font-size: 12px; +} + +.phui-hovercard-object-type .phui-icon-view { + margin-right: 6px; +} + +.phui-hovercard-object-type .phui-crumb-divider { + margin: 0px 6px; +} + +.phui-hovercard-object-type .phabricator-handle-tag-list { + margin-top: 10px; +} + +.phui-hovercard-object-type .phabricator-handle-tag-list-item { + display: inline-block; + margin: 0 4px 2px 0; +} diff --git a/webroot/rsrc/css/phui/phui-icon.css b/webroot/rsrc/css/phui/phui-icon.css index 5436bb04b1..21097b9232 100644 --- a/webroot/rsrc/css/phui/phui-icon.css +++ b/webroot/rsrc/css/phui/phui-icon.css @@ -41,8 +41,8 @@ a.phui-icon-view:hover { img.phui-image-disabled { opacity: .8; - -webkit-filter: grayscale(100%); - filter: grayscale(100%); + -webkit-filter: blur(4px) grayscale(100%) sepia(25%); + filter: blur(4px) grayscale(100%) sepia(25%); } .phui-icon-view.bluetext { diff --git a/webroot/rsrc/css/phui/phui-list.css b/webroot/rsrc/css/phui/phui-list.css index dee105f25b..0289c58fff 100644 --- a/webroot/rsrc/css/phui/phui-list.css +++ b/webroot/rsrc/css/phui/phui-list.css @@ -287,6 +287,10 @@ border-bottom: 1px solid {$thinblueborder}; } +.dashboard-panel-disabled { + color: {$lightgreytext}; +} + /* - Info Stack ------------------------------------------------------------ */ .phui-info-view + .phui-list-view { diff --git a/webroot/rsrc/css/phui/phui-property-list-view.css b/webroot/rsrc/css/phui/phui-property-list-view.css index 4d2cbc9b50..93388c3d7c 100644 --- a/webroot/rsrc/css/phui/phui-property-list-view.css +++ b/webroot/rsrc/css/phui/phui-property-list-view.css @@ -204,13 +204,15 @@ div.phui-property-list-stacked .phui-property-list-properties } -.document-engine-image img { +.document-engine-image img, +.phabricator-remarkup-embed-image img { margin: 20px auto; background: url('/rsrc/image/checker_light.png'); max-width: 100%; } -.device-desktop .document-engine-image img:hover { +.device-desktop .document-engine-image img:hover, +.device-desktop .phabricator-remarkup-embed-image img:hover { background: url('/rsrc/image/checker_dark.png'); } diff --git a/webroot/rsrc/css/sprite-login.css b/webroot/rsrc/css/sprite-login.css index ec4e082031..5938ecdf81 100644 --- a/webroot/rsrc/css/sprite-login.css +++ b/webroot/rsrc/css/sprite-login.css @@ -56,22 +56,26 @@ only screen and (min-resolution: 1.5dppx) { } .login-Jira { - background-position: 0px -58px; + background-position: -116px -29px; } .login-LDAP { - background-position: -29px -58px; + background-position: 0px -58px; } .login-MediaWiki { - background-position: -58px -58px; + background-position: -29px -58px; } .login-PayPal { - background-position: -87px -58px; + background-position: -58px -58px; } .login-Phabricator { + background-position: -87px -58px; +} + +.login-Phorge { background-position: 0px -87px; } diff --git a/webroot/rsrc/image/sprite-login-X2.png b/webroot/rsrc/image/sprite-login-X2.png index 24b3ed02b1..4a9daf8247 100644 Binary files a/webroot/rsrc/image/sprite-login-X2.png and b/webroot/rsrc/image/sprite-login-X2.png differ diff --git a/webroot/rsrc/image/sprite-login.png b/webroot/rsrc/image/sprite-login.png index 4a07583c0d..49b507b15c 100644 Binary files a/webroot/rsrc/image/sprite-login.png and b/webroot/rsrc/image/sprite-login.png differ diff --git a/webroot/rsrc/js/application/diffusion/DiffusionLocateFileSource.js b/webroot/rsrc/js/application/diffusion/DiffusionLocateFileSource.js index 1f94f349d3..3a3bcb9bce 100644 --- a/webroot/rsrc/js/application/diffusion/DiffusionLocateFileSource.js +++ b/webroot/rsrc/js/application/diffusion/DiffusionLocateFileSource.js @@ -99,6 +99,10 @@ JX.install('DiffusionLocateFileSource', { return []; } + // Be nice with terminal users that may have "./" or "/" prefixes. + // Otherwise, not a single result is returned. + search = search.replace(/^\.?\//, ''); + // We know that the results for "abc" are always a subset of the results // for "a" and "ab" -- and there's a good chance we already computed // those result sets. Find the longest cached result which is a prefix