1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-15 18:10:53 +01:00

(stable) Promote 2017 Week 12

This commit is contained in:
epriestley 2017-03-24 17:42:01 -07:00
commit 6f1e2d8055
107 changed files with 1469 additions and 10964 deletions

View file

@ -7,9 +7,9 @@
*/
return array(
'names' => array(
'conpherence.pkg.css' => '32f2c040',
'conpherence.pkg.css' => '82aca405',
'conpherence.pkg.js' => '6249a1cf',
'core.pkg.css' => '491d7018',
'core.pkg.css' => 'dc689e29',
'core.pkg.js' => '1fa7c0c5',
'darkconsole.pkg.js' => 'e7393ebb',
'differential.pkg.css' => '90b30783',
@ -46,7 +46,7 @@ return array(
'rsrc/css/application/config/setup-issue.css' => 'f794cfc3',
'rsrc/css/application/config/unhandled-exception.css' => '4c96257a',
'rsrc/css/application/conpherence/durable-column.css' => '292c71f0',
'rsrc/css/application/conpherence/header-pane.css' => 'db93ebc6',
'rsrc/css/application/conpherence/header-pane.css' => '4082233d',
'rsrc/css/application/conpherence/menu.css' => '3d8e5c9c',
'rsrc/css/application/conpherence/message-pane.css' => 'd1fc13e1',
'rsrc/css/application/conpherence/notification.css' => '965db05b',
@ -83,7 +83,7 @@ return array(
'rsrc/css/application/paste/paste.css' => '1898e534',
'rsrc/css/application/people/people-picture-menu-item.css' => 'a06f7f34',
'rsrc/css/application/people/people-profile.css' => '4df76faf',
'rsrc/css/application/phame/phame.css' => '53fa6236',
'rsrc/css/application/phame/phame.css' => 'b3a0b3a3',
'rsrc/css/application/pholio/pholio-edit.css' => '07676f51',
'rsrc/css/application/pholio/pholio-inline-comments.css' => '8e545e49',
'rsrc/css/application/pholio/pholio.css' => 'ca89d380',
@ -96,7 +96,7 @@ return array(
'rsrc/css/application/policy/policy-transaction-detail.css' => '82100a43',
'rsrc/css/application/policy/policy.css' => '957ea14c',
'rsrc/css/application/ponder/ponder-view.css' => 'fbd45f96',
'rsrc/css/application/project/project-card-view.css' => '1be8c87b',
'rsrc/css/application/project/project-card-view.css' => '3d3c1f91',
'rsrc/css/application/project/project-view.css' => '792c9057',
'rsrc/css/application/releeph/releeph-core.css' => '9b3c5733',
'rsrc/css/application/releeph/releeph-preview-branch.css' => 'b7a6f4a5',
@ -112,7 +112,6 @@ return array(
'rsrc/css/core/syntax.css' => '769d3498',
'rsrc/css/core/z-index.css' => '5e72c4e0',
'rsrc/css/diviner/diviner-shared.css' => '896f1d43',
'rsrc/css/font/font-aleo.css' => '8bdb2835',
'rsrc/css/font/font-awesome.css' => 'e838e088',
'rsrc/css/font/font-lato.css' => 'c7ccd872',
'rsrc/css/font/phui-font-icon-base.css' => '870a7360',
@ -145,11 +144,11 @@ return array(
'rsrc/css/phui/phui-document-summary.css' => '9ca48bdf',
'rsrc/css/phui/phui-document.css' => 'c32e8dec',
'rsrc/css/phui/phui-feed-story.css' => '44a9c8e9',
'rsrc/css/phui/phui-fontkit.css' => 'b78a0059',
'rsrc/css/phui/phui-form-view.css' => 'cf198e10',
'rsrc/css/phui/phui-fontkit.css' => '1320ed01',
'rsrc/css/phui/phui-form-view.css' => '6175808d',
'rsrc/css/phui/phui-form.css' => 'b62c01d8',
'rsrc/css/phui/phui-head-thing.css' => 'fd311e5f',
'rsrc/css/phui/phui-header-view.css' => 'fef6a54e',
'rsrc/css/phui/phui-header-view.css' => '9cf828ce',
'rsrc/css/phui/phui-hovercard.css' => 'ae091fc5',
'rsrc/css/phui/phui-icon-set-selector.css' => '87db8fee',
'rsrc/css/phui/phui-icon.css' => '12b387a1',
@ -169,7 +168,7 @@ return array(
'rsrc/css/phui/phui-status.css' => 'd5263e49',
'rsrc/css/phui/phui-tag-view.css' => '84d65f26',
'rsrc/css/phui/phui-timeline-view.css' => 'bf45789e',
'rsrc/css/phui/phui-two-column-view.css' => '8a1074c7',
'rsrc/css/phui/phui-two-column-view.css' => 'ce9fa0b7',
'rsrc/css/phui/workboards/phui-workboard-color.css' => '783cdff5',
'rsrc/css/phui/workboards/phui-workboard.css' => '3bc85455',
'rsrc/css/phui/workboards/phui-workcard.css' => 'cca5fa92',
@ -178,16 +177,6 @@ return array(
'rsrc/css/sprite-tokens.css' => '9cdfd599',
'rsrc/css/syntax/syntax-default.css' => '9923583c',
'rsrc/externals/d3/d3.min.js' => 'a11a5ff2',
'rsrc/externals/font/aleo/aleo-bold.eot' => 'd3d3bed7',
'rsrc/externals/font/aleo/aleo-bold.svg' => '45899c8e',
'rsrc/externals/font/aleo/aleo-bold.ttf' => '4b08bef0',
'rsrc/externals/font/aleo/aleo-bold.woff' => '93b513a1',
'rsrc/externals/font/aleo/aleo-bold.woff2' => '75fbf322',
'rsrc/externals/font/aleo/aleo-regular.eot' => 'a4e29e2f',
'rsrc/externals/font/aleo/aleo-regular.svg' => '42a86f7a',
'rsrc/externals/font/aleo/aleo-regular.ttf' => '751e7479',
'rsrc/externals/font/aleo/aleo-regular.woff' => 'c3744be9',
'rsrc/externals/font/aleo/aleo-regular.woff2' => '851aa0ee',
'rsrc/externals/font/fontawesome/fontawesome-webfont.eot' => '24a7064f',
'rsrc/externals/font/fontawesome/fontawesome-webfont.ttf' => '0039fe26',
'rsrc/externals/font/fontawesome/fontawesome-webfont.woff' => 'de978a43',
@ -541,7 +530,7 @@ return array(
'rsrc/js/phuix/PHUIXActionView.js' => 'b3465b9b',
'rsrc/js/phuix/PHUIXAutocomplete.js' => '7c492cd2',
'rsrc/js/phuix/PHUIXDropdownMenu.js' => '8018ee50',
'rsrc/js/phuix/PHUIXFormControl.js' => 'bbece68d',
'rsrc/js/phuix/PHUIXFormControl.js' => '83e03671',
'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b',
),
'symbols' => array(
@ -564,7 +553,7 @@ return array(
'config-options-css' => '0ede4c9b',
'config-page-css' => 'c1d5121b',
'conpherence-durable-column-view' => '292c71f0',
'conpherence-header-pane-css' => 'db93ebc6',
'conpherence-header-pane-css' => '4082233d',
'conpherence-menu-css' => '3d8e5c9c',
'conpherence-message-pane-css' => 'd1fc13e1',
'conpherence-notification-css' => '965db05b',
@ -584,7 +573,6 @@ return array(
'diffusion-readme-css' => '297373eb',
'diffusion-source-css' => '750add59',
'diviner-shared-css' => '896f1d43',
'font-aleo' => '8bdb2835',
'font-fontawesome' => 'e838e088',
'font-lato' => 'c7ccd872',
'global-drag-and-drop-css' => '5c1b47c2',
@ -826,7 +814,7 @@ return array(
'phabricator-uiexample-reactor-sendclass' => '1def2711',
'phabricator-uiexample-reactor-sendproperties' => 'b1f0ccee',
'phabricator-zindex-css' => '5e72c4e0',
'phame-css' => '53fa6236',
'phame-css' => 'b3a0b3a3',
'pholio-css' => 'ca89d380',
'pholio-edit-css' => '07676f51',
'pholio-inline-comments-css' => '8e545e49',
@ -857,11 +845,11 @@ return array(
'phui-document-view-pro-css' => 'f56738ed',
'phui-feed-story-css' => '44a9c8e9',
'phui-font-icon-base-css' => '870a7360',
'phui-fontkit-css' => 'b78a0059',
'phui-fontkit-css' => '1320ed01',
'phui-form-css' => 'b62c01d8',
'phui-form-view-css' => 'cf198e10',
'phui-form-view-css' => '6175808d',
'phui-head-thing-view-css' => 'fd311e5f',
'phui-header-view-css' => 'fef6a54e',
'phui-header-view-css' => '9cf828ce',
'phui-hovercard' => '1bd28176',
'phui-hovercard-view-css' => 'ae091fc5',
'phui-icon-set-selector-css' => '87db8fee',
@ -890,7 +878,7 @@ return array(
'phui-tag-view-css' => '84d65f26',
'phui-theme-css' => '9f261c6b',
'phui-timeline-view-css' => 'bf45789e',
'phui-two-column-view-css' => '8a1074c7',
'phui-two-column-view-css' => 'ce9fa0b7',
'phui-workboard-color-css' => '783cdff5',
'phui-workboard-view-css' => '3bc85455',
'phui-workcard-view-css' => 'cca5fa92',
@ -899,13 +887,13 @@ return array(
'phuix-action-view' => 'b3465b9b',
'phuix-autocomplete' => '7c492cd2',
'phuix-dropdown-menu' => '8018ee50',
'phuix-form-control-view' => 'bbece68d',
'phuix-form-control-view' => '83e03671',
'phuix-icon-view' => 'bff6884b',
'policy-css' => '957ea14c',
'policy-edit-css' => '815c66f7',
'policy-transaction-detail-css' => '82100a43',
'ponder-view-css' => 'fbd45f96',
'project-card-view-css' => '1be8c87b',
'project-card-view-css' => '3d3c1f91',
'project-view-css' => '792c9057',
'releeph-core' => '9b3c5733',
'releeph-preview-branch' => 'b7a6f4a5',
@ -1530,6 +1518,10 @@ return array(
'javelin-behavior',
'javelin-scrollbar',
),
'83e03671' => array(
'javelin-install',
'javelin-dom',
),
'8499b6ab' => array(
'javelin-behavior',
'javelin-dom',
@ -1585,9 +1577,6 @@ return array(
'javelin-install',
'javelin-dom',
),
'8bdb2835' => array(
'phui-fontkit-css',
),
'8ce821c5' => array(
'phabricator-notification',
'javelin-stratcom',
@ -1917,10 +1906,6 @@ return array(
'javelin-vector',
'javelin-install',
),
'bbece68d' => array(
'javelin-install',
'javelin-dom',
),
'bcaccd64' => array(
'javelin-behavior',
'javelin-behavior-device',
@ -2322,7 +2307,6 @@ return array(
'phui-list-view-css',
'font-fontawesome',
'font-lato',
'font-aleo',
'phui-font-icon-base-css',
'phui-fontkit-css',
'phui-box-css',

View file

@ -138,7 +138,6 @@ return array(
'font-fontawesome',
'font-lato',
'font-aleo',
'phui-font-icon-base-css',
'phui-fontkit-css',
'phui-box-css',

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_differential.differential_reviewer
ADD lastActionDiffPHID VARBINARY(64);

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_differential.differential_reviewer
ADD lastCommentDiffPHID VARBINARY(64);

View file

@ -0,0 +1,125 @@
<?php
$table = new DifferentialRevision();
$diff_table = new DifferentialDiff();
$reviewer_table = new DifferentialReviewer();
$table_name = PhabricatorEdgeConfig::TABLE_NAME_EDGE;
$data_name = PhabricatorEdgeConfig::TABLE_NAME_EDGEDATA;
$conn = $table->establishConnection('w');
// Previously "DifferentialRevisionHasReviewerEdgeType::EDGECONST".
$edge_type = 35;
// NOTE: We can't use normal migration iterators for edges because they don't
// have an "id" column. For now, try just loading the whole result set: the
// actual size of the rows is small. If we run into issues, we could write an
// EdgeIterator.
$every_edge = queryfx_all(
$conn,
'SELECT * FROM %T edge LEFT JOIN %T data ON edge.dataID = data.id
WHERE edge.type = %d',
$table_name,
$data_name,
$edge_type);
foreach ($every_edge as $edge) {
if ($edge['type'] != $edge_type) {
// Ignore edges which aren't "reviewers", like subscribers.
continue;
}
try {
$data = phutil_json_decode($edge['data']);
$data = idx($data, 'data');
} catch (Exception $ex) {
// Just ignore any kind of issue with the edge data, we'll use a default
// below.
$data = null;
}
if (!$data) {
$data = array(
'status' => 'added',
);
}
$status = idx($data, 'status');
$diff_phid = null;
// NOTE: At one point, the code to populate "diffID" worked correctly, but
// it seems to have later been broken. Salvage it if we can, and look up
// the corresponding diff PHID.
$diff_id = idx($data, 'diffID');
if ($diff_id) {
$row = queryfx_one(
$conn,
'SELECT phid FROM %T WHERE id = %d',
$diff_table->getTableName(),
$diff_id);
if ($row) {
$diff_phid = $row['phid'];
}
}
if (!$diff_phid) {
// If the status is "accepted" or "rejected", look up the current diff
// PHID so we can distinguish between "accepted" and "accepted older".
switch ($status) {
case 'accepted':
case 'rejected':
case 'commented':
$row = queryfx_one(
$conn,
'SELECT diff.phid FROM %T diff JOIN %T revision
ON diff.revisionID = revision.id
WHERE revision.phid = %s
ORDER BY diff.id DESC LIMIT 1',
$diff_table->getTableName(),
$table->getTableName(),
$edge['src']);
if ($row) {
$diff_phid = $row['phid'];
}
break;
}
}
// We now represent some states (like "Commented" and "Accepted Older") as
// a primary state plus an extra flag, instead of making "Commented" a
// primary state. Map old states to new states and flags.
if ($status == 'commented') {
$status = 'added';
$comment_phid = $diff_phid;
$action_phid = null;
} else {
$comment_phid = null;
$action_phid = $diff_phid;
}
if ($status == 'accepted-older') {
$status = 'accepted';
}
if ($status == 'rejected-older') {
$status = 'rejected';
}
queryfx(
$conn,
'INSERT INTO %T (revisionPHID, reviewerPHID, reviewerStatus,
lastActionDiffPHID, lastCommentDiffPHID, dateCreated, dateModified)
VALUES (%s, %s, %s, %ns, %ns, %d, %d)
ON DUPLICATE KEY UPDATE dateCreated = VALUES(dateCreated)',
$reviewer_table->getTableName(),
$edge['src'],
$edge['dst'],
$status,
$action_phid,
$comment_phid,
$edge['dateCreated'],
$edge['dateCreated']);
}

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_differential.differential_reviewer
ADD lastActorPHID VARBINARY(64);

View file

@ -494,7 +494,6 @@ phutil_register_library_map(array(
'DifferentialReviewer' => 'applications/differential/storage/DifferentialReviewer.php',
'DifferentialReviewerDatasource' => 'applications/differential/typeahead/DifferentialReviewerDatasource.php',
'DifferentialReviewerForRevisionEdgeType' => 'applications/differential/edge/DifferentialReviewerForRevisionEdgeType.php',
'DifferentialReviewerProxy' => 'applications/differential/storage/DifferentialReviewerProxy.php',
'DifferentialReviewerStatus' => 'applications/differential/constants/DifferentialReviewerStatus.php',
'DifferentialReviewersAddBlockingReviewersHeraldAction' => 'applications/differential/herald/DifferentialReviewersAddBlockingReviewersHeraldAction.php',
'DifferentialReviewersAddBlockingSelfHeraldAction' => 'applications/differential/herald/DifferentialReviewersAddBlockingSelfHeraldAction.php',
@ -2013,7 +2012,6 @@ phutil_register_library_map(array(
'PhabricatorAuthValidateController' => 'applications/auth/controller/PhabricatorAuthValidateController.php',
'PhabricatorAuthenticationConfigOptions' => 'applications/config/option/PhabricatorAuthenticationConfigOptions.php',
'PhabricatorAutoEventListener' => 'infrastructure/events/PhabricatorAutoEventListener.php',
'PhabricatorBadgeHasRecipientEdgeType' => 'applications/badges/edge/PhabricatorBadgeHasRecipientEdgeType.php',
'PhabricatorBadgesApplication' => 'applications/badges/application/PhabricatorBadgesApplication.php',
'PhabricatorBadgesArchiveController' => 'applications/badges/controller/PhabricatorBadgesArchiveController.php',
'PhabricatorBadgesAward' => 'applications/badges/storage/PhabricatorBadgesAward.php',
@ -2530,6 +2528,7 @@ phutil_register_library_map(array(
'PhabricatorDashboardProfileController' => 'applications/dashboard/controller/PhabricatorDashboardProfileController.php',
'PhabricatorDashboardProfileMenuItem' => 'applications/search/menuitem/PhabricatorDashboardProfileMenuItem.php',
'PhabricatorDashboardQuery' => 'applications/dashboard/query/PhabricatorDashboardQuery.php',
'PhabricatorDashboardQueryPanelInstallController' => 'applications/dashboard/controller/PhabricatorDashboardQueryPanelInstallController.php',
'PhabricatorDashboardQueryPanelType' => 'applications/dashboard/paneltype/PhabricatorDashboardQueryPanelType.php',
'PhabricatorDashboardRemarkupRule' => 'applications/dashboard/remarkup/PhabricatorDashboardRemarkupRule.php',
'PhabricatorDashboardRemovePanelController' => 'applications/dashboard/controller/PhabricatorDashboardRemovePanelController.php',
@ -2602,6 +2601,7 @@ phutil_register_library_map(array(
'PhabricatorEdgesDestructionEngineExtension' => 'infrastructure/edges/engineextension/PhabricatorEdgesDestructionEngineExtension.php',
'PhabricatorEditEngine' => 'applications/transactions/editengine/PhabricatorEditEngine.php',
'PhabricatorEditEngineAPIMethod' => 'applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php',
'PhabricatorEditEngineCheckboxesCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineCheckboxesCommentAction.php',
'PhabricatorEditEngineColumnsCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineColumnsCommentAction.php',
'PhabricatorEditEngineCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineCommentAction.php',
'PhabricatorEditEngineCommentActionGroup' => 'applications/transactions/commentaction/PhabricatorEditEngineCommentActionGroup.php',
@ -3622,7 +3622,6 @@ phutil_register_library_map(array(
'PhabricatorQueryOrderVector' => 'infrastructure/query/order/PhabricatorQueryOrderVector.php',
'PhabricatorRateLimitRequestExceptionHandler' => 'aphront/handler/PhabricatorRateLimitRequestExceptionHandler.php',
'PhabricatorRecaptchaConfigOptions' => 'applications/config/option/PhabricatorRecaptchaConfigOptions.php',
'PhabricatorRecipientHasBadgeEdgeType' => 'applications/badges/edge/PhabricatorRecipientHasBadgeEdgeType.php',
'PhabricatorRedirectController' => 'applications/base/controller/PhabricatorRedirectController.php',
'PhabricatorRefreshCSRFController' => 'applications/auth/controller/PhabricatorRefreshCSRFController.php',
'PhabricatorRegistrationProfile' => 'applications/people/storage/PhabricatorRegistrationProfile.php',
@ -3691,6 +3690,7 @@ phutil_register_library_map(array(
'PhabricatorRepositoryPullEventPHIDType' => 'applications/repository/phid/PhabricatorRepositoryPullEventPHIDType.php',
'PhabricatorRepositoryPullEventQuery' => 'applications/repository/query/PhabricatorRepositoryPullEventQuery.php',
'PhabricatorRepositoryPullLocalDaemon' => 'applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php',
'PhabricatorRepositoryPullLocalDaemonModule' => 'applications/repository/daemon/PhabricatorRepositoryPullLocalDaemonModule.php',
'PhabricatorRepositoryPushEvent' => 'applications/repository/storage/PhabricatorRepositoryPushEvent.php',
'PhabricatorRepositoryPushEventPHIDType' => 'applications/repository/phid/PhabricatorRepositoryPushEventPHIDType.php',
'PhabricatorRepositoryPushEventQuery' => 'applications/repository/query/PhabricatorRepositoryPushEventQuery.php',
@ -3987,6 +3987,7 @@ phutil_register_library_map(array(
'PhabricatorTOTPAuthFactor' => 'applications/auth/factor/PhabricatorTOTPAuthFactor.php',
'PhabricatorTOTPAuthFactorTestCase' => 'applications/auth/factor/__tests__/PhabricatorTOTPAuthFactorTestCase.php',
'PhabricatorTaskmasterDaemon' => 'infrastructure/daemon/workers/PhabricatorTaskmasterDaemon.php',
'PhabricatorTaskmasterDaemonModule' => 'infrastructure/daemon/workers/PhabricatorTaskmasterDaemonModule.php',
'PhabricatorTestApplication' => 'applications/base/controller/__tests__/PhabricatorTestApplication.php',
'PhabricatorTestCase' => 'infrastructure/testing/PhabricatorTestCase.php',
'PhabricatorTestController' => 'applications/base/controller/__tests__/PhabricatorTestController.php',
@ -5254,7 +5255,6 @@ phutil_register_library_map(array(
'DifferentialReviewer' => 'DifferentialDAO',
'DifferentialReviewerDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
'DifferentialReviewerForRevisionEdgeType' => 'PhabricatorEdgeType',
'DifferentialReviewerProxy' => 'Phobject',
'DifferentialReviewerStatus' => 'Phobject',
'DifferentialReviewersAddBlockingReviewersHeraldAction' => 'DifferentialReviewersHeraldAction',
'DifferentialReviewersAddBlockingSelfHeraldAction' => 'DifferentialReviewersHeraldAction',
@ -6996,7 +6996,6 @@ phutil_register_library_map(array(
'PhabricatorAuthValidateController' => 'PhabricatorAuthController',
'PhabricatorAuthenticationConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorAutoEventListener' => 'PhabricatorEventListener',
'PhabricatorBadgeHasRecipientEdgeType' => 'PhabricatorEdgeType',
'PhabricatorBadgesApplication' => 'PhabricatorApplication',
'PhabricatorBadgesArchiveController' => 'PhabricatorBadgesController',
'PhabricatorBadgesAward' => array(
@ -7610,6 +7609,7 @@ phutil_register_library_map(array(
'PhabricatorDashboardProfileController' => 'PhabricatorController',
'PhabricatorDashboardProfileMenuItem' => 'PhabricatorProfileMenuItem',
'PhabricatorDashboardQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorDashboardQueryPanelInstallController' => 'PhabricatorDashboardController',
'PhabricatorDashboardQueryPanelType' => 'PhabricatorDashboardPanelType',
'PhabricatorDashboardRemarkupRule' => 'PhabricatorObjectRemarkupRule',
'PhabricatorDashboardRemovePanelController' => 'PhabricatorDashboardController',
@ -7686,6 +7686,7 @@ phutil_register_library_map(array(
'PhabricatorPolicyInterface',
),
'PhabricatorEditEngineAPIMethod' => 'ConduitAPIMethod',
'PhabricatorEditEngineCheckboxesCommentAction' => 'PhabricatorEditEngineCommentAction',
'PhabricatorEditEngineColumnsCommentAction' => 'PhabricatorEditEngineCommentAction',
'PhabricatorEditEngineCommentAction' => 'Phobject',
'PhabricatorEditEngineCommentActionGroup' => 'Phobject',
@ -8874,7 +8875,6 @@ phutil_register_library_map(array(
),
'PhabricatorRateLimitRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler',
'PhabricatorRecaptchaConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorRecipientHasBadgeEdgeType' => 'PhabricatorEdgeType',
'PhabricatorRedirectController' => 'PhabricatorController',
'PhabricatorRefreshCSRFController' => 'PhabricatorAuthController',
'PhabricatorRegistrationProfile' => 'Phobject',
@ -8985,6 +8985,7 @@ phutil_register_library_map(array(
'PhabricatorRepositoryPullEventPHIDType' => 'PhabricatorPHIDType',
'PhabricatorRepositoryPullEventQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorRepositoryPullLocalDaemon' => 'PhabricatorDaemon',
'PhabricatorRepositoryPullLocalDaemonModule' => 'PhutilDaemonOverseerModule',
'PhabricatorRepositoryPushEvent' => array(
'PhabricatorRepositoryDAO',
'PhabricatorPolicyInterface',
@ -9314,6 +9315,7 @@ phutil_register_library_map(array(
'PhabricatorTOTPAuthFactor' => 'PhabricatorAuthFactor',
'PhabricatorTOTPAuthFactorTestCase' => 'PhabricatorTestCase',
'PhabricatorTaskmasterDaemon' => 'PhabricatorDaemon',
'PhabricatorTaskmasterDaemonModule' => 'PhutilDaemonOverseerModule',
'PhabricatorTestApplication' => 'PhabricatorApplication',
'PhabricatorTestCase' => 'PhutilTestCase',
'PhabricatorTestController' => 'PhabricatorController',

View file

@ -178,10 +178,13 @@ final class PhabricatorCommitSearchEngine
$groups = $bucket->newResultGroups($query, $commits);
foreach ($groups as $group) {
$views[] = id(clone $template)
->setHeader($group->getName())
->setNoDataString($group->getNoDataString())
->setCommits($group->getObjects());
// Don't show groups in Dashboard Panels
if ($group->getObjects() || !$this->isPanelContext()) {
$views[] = id(clone $template)
->setHeader($group->getName())
->setNoDataString($group->getNoDataString())
->setCommits($group->getObjects());
}
}
} catch (Exception $ex) {
$this->addError($ex->getMessage());
@ -189,7 +192,13 @@ final class PhabricatorCommitSearchEngine
} else {
$views[] = id(clone $template)
->setCommits($commits)
->setNoDataString(pht('No matching commits.'));
->setNoDataString(pht('No commits found.'));
}
if (!$views) {
$views[] = id(new PhabricatorAuditListView())
->setViewer($viewer)
->setNoDataString(pht('No commits found.'));
}
if (count($views) == 1) {

View file

@ -26,10 +26,6 @@ final class PhabricatorBadgesApplication extends PhabricatorApplication {
return self::GROUP_UTILITIES;
}
public function isPrototype() {
return true;
}
public function getRoutes() {
return array(
'/badges/' => array(

View file

@ -1,103 +0,0 @@
<?php
final class PhabricatorBadgeHasRecipientEdgeType
extends PhabricatorEdgeType {
const EDGECONST = 59;
public function getInverseEdgeConstant() {
return PhabricatorRecipientHasBadgeEdgeType::EDGECONST;
}
public function shouldWriteInverseTransactions() {
return true;
}
public function getTransactionAddString(
$actor,
$add_count,
$add_edges) {
return pht(
'%s awarded %s recipients(s): %s.',
$actor,
$add_count,
$add_edges);
}
public function getTransactionRemoveString(
$actor,
$rem_count,
$rem_edges) {
return pht(
'%s revoked %s recipients(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 recipient(s), awarded %s: %s; revoked %s: %s.',
$actor,
$add_count,
$add_edges,
$rem_count,
$rem_edges);
}
public function getFeedAddString(
$actor,
$object,
$add_count,
$add_edges) {
return pht(
'%s awarded %s recipient(s) for %s: %s.',
$actor,
$add_count,
$object,
$add_edges);
}
public function getFeedRemoveString(
$actor,
$object,
$rem_count,
$rem_edges) {
return pht(
'%s revoked %s recipient(s) for %s: %s.',
$actor,
$rem_count,
$object,
$rem_edges);
}
public function getFeedEditString(
$actor,
$object,
$total_count,
$add_count,
$add_edges,
$rem_count,
$rem_edges) {
return pht(
'%s edited recipient(s) for %s, awarded %s: %s; revoked %s: %s.',
$actor,
$object,
$add_count,
$add_edges,
$rem_count,
$rem_edges);
}
}

View file

@ -1,103 +0,0 @@
<?php
final class PhabricatorRecipientHasBadgeEdgeType
extends PhabricatorEdgeType {
const EDGECONST = 58;
public function getInverseEdgeConstant() {
return PhabricatorBadgeHasRecipientEdgeType::EDGECONST;
}
public function shouldWriteInverseTransactions() {
return true;
}
public function getTransactionAddString(
$actor,
$add_count,
$add_edges) {
return pht(
'%s added %s badge(s): %s.',
$actor,
$add_count,
$add_edges);
}
public function getTransactionRemoveString(
$actor,
$rem_count,
$rem_edges) {
return pht(
'%s revoked %s badge(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 badge(s), added %s: %s; revoked %s: %s.',
$actor,
$add_count,
$add_edges,
$rem_count,
$rem_edges);
}
public function getFeedAddString(
$actor,
$object,
$add_count,
$add_edges) {
return pht(
'%s added %s badge(s) for %s: %s.',
$actor,
$add_count,
$object,
$add_edges);
}
public function getFeedRemoveString(
$actor,
$object,
$rem_count,
$rem_edges) {
return pht(
'%s revoked %s badge(s) for %s: %s.',
$actor,
$rem_count,
$object,
$rem_edges);
}
public function getFeedEditString(
$actor,
$object,
$total_count,
$add_count,
$add_edges,
$rem_count,
$rem_edges) {
return pht(
'%s edited badge(s) for %s, added %s: %s; revoked %s: %s.',
$actor,
$object,
$add_count,
$add_edges,
$rem_count,
$rem_edges);
}
}

View file

@ -1343,7 +1343,21 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
PhabricatorDestructionEngine $engine) {
$this->openTransaction();
$this->delete();
$invitees = id(new PhabricatorCalendarEventInvitee())->loadAllWhere(
'eventPHID = %s',
$this->getPHID());
foreach ($invitees as $invitee) {
$invitee->delete();
}
$notifications = id(new PhabricatorCalendarNotification())->loadAllWhere(
'eventPHID = %s',
$this->getPHID());
foreach ($notifications as $notification) {
$notification->delete();
}
$this->delete();
$this->saveTransaction();
}

View file

@ -56,6 +56,12 @@ abstract class PhabricatorConduitController extends PhabricatorController {
$panel_link),
);
if ($params === null) {
$messages[] = pht(
'If you submit parameters, these examples will update to show '.
'exactly how to encode the parameters you submit.');
}
$info_view = id(new PHUIInfoView())
->setErrors($messages)
->setSeverity(PHUIInfoView::SEVERITY_NOTICE);

View file

@ -7,6 +7,10 @@ abstract class PhabricatorDaemonController
return true;
}
public function buildApplicationMenu() {
return $this->buildSideNavView(true)->getMenu();
}
protected function buildSideNavView() {
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));

View file

@ -125,12 +125,10 @@ final class PhabricatorDaemonLogViewController
case PhabricatorDaemonLog::STATUS_WAIT:
$details = pht(
'This daemon is running normally and reported a status update '.
'recently (within %s). However, it encountered an error while '.
'doing work and is waiting a little while (%s) to resume '.
'processing. After encountering an error, daemons wait before '.
'resuming work to avoid overloading services.',
phutil_format_relative_time($unknown_time),
phutil_format_relative_time($wait_time));
'recently (within %s). The process is currently waiting to '.
'restart, either because it is hibernating or because it '.
'encountered an error.',
phutil_format_relative_time($unknown_time));
break;
case PhabricatorDaemonLog::STATUS_EXITING:
$details = pht('This daemon is shutting down gracefully.');

View file

@ -18,6 +18,14 @@ final class PhabricatorDashboardApplication extends PhabricatorApplication {
return 'fa-dashboard';
}
public function isPinnedByDefault(PhabricatorUser $viewer) {
return true;
}
public function getApplicationOrder() {
return 0.160;
}
public function getRoutes() {
return array(
'/W(?P<id>\d+)' => 'PhabricatorDashboardPanelViewController',
@ -36,6 +44,8 @@ final class PhabricatorDashboardApplication extends PhabricatorApplication {
'removepanel/(?P<id>\d+)/'
=> 'PhabricatorDashboardRemovePanelController',
'panel/' => array(
'install/(?P<engineKey>[^/]+)/(?:(?P<queryKey>[^/]+)/)?' =>
'PhabricatorDashboardQueryPanelInstallController',
'(?:query/(?P<queryKey>[^/]+)/)?'
=> 'PhabricatorDashboardPanelListController',
'create/' => 'PhabricatorDashboardPanelEditController',

View file

@ -0,0 +1,194 @@
<?php
final class PhabricatorDashboardQueryPanelInstallController
extends PhabricatorDashboardController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$v_dashboard = null;
$v_name = null;
$v_column = 0;
$v_engine = $request->getURIData('engineKey');
$v_query = $request->getURIData('queryKey');
$e_name = true;
// Validate Engines
$engines = PhabricatorApplicationSearchEngine::getAllEngines();
foreach ($engines as $name => $engine) {
if (!$engine->canUseInPanelContext()) {
unset($engines[$name]);
}
}
if (!in_array($v_engine, array_keys($engines))) {
return new Aphront404Response();
}
// Validate Queries
$engine = $engines[$v_engine];
$engine->setViewer($viewer);
$good_query = false;
if ($engine->isBuiltinQuery($v_query)) {
$good_query = true;
} else {
$saved_query = id(new PhabricatorSavedQueryQuery())
->setViewer($viewer)
->withEngineClassNames(array($v_engine))
->withQueryKeys(array($v_query))
->executeOne();
if ($saved_query) {
$good_query = true;
}
}
if (!$good_query) {
return new Aphront404Response();
}
$named_query = idx($engine->loadEnabledNamedQueries(), $v_query);
if ($named_query) {
$v_name = $named_query->getQueryName();
}
$errors = array();
if ($request->isFormPost()) {
$v_dashboard = $request->getInt('dashboardID');
$v_name = $request->getStr('name');
if (!$v_name) {
$errors[] = pht('You must provide a name for this panel.');
$e_name = pht('Required');
}
$dashboard = id(new PhabricatorDashboardQuery())
->setViewer($viewer)
->withIDs(array($v_dashboard))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$dashboard) {
$errors[] = pht('Please select a valid dashboard.');
}
if (!$errors) {
$redirect_uri = "/dashboard/arrange/{$v_dashboard}/";
$panel_type = id(new PhabricatorDashboardQueryPanelType())
->getPanelTypeKey();
$panel = PhabricatorDashboardPanel::initializeNewPanel($viewer);
$panel->setPanelType($panel_type);
$field_list = PhabricatorCustomField::getObjectFields(
$panel,
PhabricatorCustomField::ROLE_EDIT);
$field_list
->setViewer($viewer)
->readFieldsFromStorage($panel);
$panel->requireImplementation()->initializeFieldsFromRequest(
$panel,
$field_list,
$request);
$xactions = array();
$xactions[] = id(new PhabricatorDashboardPanelTransaction())
->setTransactionType(PhabricatorDashboardPanelTransaction::TYPE_NAME)
->setNewValue($v_name);
$xactions[] = id(new PhabricatorDashboardPanelTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_CUSTOMFIELD)
->setMetadataValue('customfield:key', 'std:dashboard:core:class')
->setOldValue(null)
->setNewValue($v_engine);
$xactions[] = id(new PhabricatorDashboardPanelTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_CUSTOMFIELD)
->setMetadataValue('customfield:key', 'std:dashboard:core:key')
->setOldValue(null)
->setNewValue($v_query);
$editor = id(new PhabricatorDashboardPanelTransactionEditor())
->setActor($viewer)
->setContinueOnNoEffect(true)
->setContentSourceFromRequest($request)
->applyTransactions($panel, $xactions);
PhabricatorDashboardTransactionEditor::addPanelToDashboard(
$viewer,
PhabricatorContentSource::newFromRequest($request),
$panel,
$dashboard,
$request->getInt('column', 0));
return id(new AphrontRedirectResponse())->setURI($redirect_uri);
}
}
// Make this a select for now, as we don't expect someone to have
// edit access to a vast number of dashboards.
// Can add optiongroup if needed down the road.
$dashboards = id(new PhabricatorDashboardQuery())
->setViewer($viewer)
->withStatuses(array(
PhabricatorDashboard::STATUS_ACTIVE,
))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->execute();
$options = mpull($dashboards, 'getName', 'getID');
asort($options);
$redirect_uri = $engine->getQueryResultsPageURI($v_query);
if (!$options) {
$notice = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
->appendChild(pht('You do not have access to any dashboards. To '.
'continue, please create a dashboard first.'));
return $this->newDialog()
->setTitle(pht('No Dashboards'))
->setWidth(AphrontDialogView::WIDTH_FORM)
->appendChild($notice)
->addCancelButton($redirect_uri);
}
$form = id(new AphrontFormView())
->setUser($viewer)
->addHiddenInput('engine', $v_engine)
->addHiddenInput('query', $v_query)
->addHiddenInput('column', $v_column)
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Name'))
->setName('name')
->setValue($v_name)
->setError($e_name))
->appendChild(
id(new AphrontFormSelectControl())
->setUser($this->getViewer())
->setValue($v_dashboard)
->setName('dashboardID')
->setOptions($options)
->setLabel(pht('Dashboard')));
return $this->newDialog()
->setTitle(pht('Add Panel to Dashboard'))
->setErrors($errors)
->setWidth(AphrontDialogView::WIDTH_FORM)
->appendChild($form->buildLayoutView())
->addCancelButton($redirect_uri)
->addSubmitButton(pht('Add Panel'));
}
}

View file

@ -12,6 +12,7 @@ final class PhabricatorDashboardIconSet
protected function newIcons() {
$map = array(
'fa-home' => pht('Home'),
'fa-dashboard' => pht('Dashboard'),
'fa-th-large' => pht('Blocks'),
'fa-columns' => pht('Columns'),
'fa-bookmark' => pht('Page Saver'),
@ -20,16 +21,26 @@ final class PhabricatorDashboardIconSet
'fa-bomb' => pht('Kaboom'),
'fa-pie-chart' => pht('Apple Blueberry'),
'fa-bar-chart' => pht('Serious Business'),
'fa-briefcase' => pht('Project'),
'fa-bell' => pht('Ding Ding'),
'fa-credit-card' => pht('Plastic Debt'),
'fa-code' => pht('PHP is Life'),
'fa-sticky-note' => pht('To Self'),
'fa-newspaper-o' => pht('Stay Woke'),
'fa-server' => pht('Metallica'),
'fa-hashtag' => pht('Corned Beef'),
'fa-group' => pht('Triplets'),
'fa-anchor' => pht('Tasks'),
'fa-calendar' => pht('Calendar'),
'fa-compass' => pht('Wayfinding'),
'fa-futbol-o' => pht('Sports'),
'fa-flag' => pht('Flag'),
'fa-ship' => pht('Water Vessel'),
'fa-feed' => pht('Wireless'),
'fa-bullhorn' => pht('Announcement'),
);
$icons = array();

View file

@ -7,6 +7,7 @@ final class PhabricatorDashboardQuery
private $phids;
private $statuses;
private $authorPHIDs;
private $canEdit;
private $needPanels;
private $needProjects;
@ -41,6 +42,11 @@ final class PhabricatorDashboardQuery
return $this;
}
public function withCanEdit($can_edit) {
$this->canEdit = $can_edit;
return $this;
}
public function withNameNgrams($ngrams) {
return $this->withNgramsConstraint(
id(new PhabricatorDashboardNgrams()),
@ -59,6 +65,15 @@ final class PhabricatorDashboardQuery
$phids = mpull($dashboards, 'getPHID');
if ($this->canEdit) {
$dashboards = id(new PhabricatorPolicyFilter())
->setViewer($this->getViewer())
->requireCapabilities(array(
PhabricatorPolicyCapability::CAN_EDIT,
))
->apply($dashboards);
}
if ($this->needPanels) {
$edge_query = id(new PhabricatorEdgeQuery())
->withSourcePHIDs($phids)

View file

@ -34,6 +34,10 @@ final class PhabricatorDashboardSearchEngine
->setKey('statuses')
->setLabel(pht('Status'))
->setOptions(PhabricatorDashboard::getStatusNameMap()),
id(new PhabricatorSearchCheckboxesField())
->setKey('editable')
->setLabel(pht('Editable'))
->setOptions(array('editable' => null)),
);
}
@ -94,6 +98,10 @@ final class PhabricatorDashboardSearchEngine
$query->withNameNgrams($map['name']);
}
if ($map['editable'] !== null) {
$query->withCanEdit($map['editable']);
}
return $query;
}
@ -126,8 +134,10 @@ final class PhabricatorDashboardSearchEngine
->setHref($this->getApplicationURI("view/{$id}/"))
->setObject($dashboard);
$bg_color = 'bg-dark';
if ($dashboard->isArchived()) {
$item->setDisabled(true);
$bg_color = 'bg-grey';
}
$panels = $dashboard->getPanels();
@ -142,7 +152,7 @@ final class PhabricatorDashboardSearchEngine
$icon = id(new PHUIIconView())
->setIcon($dashboard->getIcon())
->setBackground('bg-dark');
->setBackground($bg_color);
$item->setImageIcon($icon);
$item->setEpoch($dashboard->getDateModified());

View file

@ -44,7 +44,7 @@ final class DifferentialCloseConduitAPIMethod
$revision = id(new DifferentialRevisionQuery())
->withIDs(array($id))
->setViewer($viewer)
->needReviewerStatus(true)
->needReviewers(true)
->executeOne();
if (!$revision) {
throw new ConduitException('ERR_NOT_FOUND');

View file

@ -47,7 +47,7 @@ final class DifferentialCreateCommentConduitAPIMethod
$revision = id(new DifferentialRevisionQuery())
->setViewer($viewer)
->withIDs(array($request->getValue('revision_id')))
->needReviewerStatus(true)
->needReviewers(true)
->needReviewerAuthority(true)
->executeOne();
if (!$revision) {
@ -56,11 +56,28 @@ final class DifferentialCreateCommentConduitAPIMethod
$xactions = array();
$modular_map = array(
'accept' => DifferentialRevisionAcceptTransaction::TRANSACTIONTYPE,
'reject' => DifferentialRevisionRejectTransaction::TRANSACTIONTYPE,
'resign' => DifferentialRevisionResignTransaction::TRANSACTIONTYPE,
);
$action = $request->getValue('action');
if ($action && ($action != 'comment') && ($action != 'none')) {
if (isset($modular_map[$action])) {
$xactions[] = id(new DifferentialTransaction())
->setTransactionType(DifferentialTransaction::TYPE_ACTION)
->setNewValue($action);
->setTransactionType($modular_map[$action])
->setNewValue(true);
} else if ($action) {
switch ($action) {
case 'comment':
case 'none':
break;
default:
$xactions[] = id(new DifferentialTransaction())
->setTransactionType(DifferentialTransaction::TYPE_ACTION)
->setNewValue($action);
break;
}
}
$content = $request->getValue('message');

View file

@ -53,7 +53,7 @@ final class DifferentialCreateRevisionConduitAPIMethod
}
$revision = DifferentialRevision::initializeNewRevision($viewer);
$revision->attachReviewerStatus(array());
$revision->attachReviewers(array());
$result = $this->applyFieldEdit(
$request,

View file

@ -39,7 +39,7 @@ final class DifferentialGetCommitMessageConduitAPIMethod
$revision = id(new DifferentialRevisionQuery())
->withIDs(array($id))
->setViewer($viewer)
->needReviewerStatus(true)
->needReviewers(true)
->needActiveDiffs(true)
->executeOne();
if (!$revision) {

View file

@ -42,15 +42,14 @@ final class DifferentialGetRevisionConduitAPIMethod
$revision = id(new DifferentialRevisionQuery())
->withIDs(array($revision_id))
->setViewer($request->getUser())
->needRelationships(true)
->needReviewerStatus(true)
->needReviewers(true)
->executeOne();
if (!$revision) {
throw new ConduitException('ERR_BAD_REVISION');
}
$reviewer_phids = array_values($revision->getReviewers());
$reviewer_phids = $revision->getReviewerPHIDs();
$diffs = id(new DifferentialDiffQuery())
->setViewer($request->getUser())

View file

@ -182,7 +182,7 @@ final class DifferentialQueryConduitAPIMethod
$query->withBranches($branches);
}
$query->needRelationships(true);
$query->needReviewers(true);
$query->needCommitPHIDs(true);
$query->needDiffIDs(true);
$query->needActiveDiffs(true);
@ -194,6 +194,14 @@ final class DifferentialQueryConduitAPIMethod
$request->getUser(),
$revisions);
if ($revisions) {
$ccs = id(new PhabricatorSubscribersQuery())
->withObjectPHIDs(mpull($revisions, 'getPHID'))
->execute();
} else {
$ccs = array();
}
$results = array();
foreach ($revisions as $revision) {
$diff = $revision->getActiveDiff();
@ -224,8 +232,8 @@ final class DifferentialQueryConduitAPIMethod
'activeDiffPHID' => $diff->getPHID(),
'diffs' => $revision->getDiffIDs(),
'commits' => $revision->getCommitPHIDs(),
'reviewers' => array_values($revision->getReviewers()),
'ccs' => array_values($revision->getCCPHIDs()),
'reviewers' => $revision->getReviewerPHIDs(),
'ccs' => idx($ccs, $phid, array()),
'hashes' => $revision->getHashes(),
'auxiliary' => idx($field_data, $phid, array()),
'repositoryPHID' => $diff->getRepositoryPHID(),

View file

@ -57,7 +57,7 @@ final class DifferentialUpdateRevisionConduitAPIMethod
$revision = id(new DifferentialRevisionQuery())
->setViewer($request->getUser())
->withIDs(array($request->getValue('id')))
->needReviewerStatus(true)
->needReviewers(true)
->needActiveDiffs(true)
->requireCapabilities(
array(

View file

@ -119,37 +119,4 @@ final class DifferentialAction extends Phobject {
return $title;
}
public static function getActionVerb($action) {
$verbs = array(
self::ACTION_COMMENT => pht('Comment'),
self::ACTION_ACCEPT => pht("Accept Revision \xE2\x9C\x94"),
self::ACTION_REJECT => pht("Request Changes \xE2\x9C\x98"),
self::ACTION_RETHINK => pht("Plan Changes \xE2\x9C\x98"),
self::ACTION_ABANDON => pht('Abandon Revision'),
self::ACTION_REQUEST => pht('Request Review'),
self::ACTION_RECLAIM => pht('Reclaim Revision'),
self::ACTION_RESIGN => pht('Resign as Reviewer'),
self::ACTION_ADDREVIEWERS => pht('Add Reviewers'),
self::ACTION_ADDCCS => pht('Add Subscribers'),
self::ACTION_CLOSE => pht('Close Revision'),
self::ACTION_CLAIM => pht('Commandeer Revision'),
self::ACTION_REOPEN => pht('Reopen'),
);
if (!empty($verbs[$action])) {
return $verbs[$action];
} else {
return pht('brazenly %s', $action);
}
}
public static function allowReviewers($action) {
if ($action == self::ACTION_ADDREVIEWERS ||
$action == self::ACTION_REQUEST ||
$action == self::ACTION_RESIGN) {
return true;
}
return false;
}
}

View file

@ -17,8 +17,7 @@ final class DifferentialRevisionViewController extends DifferentialController {
$revision = id(new DifferentialRevisionQuery())
->withIDs(array($this->revisionID))
->setViewer($viewer)
->needRelationships(true)
->needReviewerStatus(true)
->needReviewers(true)
->needReviewerAuthority(true)
->executeOne();
if (!$revision) {
@ -103,9 +102,12 @@ final class DifferentialRevisionViewController extends DifferentialController {
$this->loadDiffProperties($diffs);
$props = $target_manual->getDiffProperties();
$subscriber_phids = PhabricatorSubscribersQuery::loadSubscribersForPHID(
$revision->getPHID());
$object_phids = array_merge(
$revision->getReviewers(),
$revision->getCCPHIDs(),
$revision->getReviewerPHIDs(),
$subscriber_phids,
$revision->loadCommitPHIDs(),
array(
$revision->getAuthorPHID(),
@ -782,7 +784,7 @@ final class DifferentialRevisionViewController extends DifferentialController {
->setLimit(10)
->needFlags(true)
->needDrafts(true)
->needRelationships(true);
->needReviewers(true);
foreach ($path_map as $path => $path_id) {
$query->withPath($repository->getID(), $path_id);

View file

@ -70,6 +70,15 @@ abstract class DifferentialCustomField
return array();
}
protected function getActiveDiff() {
$object = $this->getObject();
try {
return $object->getActiveDiff();
} catch (Exception $ex) {
return null;
}
}
public function getRequiredHandlePHIDsForRevisionHeaderWarnings() {
return array();
}

View file

@ -42,14 +42,17 @@ final class DifferentialProjectReviewersField
->setReviewers($reviewers)
->setHandles($handles);
// TODO: Active diff stuff.
$diff = $this->getActiveDiff();
if ($diff) {
$view->setActiveDiff($diff);
}
return $view;
}
private function getProjectReviewers() {
$reviewers = array();
foreach ($this->getObject()->getReviewerStatus() as $reviewer) {
foreach ($this->getObject()->getReviewers() as $reviewer) {
if (!$reviewer->isUser()) {
$reviewers[] = $reviewer;
}

View file

@ -17,7 +17,7 @@ final class DifferentialReviewersField
protected function readValueFromRevision(
DifferentialRevision $revision) {
return $revision->getReviewerStatus();
return $revision->getReviewers();
}
public function shouldAppearInPropertyView() {
@ -43,14 +43,17 @@ final class DifferentialReviewersField
->setReviewers($reviewers)
->setHandles($handles);
// TODO: Active diff stuff.
$diff = $this->getActiveDiff();
if ($diff) {
$view->setActiveDiff($diff);
}
return $view;
}
private function getUserReviewers() {
$reviewers = array();
foreach ($this->getObject()->getReviewerStatus() as $reviewer) {
foreach ($this->getObject()->getReviewers() as $reviewer) {
if ($reviewer->isUser()) {
$reviewers[] = $reviewer;
}

View file

@ -26,7 +26,7 @@ final class DifferentialDoorkeeperRevisionFeedStoryPublisher
return id(new DifferentialRevisionQuery())
->setViewer($this->getViewer())
->withIDs(array($object->getID()))
->needRelationships(true)
->needReviewers(true)
->executeOne();
}
@ -37,7 +37,7 @@ final class DifferentialDoorkeeperRevisionFeedStoryPublisher
public function getActiveUserPHIDs($object) {
$status = $object->getStatus();
if ($status == ArcanistDifferentialRevisionStatus::NEEDS_REVIEW) {
return $object->getReviewers();
return $object->getReviewerPHIDs();
} else {
return array();
}
@ -48,12 +48,13 @@ final class DifferentialDoorkeeperRevisionFeedStoryPublisher
if ($status == ArcanistDifferentialRevisionStatus::NEEDS_REVIEW) {
return array();
} else {
return $object->getReviewers();
return $object->getReviewerPHIDs();
}
}
public function getCCUserPHIDs($object) {
return $object->getCCPHIDs();
return PhabricatorSubscribersQuery::loadSubscribersForPHID(
$object->getPHID());
}
public function getObjectTitle($object) {

View file

@ -41,7 +41,7 @@ final class DifferentialRevisionEditEngine
protected function newObjectQuery() {
return id(new DifferentialRevisionQuery())
->needActiveDiffs(true)
->needReviewerStatus(true)
->needReviewers(true)
->needReviewerAuthority(true);
}

View file

@ -130,33 +130,6 @@ final class DifferentialTransactionEditor
$action_type = $xaction->getNewValue();
switch ($action_type) {
case DifferentialAction::ACTION_ACCEPT:
case DifferentialAction::ACTION_REJECT:
if ($action_type == DifferentialAction::ACTION_ACCEPT) {
$new_status = DifferentialReviewerStatus::STATUS_ACCEPTED;
} else {
$new_status = DifferentialReviewerStatus::STATUS_REJECTED;
}
$actor = $this->getActor();
// These transactions can cause effects in two ways: by altering the
// status of an existing reviewer; or by adding the actor as a new
// reviewer.
$will_add_reviewer = true;
foreach ($object->getReviewerStatus() as $reviewer) {
if ($reviewer->hasAuthority($actor)) {
if ($reviewer->getStatus() != $new_status) {
return true;
}
}
if ($reviewer->getReviewerPHID() == $actor_phid) {
$will_add_reviwer = false;
}
}
return $will_add_reviewer;
case DifferentialAction::ACTION_CLOSE:
return ($object->getStatus() != $status_closed);
case DifferentialAction::ACTION_ABANDON:
@ -169,13 +142,6 @@ final class DifferentialTransactionEditor
return ($object->getStatus() != $status_plan);
case DifferentialAction::ACTION_REQUEST:
return ($object->getStatus() != $status_review);
case DifferentialAction::ACTION_RESIGN:
foreach ($object->getReviewerStatus() as $reviewer) {
if ($reviewer->getReviewerPHID() == $actor_phid) {
return true;
}
}
return false;
case DifferentialAction::ACTION_CLAIM:
return ($actor_phid != $object->getAuthorPHID());
}
@ -222,12 +188,6 @@ final class DifferentialTransactionEditor
return;
case DifferentialTransaction::TYPE_ACTION:
switch ($xaction->getNewValue()) {
case DifferentialAction::ACTION_RESIGN:
case DifferentialAction::ACTION_ACCEPT:
case DifferentialAction::ACTION_REJECT:
// These have no direct effects, and affect review status only
// indirectly by altering reviewers with TYPE_EDGE transactions.
return;
case DifferentialAction::ACTION_ABANDON:
$object->setStatus(ArcanistDifferentialRevisionStatus::ABANDONED);
return;
@ -366,9 +326,9 @@ final class DifferentialTransactionEditor
// actually change the diff text.
$edits = array();
foreach ($object->getReviewerStatus() as $reviewer) {
foreach ($object->getReviewers() as $reviewer) {
if ($downgrade_rejects) {
if ($reviewer->getStatus() == $new_reject) {
if ($reviewer->getReviewerStatus() == $new_reject) {
$edits[$reviewer->getReviewerPHID()] = array(
'data' => array(
'status' => $old_reject,
@ -378,7 +338,7 @@ final class DifferentialTransactionEditor
}
if ($downgrade_accepts) {
if ($reviewer->getStatus() == $new_accept) {
if ($reviewer->getReviewerStatus() == $new_accept) {
$edits[$reviewer->getReviewerPHID()] = array(
'data' => array(
'status' => $old_accept,
@ -455,9 +415,9 @@ final class DifferentialTransactionEditor
);
$edits = array();
foreach ($object->getReviewerStatus() as $reviewer) {
foreach ($object->getReviewers() as $reviewer) {
if ($reviewer->getReviewerPHID() == $actor_phid) {
if ($reviewer->getStatus() == $status_added) {
if ($reviewer->getReviewerStatus() == $status_added) {
$edits[$actor_phid] = array(
'data' => $data,
);
@ -482,59 +442,9 @@ final class DifferentialTransactionEditor
$action_type = $xaction->getNewValue();
switch ($action_type) {
case DifferentialAction::ACTION_ACCEPT:
case DifferentialAction::ACTION_REJECT:
if ($action_type == DifferentialAction::ACTION_ACCEPT) {
$data = array(
'status' => DifferentialReviewerStatus::STATUS_ACCEPTED,
);
} else {
$data = array(
'status' => DifferentialReviewerStatus::STATUS_REJECTED,
);
}
$edits = array();
foreach ($object->getReviewerStatus() as $reviewer) {
if ($reviewer->hasAuthority($actor)) {
$edits[$reviewer->getReviewerPHID()] = array(
'data' => $data,
);
}
}
// Also either update or add the actor themselves as a reviewer.
$edits[$actor_phid] = array(
'data' => $data,
);
$results[] = id(new DifferentialTransaction())
->setTransactionType($type_edge)
->setMetadataValue('edge:type', $edge_reviewer)
->setIgnoreOnNoEffect(true)
->setNewValue(array('+' => $edits));
break;
case DifferentialAction::ACTION_CLAIM:
$is_commandeer = true;
break;
case DifferentialAction::ACTION_RESIGN:
// If the user is resigning, add a separate reviewer edit
// transaction which removes them as a reviewer.
$results[] = id(new DifferentialTransaction())
->setTransactionType($type_edge)
->setMetadataValue('edge:type', $edge_reviewer)
->setIgnoreOnNoEffect(true)
->setNewValue(
array(
'-' => array(
$actor_phid => $actor_phid,
),
));
break;
}
break;
}
@ -704,7 +614,7 @@ final class DifferentialTransactionEditor
$new_revision = id(new DifferentialRevisionQuery())
->setViewer($this->getActor())
->needReviewerStatus(true)
->needReviewers(true)
->needActiveDiffs(true)
->withIDs(array($object->getID()))
->executeOne();
@ -713,7 +623,7 @@ final class DifferentialTransactionEditor
pht('Failed to load revision from transaction finalization.'));
}
$object->attachReviewerStatus($new_revision->getReviewerStatus());
$object->attachReviewers($new_revision->getReviewers());
$object->attachActiveDiff($new_revision->getActiveDiff());
$object->attachRepository($new_revision->getRepository());
@ -735,7 +645,11 @@ final class DifferentialTransactionEditor
$status_revision = ArcanistDifferentialRevisionStatus::NEEDS_REVISION;
$status_review = ArcanistDifferentialRevisionStatus::NEEDS_REVIEW;
$is_sticky_accept = PhabricatorEnv::getEnvConfig(
'differential.sticky-accept');
$old_status = $object->getStatus();
$active_diff = $object->getActiveDiff();
switch ($old_status) {
case $status_accepted:
case $status_revision:
@ -751,11 +665,17 @@ final class DifferentialTransactionEditor
$has_rejecting_reviewer = false;
$has_rejecting_older_reviewer = false;
$has_blocking_reviewer = false;
foreach ($object->getReviewerStatus() as $reviewer) {
$reviewer_status = $reviewer->getStatus();
foreach ($object->getReviewers() as $reviewer) {
$reviewer_status = $reviewer->getReviewerStatus();
switch ($reviewer_status) {
case DifferentialReviewerStatus::STATUS_REJECTED:
$has_rejecting_reviewer = true;
$action_phid = $reviewer->getLastActionDiffPHID();
$active_phid = $active_diff->getPHID();
$is_current = ($action_phid == $active_phid);
if ($is_current) {
$has_rejecting_reviewer = true;
}
break;
case DifferentialReviewerStatus::STATUS_REJECTED_OLDER:
$has_rejecting_older_reviewer = true;
@ -765,7 +685,13 @@ final class DifferentialTransactionEditor
break;
case DifferentialReviewerStatus::STATUS_ACCEPTED:
if ($reviewer->isUser()) {
$has_accepting_user = true;
$action_phid = $reviewer->getLastActionDiffPHID();
$active_phid = $active_diff->getPHID();
$is_current = ($action_phid == $active_phid);
if ($is_sticky_accept || $is_current) {
$has_accepting_user = true;
}
}
break;
}
@ -808,6 +734,9 @@ final class DifferentialTransactionEditor
break;
}
$this->markReviewerComments($object, $xactions);
return $xactions;
}
@ -925,60 +854,6 @@ final class DifferentialTransactionEditor
$status_closed = ArcanistDifferentialRevisionStatus::CLOSED;
switch ($action) {
case DifferentialAction::ACTION_ACCEPT:
if ($actor_is_author && !$allow_self_accept) {
return pht(
'You can not accept this revision because you are the owner.');
}
if ($revision_status == $status_abandoned) {
return pht(
'You can not accept this revision because it has been '.
'abandoned.');
}
if ($revision_status == $status_closed) {
return pht(
'You can not accept this revision because it has already been '.
'closed.');
}
// TODO: It would be nice to make this generic at some point.
$signatures = DifferentialRequiredSignaturesField::loadForRevision(
$revision);
foreach ($signatures as $phid => $signed) {
if (!$signed) {
return pht(
'You can not accept this revision because the author has '.
'not signed all of the required legal documents.');
}
}
break;
case DifferentialAction::ACTION_REJECT:
if ($actor_is_author) {
return pht('You can not request changes to your own revision.');
}
if ($revision_status == $status_abandoned) {
return pht(
'You can not request changes to this revision because it has been '.
'abandoned.');
}
if ($revision_status == $status_closed) {
return pht(
'You can not request changes to this revision because it has '.
'already been closed.');
}
break;
case DifferentialAction::ACTION_RESIGN:
// You can always resign from a revision if you're a reviewer. If you
// aren't, this is a no-op rather than invalid.
break;
case DifferentialAction::ACTION_CLAIM:
// You can claim a revision if you're not the owner. If you are, this
// is a no-op rather than invalid.
@ -1173,7 +1048,7 @@ final class DifferentialTransactionEditor
protected function getMailTo(PhabricatorLiskDAO $object) {
$phids = array();
$phids[] = $object->getAuthorPHID();
foreach ($object->getReviewerStatus() as $reviewer) {
foreach ($object->getReviewers() as $reviewer) {
$phids[] = $reviewer->getReviewerPHID();
}
return $phids;
@ -1648,7 +1523,7 @@ final class DifferentialTransactionEditor
// and both are needlessly complex. This logic should live in the normal
// transaction application pipeline. See T10967.
$reviewers = $object->getReviewerStatus();
$reviewers = $object->getReviewers();
$reviewers = mpull($reviewers, null, 'getReviewerPHID');
if ($is_blocking) {
@ -1669,7 +1544,7 @@ final class DifferentialTransactionEditor
// If we're applying a stronger status (usually, upgrading a reviewer
// into a blocking reviewer), skip this check so we apply the change.
$old_strength = DifferentialReviewerStatus::getStatusStrength(
$reviewers[$phid]->getStatus());
$reviewers[$phid]->getReviewerStatus());
if ($old_strength <= $new_strength) {
continue;
}
@ -1687,22 +1562,21 @@ final class DifferentialTransactionEditor
$value = array();
foreach ($phids as $phid) {
$value[$phid] = array(
'data' => array(
'status' => $new_status,
),
);
if ($is_blocking) {
$value[] = 'blocking('.$phid.')';
} else {
$value[] = $phid;
}
}
$edgetype_reviewer = DifferentialRevisionHasReviewerEdgeType::EDGECONST;
$owners_phid = id(new PhabricatorOwnersApplication())
->getPHID();
$reviewers_type = DifferentialRevisionReviewersTransaction::TRANSACTIONTYPE;
return $object->getApplicationTransactionTemplate()
->setAuthorPHID($owners_phid)
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue('edge:type', $edgetype_reviewer)
->setTransactionType($reviewers_type)
->setNewValue(
array(
'+' => $value,
@ -1717,7 +1591,7 @@ final class DifferentialTransactionEditor
->setViewer($this->getActor())
->withPHIDs(array($object->getPHID()))
->needActiveDiffs(true)
->needReviewerStatus(true)
->needReviewers(true)
->executeOne();
if (!$revision) {
throw new Exception(
@ -1933,7 +1807,7 @@ final class DifferentialTransactionEditor
// Reload to pick up the active diff and reviewer status.
return id(new DifferentialRevisionQuery())
->setViewer($this->getActor())
->needReviewerStatus(true)
->needReviewers(true)
->needActiveDiffs(true)
->withIDs(array($object->getID()))
->executeOne();
@ -1981,4 +1855,59 @@ final class DifferentialTransactionEditor
->setNewValue($edits);
}
public function getActiveDiff($object) {
if ($this->getIsNewObject()) {
return null;
} else {
return $object->getActiveDiff();
}
}
/**
* When a reviewer makes a comment, mark the last revision they commented
* on.
*
* This allows us to show a hint to help authors and other reviewers quickly
* distinguish between reviewers who have participated in the discussion and
* reviewers who haven't been part of it.
*/
private function markReviewerComments($object, array $xactions) {
$acting_phid = $this->getActingAsPHID();
if (!$acting_phid) {
return;
}
$diff = $this->getActiveDiff($object);
if (!$diff) {
return;
}
$has_comment = false;
foreach ($xactions as $xaction) {
if ($xaction->hasComment()) {
$has_comment = true;
break;
}
}
if (!$has_comment) {
return;
}
$reviewer_table = new DifferentialReviewer();
$conn = $reviewer_table->establishConnection('w');
queryfx(
$conn,
'UPDATE %T SET lastCommentDiffPHID = %s
WHERE revisionPHID = %s
AND reviewerPHID = %s',
$reviewer_table->getTableName(),
$diff->getPHID(),
$object->getPHID(),
$acting_phid);
}
}

View file

@ -25,7 +25,7 @@ final class DifferentialHovercardEngineExtension
$revisions = id(new DifferentialRevisionQuery())
->setViewer($viewer)
->withPHIDs($phids)
->needReviewerStatus(true)
->needReviewers(true)
->execute();
$revisions = mpull($revisions, null, 'getPHID');
@ -54,8 +54,7 @@ final class DifferentialHovercardEngineExtension
pht('Author'),
$viewer->renderHandle($revision->getAuthorPHID()));
$reviewer_phids = $revision->getReviewerStatus();
$reviewer_phids = mpull($reviewer_phids, 'getReviewerPHID');
$reviewer_phids = $revision->getReviewerPHIDs();
$hovercard->addField(
pht('Reviewers'),

View file

@ -37,10 +37,9 @@ final class DifferentialReviewedByCommitMessageField
}
$phids = array();
foreach ($revision->getReviewerStatus() as $reviewer) {
switch ($reviewer->getStatus()) {
foreach ($revision->getReviewers() as $reviewer) {
switch ($reviewer->getReviewerStatus()) {
case DifferentialReviewerStatus::STATUS_ACCEPTED:
case DifferentialReviewerStatus::STATUS_ACCEPTED_OLDER:
$phids[] = $reviewer->getReviewerPHID();
break;
}

View file

@ -45,8 +45,8 @@ final class DifferentialReviewersCommitMessageField
$status_blocking = DifferentialReviewerStatus::STATUS_BLOCKING;
$results = array();
foreach ($revision->getReviewerStatus() as $reviewer) {
if ($reviewer->getStatus() == $status_blocking) {
foreach ($revision->getReviewers() as $reviewer) {
if ($reviewer->getReviewerStatus() == $status_blocking) {
$suffixes = array('!' => '!');
} else {
$suffixes = array();

View file

@ -37,8 +37,7 @@ abstract class DifferentialReviewersHeraldAction
}
}
$reviewers = $object->getReviewerStatus();
$reviewers = mpull($reviewers, null, 'getReviewerPHID');
$reviewers = $object->getReviewers();
if ($is_blocking) {
$new_status = DifferentialReviewerStatus::STATUS_BLOCKING;
@ -58,7 +57,7 @@ abstract class DifferentialReviewersHeraldAction
// If we're applying a stronger status (usually, upgrading a reviewer
// into a blocking reviewer), skip this check so we apply the change.
$old_strength = DifferentialReviewerStatus::getStatusStrength(
$reviewers[$phid]->getStatus());
$reviewers[$phid]->getReviewerStatus());
if ($old_strength <= $new_strength) {
continue;
}
@ -81,18 +80,17 @@ abstract class DifferentialReviewersHeraldAction
$value = array();
foreach ($phids as $phid) {
$value[$phid] = array(
'data' => array(
'status' => $new_status,
),
);
if ($is_blocking) {
$value[] = 'blocking('.$phid.')';
} else {
$value[] = $phid;
}
}
$edgetype_reviewer = DifferentialRevisionHasReviewerEdgeType::EDGECONST;
$reviewers_type = DifferentialRevisionReviewersTransaction::TRANSACTIONTYPE;
$xaction = $adapter->newTransaction()
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue('edge:type', $edgetype_reviewer)
->setTransactionType($reviewers_type)
->setNewValue(
array(
'+' => $value,

View file

@ -85,8 +85,7 @@ final class HeraldDifferentialRevisionAdapter
$revision = id(new DifferentialRevisionQuery())
->withIDs(array($revision->getID()))
->setViewer(PhabricatorUser::getOmnipotentUser())
->needRelationships(true)
->needReviewerStatus(true)
->needReviewers(true)
->executeOne();
$object->revision = $revision;
@ -138,8 +137,7 @@ final class HeraldDifferentialRevisionAdapter
}
public function loadReviewers() {
$reviewers = $this->getObject()->getReviewerStatus();
return mpull($reviewers, 'getReviewerPHID');
return $this->getObject()->getReviewerPHIDs();
}

View file

@ -13,7 +13,7 @@ final class PhabricatorDifferentialRevisionTestDataGenerator
$author = $this->loadPhabricatorUser();
$revision = DifferentialRevision::initializeNewRevision($author);
$revision->attachReviewerStatus(array());
$revision->attachReviewers(array());
$revision->attachActiveDiff(null);
// This could be a bit richer and more formal than it is.

View file

@ -18,7 +18,7 @@ final class DifferentialRevisionMailReceiver
return id(new DifferentialRevisionQuery())
->setViewer($viewer)
->withIDs(array($id))
->needReviewerStatus(true)
->needReviewers(true)
->needReviewerAuthority(true)
->needActiveDiffs(true)
->executeOne();

View file

@ -43,12 +43,11 @@ final class DifferentialRevisionQuery
const ORDER_MODIFIED = 'order-modified';
const ORDER_CREATED = 'order-created';
private $needRelationships = false;
private $needActiveDiffs = false;
private $needDiffIDs = false;
private $needCommitPHIDs = false;
private $needHashes = false;
private $needReviewerStatus = false;
private $needReviewers = false;
private $needReviewerAuthority;
private $needDrafts;
private $needFlags;
@ -227,20 +226,6 @@ final class DifferentialRevisionQuery
}
/**
* Set whether or not the query will load and attach relationships.
*
* @param bool True to load and attach relationships.
* @return this
* @task config
*/
public function needRelationships($need_relationships) {
$this->needRelationships = $need_relationships;
return $this;
}
/**
* Set whether or not the query should load the active diff for each
* revision.
@ -298,14 +283,14 @@ final class DifferentialRevisionQuery
/**
* Set whether or not the query should load associated reviewer status.
* Set whether or not the query should load associated reviewers.
*
* @param bool True to load and attach reviewers.
* @return this
* @task config
*/
public function needReviewerStatus($need_reviewer_status) {
$this->needReviewerStatus = $need_reviewer_status;
public function needReviewers($need_reviewers) {
$this->needReviewers = $need_reviewers;
return $this;
}
@ -425,10 +410,6 @@ final class DifferentialRevisionQuery
$table = new DifferentialRevision();
$conn_r = $table->establishConnection('r');
if ($this->needRelationships) {
$this->loadRelationships($conn_r, $revisions);
}
if ($this->needCommitPHIDs) {
$this->loadCommitPHIDs($conn_r, $revisions);
}
@ -448,7 +429,7 @@ final class DifferentialRevisionQuery
$this->loadHashes($conn_r, $revisions);
}
if ($this->needReviewerStatus || $this->needReviewerAuthority) {
if ($this->needReviewers || $this->needReviewerAuthority) {
$this->loadReviewers($conn_r, $revisions);
}
@ -605,11 +586,11 @@ final class DifferentialRevisionQuery
if ($this->reviewers) {
$joins[] = qsprintf(
$conn_r,
'JOIN %T e_reviewers ON e_reviewers.src = r.phid '.
'AND e_reviewers.type = %s '.
'AND e_reviewers.dst in (%Ls)',
PhabricatorEdgeConfig::TABLE_NAME_EDGE,
DifferentialRevisionHasReviewerEdgeType::EDGECONST,
'JOIN %T reviewer ON reviewer.revisionPHID = r.phid
AND reviewer.reviewerStatus != %s
AND reviewer.reviewerPHID in (%Ls)',
id(new DifferentialReviewer())->getTableName(),
DifferentialReviewerStatus::STATUS_RESIGNED,
$this->reviewers);
}
@ -854,40 +835,6 @@ final class DifferentialRevisionQuery
);
}
private function loadRelationships($conn_r, array $revisions) {
assert_instances_of($revisions, 'DifferentialRevision');
$type_reviewer = DifferentialRevisionHasReviewerEdgeType::EDGECONST;
$type_subscriber = PhabricatorObjectHasSubscriberEdgeType::EDGECONST;
$edges = id(new PhabricatorEdgeQuery())
->withSourcePHIDs(mpull($revisions, 'getPHID'))
->withEdgeTypes(array($type_reviewer, $type_subscriber))
->setOrder(PhabricatorEdgeQuery::ORDER_OLDEST_FIRST)
->execute();
$type_map = array(
DifferentialRevision::RELATION_REVIEWER => $type_reviewer,
DifferentialRevision::RELATION_SUBSCRIBED => $type_subscriber,
);
foreach ($revisions as $revision) {
$data = array();
foreach ($type_map as $rel_type => $edge_type) {
$revision_edges = $edges[$revision->getPHID()][$edge_type];
foreach ($revision_edges as $dst_phid => $edge_data) {
$data[] = array(
'relation' => $rel_type,
'objectPHID' => $dst_phid,
'reasonPHID' => null,
);
}
}
$revision->attachRelationships($data);
}
}
private function loadCommitPHIDs($conn_r, array $revisions) {
assert_instances_of($revisions, 'DifferentialRevision');
$commit_phids = queryfx_all(
@ -972,21 +919,28 @@ final class DifferentialRevisionQuery
}
private function loadReviewers(
AphrontDatabaseConnection $conn_r,
AphrontDatabaseConnection $conn,
array $revisions) {
assert_instances_of($revisions, 'DifferentialRevision');
$edge_type = DifferentialRevisionHasReviewerEdgeType::EDGECONST;
$edges = id(new PhabricatorEdgeQuery())
->withSourcePHIDs(mpull($revisions, 'getPHID'))
->withEdgeTypes(array($edge_type))
->needEdgeData(true)
->setOrder(PhabricatorEdgeQuery::ORDER_OLDEST_FIRST)
->execute();
$reviewer_table = new DifferentialReviewer();
$reviewer_rows = queryfx_all(
$conn,
'SELECT * FROM %T WHERE revisionPHID IN (%Ls)
ORDER BY id ASC',
$reviewer_table->getTableName(),
mpull($revisions, 'getPHID'));
$reviewer_list = $reviewer_table->loadAllFromArray($reviewer_rows);
$reviewer_map = mgroup($reviewer_list, 'getRevisionPHID');
foreach ($reviewer_map as $key => $reviewers) {
$reviewer_map[$key] = mpull($reviewers, null, 'getReviewerPHID');
}
$viewer = $this->getViewer();
$viewer_phid = $viewer->getPHID();
$allow_key = 'differential.allow-self-accept';
$allow_self = PhabricatorEnv::getEnvConfig($allow_key);
@ -994,18 +948,13 @@ final class DifferentialRevisionQuery
if ($this->needReviewerAuthority && $viewer_phid) {
$authority = $this->loadReviewerAuthority(
$revisions,
$edges,
$reviewer_map,
$allow_self);
}
foreach ($revisions as $revision) {
$revision_edges = $edges[$revision->getPHID()][$edge_type];
$reviewers = array();
foreach ($revision_edges as $reviewer_phid => $edge) {
$reviewer = new DifferentialReviewerProxy(
$reviewer_phid,
$edge['data']);
$reviewers = idx($reviewer_map, $revision->getPHID(), array());
foreach ($reviewers as $reviewer_phid => $reviewer) {
if ($this->needReviewerAuthority) {
if (!$viewer_phid) {
// Logged-out users never have authority.
@ -1025,13 +974,13 @@ final class DifferentialRevisionQuery
$reviewers[$reviewer_phid] = $reviewer;
}
$revision->attachReviewerStatus($reviewers);
$revision->attachReviewers($reviewers);
}
}
private function loadReviewerAuthority(
array $revisions,
array $edges,
array $reviewers,
$allow_self) {
$revision_map = mpull($revisions, null, 'getPHID');
@ -1044,10 +993,9 @@ final class DifferentialRevisionQuery
$project_type = PhabricatorProjectProjectPHIDType::TYPECONST;
$package_type = PhabricatorOwnersPackagePHIDType::TYPECONST;
$edge_type = DifferentialRevisionHasReviewerEdgeType::EDGECONST;
foreach ($edges as $src => $types) {
foreach ($reviewers as $revision_phid => $reviewer_list) {
if (!$allow_self) {
if ($revision_map[$src]->getAuthorPHID() == $viewer_phid) {
if ($revision_map[$revision_phid]->getAuthorPHID() == $viewer_phid) {
// If self-review isn't permitted, the user will never have
// authority over projects on revisions they authored because you
// can't accept your own revisions, so we don't need to load any
@ -1055,14 +1003,14 @@ final class DifferentialRevisionQuery
continue;
}
}
$edge_data = idx($types, $edge_type, array());
foreach ($edge_data as $dst => $data) {
$phid_type = phid_get_type($dst);
foreach ($reviewer_list as $reviewer_phid => $reviewer) {
$phid_type = phid_get_type($reviewer_phid);
if ($phid_type == $project_type) {
$project_phids[] = $dst;
$project_phids[] = $reviewer_phid;
}
if ($phid_type == $package_type) {
$package_phids[] = $dst;
$package_phids[] = $reviewer_phid;
}
}
}

View file

@ -29,6 +29,14 @@ final class DifferentialRevisionRequiredActionResultBucket
}
$phids = array_fuse($phids);
// Before continuing, throw away any revisions which responsible users
// have explicitly resigned from.
// The goal is to allow users to resign from revisions they don't want to
// review to get these revisions off their dashboard, even if there are
// other project or package reviewers which they have authority over.
$this->filterResigned($phids);
$groups = array();
$groups[] = $this->newGroup()
@ -229,4 +237,25 @@ final class DifferentialRevisionRequiredActionResultBucket
return $results;
}
private function filterResigned(array $phids) {
$resigned = array(
DifferentialReviewerStatus::STATUS_RESIGNED,
);
$resigned = array_fuse($resigned);
$objects = $this->getRevisionsNotAuthored($this->objects, $phids);
$results = array();
foreach ($objects as $key => $object) {
if (!$this->hasReviewersWithStatus($object, $phids, $resigned)) {
continue;
}
$results[$key] = $object;
unset($this->objects[$key]);
}
return $results;
}
}

View file

@ -56,13 +56,13 @@ abstract class DifferentialRevisionResultBucket
array $phids,
array $statuses) {
foreach ($revision->getReviewerStatus() as $reviewer) {
foreach ($revision->getReviewers() as $reviewer) {
$reviewer_phid = $reviewer->getReviewerPHID();
if (empty($phids[$reviewer_phid])) {
continue;
}
$status = $reviewer->getStatus();
$status = $reviewer->getReviewerStatus();
if (empty($statuses[$status])) {
continue;
}

View file

@ -19,8 +19,7 @@ final class DifferentialRevisionSearchEngine
return id(new DifferentialRevisionQuery())
->needFlags(true)
->needDrafts(true)
->needRelationships(true)
->needReviewerStatus(true);
->needReviewers(true);
}
protected function buildQueryFromParameters(array $map) {
@ -163,10 +162,13 @@ final class DifferentialRevisionSearchEngine
$groups = $bucket->newResultGroups($query, $revisions);
foreach ($groups as $group) {
$views[] = id(clone $template)
->setHeader($group->getName())
->setNoDataString($group->getNoDataString())
->setRevisions($group->getObjects());
// Don't show groups in Dashboard Panels
if ($group->getObjects() || !$this->isPanelContext()) {
$views[] = id(clone $template)
->setHeader($group->getName())
->setNoDataString($group->getNoDataString())
->setRevisions($group->getObjects());
}
}
} catch (Exception $ex) {
$this->addError($ex->getMessage());
@ -177,6 +179,12 @@ final class DifferentialRevisionSearchEngine
->setHandles(array());
}
if (!$views) {
$views[] = id(new DifferentialRevisionListView())
->setUser($viewer)
->setNoDataString(pht('No revisions found.'));
}
$phids = array_mergev(mpull($views, 'getRequiredHandlePHIDs'));
if ($phids) {
$handles = id(new PhabricatorHandleQuery())

View file

@ -10,11 +10,11 @@ final class DifferentialRevisionFulltextEngine
$revision = id(new DifferentialRevisionQuery())
->setViewer($this->getViewer())
->withPHIDs(array($object->getPHID()))
->needReviewerStatus(true)
->needReviewers(true)
->executeOne();
// TODO: This isn't very clean, but custom fields currently rely on it.
$object->attachReviewerStatus($revision->getReviewerStatus());
$object->attachReviewers($revision->getReviewers());
$document->setDocumentTitle($revision->getTitle());
@ -36,8 +36,9 @@ final class DifferentialRevisionFulltextEngine
// owner is the author (e.g., accepted, rejected, closed).
$status_review = ArcanistDifferentialRevisionStatus::NEEDS_REVIEW;
if ($revision->getStatus() == $status_review) {
$reviewers = $revision->getReviewerStatus();
$reviewers = mpull($reviewers, 'getReviewerPHID', 'getReviewerPHID');
$reviewers = $revision->getReviewerPHIDs();
$reviewers = array_fuse($reviewers);
if ($reviewers) {
foreach ($reviewers as $phid) {
$document->addRelationship(

View file

@ -6,19 +6,81 @@ final class DifferentialReviewer
protected $revisionPHID;
protected $reviewerPHID;
protected $reviewerStatus;
protected $lastActionDiffPHID;
protected $lastCommentDiffPHID;
protected $lastActorPHID;
private $authority = array();
protected function getConfiguration() {
return array(
self::CONFIG_COLUMN_SCHEMA => array(
'reviewerStatus' => 'text64',
'lastActionDiffPHID' => 'phid?',
'lastCommentDiffPHID' => 'phid?',
'lastActorPHID' => 'phid?',
),
self::CONFIG_KEY_SCHEMA => array(
'key_revision' => array(
'columns' => array('revisionPHID', 'reviewerPHID'),
'unique' => true,
),
'key_reviewer' => array(
'columns' => array('reviewerPHID', 'revisionPHID'),
),
),
) + parent::getConfiguration();
}
public function isUser() {
$user_type = PhabricatorPeopleUserPHIDType::TYPECONST;
return (phid_get_type($this->getReviewerPHID()) == $user_type);
}
public function attachAuthority(PhabricatorUser $user, $has_authority) {
$this->authority[$user->getCacheFragment()] = $has_authority;
return $this;
}
public function hasAuthority(PhabricatorUser $viewer) {
$cache_fragment = $viewer->getCacheFragment();
return $this->assertAttachedKey($this->authority, $cache_fragment);
}
public function isResigned() {
$status_resigned = DifferentialReviewerStatus::STATUS_RESIGNED;
return ($this->getReviewerStatus() == $status_resigned);
}
public function isAccepted($diff_phid) {
$status_accepted = DifferentialReviewerStatus::STATUS_ACCEPTED;
if ($this->getReviewerStatus() != $status_accepted) {
return false;
}
if (!$diff_phid) {
return true;
}
$action_phid = $this->getLastActionDiffPHID();
if (!$action_phid) {
return true;
}
if ($action_phid == $diff_phid) {
return true;
}
$sticky_key = 'differential.sticky-accept';
$is_sticky = PhabricatorEnv::getEnvConfig($sticky_key);
if ($is_sticky) {
return true;
}
return false;
}
}

View file

@ -1,56 +0,0 @@
<?php
final class DifferentialReviewerProxy extends Phobject {
private $reviewerPHID;
private $status;
private $diffID;
private $authority = array();
public function __construct($reviewer_phid, array $edge_data) {
$this->reviewerPHID = $reviewer_phid;
$this->status = idx($edge_data, 'status');
$this->diffID = idx($edge_data, 'diff');
}
public function getReviewerPHID() {
return $this->reviewerPHID;
}
public function getStatus() {
return $this->status;
}
public function getDiffID() {
return $this->diffID;
}
public function isUser() {
$user_type = PhabricatorPeopleUserPHIDType::TYPECONST;
return (phid_get_type($this->getReviewerPHID()) == $user_type);
}
public function attachAuthority(PhabricatorUser $user, $has_authority) {
$this->authority[$user->getPHID()] = $has_authority;
return $this;
}
public function hasAuthority(PhabricatorUser $viewer) {
// It would be nice to use assertAttachedKey() here, but we don't extend
// PhabricatorLiskDAO, and faking that seems sketchy.
$viewer_phid = $viewer->getPHID();
if (!array_key_exists($viewer_phid, $this->authority)) {
throw new Exception(pht('You must %s first!', 'attachAuthority()'));
}
return $this->authority[$viewer_phid];
}
public function getEdgeData() {
return array(
'status' => $this->status,
'diffID' => $this->diffID,
);
}
}

View file

@ -38,7 +38,6 @@ final class DifferentialRevision extends DifferentialDAO
protected $editPolicy = PhabricatorPolicies::POLICY_USER;
protected $properties = array();
private $relationships = self::ATTACHABLE;
private $commits = self::ATTACHABLE;
private $activeDiff = self::ATTACHABLE;
private $diffIDs = self::ATTACHABLE;
@ -69,10 +68,9 @@ final class DifferentialRevision extends DifferentialDAO
return id(new DifferentialRevision())
->setViewPolicy($view_policy)
->setAuthorPHID($actor->getPHID())
->attachRelationships(array())
->attachRepository(null)
->attachActiveDiff(null)
->attachReviewerStatus(array())
->attachReviewers(array())
->setStatus(ArcanistDifferentialRevisionStatus::NEEDS_REVIEW);
}
@ -238,73 +236,6 @@ final class DifferentialRevision extends DifferentialDAO
return parent::save();
}
public function loadRelationships() {
if (!$this->getID()) {
$this->relationships = array();
return;
}
$data = array();
$subscriber_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
$this->getPHID(),
PhabricatorObjectHasSubscriberEdgeType::EDGECONST);
$subscriber_phids = array_reverse($subscriber_phids);
foreach ($subscriber_phids as $phid) {
$data[] = array(
'relation' => self::RELATION_SUBSCRIBED,
'objectPHID' => $phid,
'reasonPHID' => null,
);
}
$reviewer_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
$this->getPHID(),
DifferentialRevisionHasReviewerEdgeType::EDGECONST);
$reviewer_phids = array_reverse($reviewer_phids);
foreach ($reviewer_phids as $phid) {
$data[] = array(
'relation' => self::RELATION_REVIEWER,
'objectPHID' => $phid,
'reasonPHID' => null,
);
}
return $this->attachRelationships($data);
}
public function attachRelationships(array $relationships) {
$this->relationships = igroup($relationships, 'relation');
return $this;
}
public function getReviewers() {
return $this->getRelatedPHIDs(self::RELATION_REVIEWER);
}
public function getCCPHIDs() {
return $this->getRelatedPHIDs(self::RELATION_SUBSCRIBED);
}
private function getRelatedPHIDs($relation) {
$this->assertAttached($this->relationships);
return ipull($this->getRawRelations($relation), 'objectPHID');
}
public function getRawRelations($relation) {
return idx($this->relationships, $relation, array());
}
public function getPrimaryReviewer() {
$reviewers = $this->getReviewers();
$last = $this->lastReviewerPHID;
if (!$last || !in_array($last, $reviewers)) {
return head($this->getReviewers());
}
return $last;
}
public function getHashes() {
return $this->assertAttached($this->hashes);
}
@ -401,26 +332,31 @@ final class DifferentialRevision extends DifferentialDAO
);
}
public function getReviewerStatus() {
public function getReviewers() {
return $this->assertAttached($this->reviewerStatus);
}
public function attachReviewerStatus(array $reviewers) {
assert_instances_of($reviewers, 'DifferentialReviewerProxy');
public function attachReviewers(array $reviewers) {
assert_instances_of($reviewers, 'DifferentialReviewer');
$reviewers = mpull($reviewers, null, 'getReviewerPHID');
$this->reviewerStatus = $reviewers;
return $this;
}
public function getReviewerPHIDs() {
$reviewers = $this->getReviewers();
return mpull($reviewers, 'getReviewerPHID');
}
public function getReviewerPHIDsForEdit() {
$reviewers = $this->getReviewerStatus();
$reviewers = $this->getReviewers();
$status_blocking = DifferentialReviewerStatus::STATUS_BLOCKING;
$value = array();
foreach ($reviewers as $reviewer) {
$phid = $reviewer->getReviewerPHID();
if ($reviewer->getStatus() == $status_blocking) {
if ($reviewer->getReviewerStatus() == $status_blocking) {
$value[] = 'blocking('.$phid.')';
} else {
$value[] = $phid;
@ -543,11 +479,11 @@ final class DifferentialRevision extends DifferentialDAO
$reviewers = id(new DifferentialRevisionQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withPHIDs(array($this->getPHID()))
->needReviewerStatus(true)
->needReviewers(true)
->executeOne()
->getReviewerStatus();
->getReviewers();
} else {
$reviewers = $this->getReviewerStatus();
$reviewers = $this->getReviewers();
}
foreach ($reviewers as $reviewer) {

View file

@ -212,13 +212,6 @@ final class DifferentialTransaction
$tags[] = self::MAILTAG_UPDATED;
}
break;
case PhabricatorTransactions::TYPE_EDGE:
switch ($this->getMetadataValue('edge:type')) {
case DifferentialRevisionHasReviewerEdgeType::EDGECONST:
$tags[] = self::MAILTAG_REVIEWERS;
break;
}
break;
case PhabricatorTransactions::TYPE_COMMENT:
case self::TYPE_INLINE:
$tags[] = self::MAILTAG_COMMENT;
@ -598,14 +591,6 @@ final class DifferentialTransaction
public function getNoEffectDescription() {
switch ($this->getTransactionType()) {
case PhabricatorTransactions::TYPE_EDGE:
switch ($this->getMetadataValue('edge:type')) {
case DifferentialRevisionHasReviewerEdgeType::EDGECONST:
return pht(
'The reviewers you are trying to add are already reviewing '.
'this revision.');
}
break;
case self::TYPE_ACTION:
switch ($this->getNewValue()) {
case DifferentialAction::ACTION_CLOSE:
@ -624,18 +609,10 @@ final class DifferentialTransaction
return pht('This revision already requires changes.');
case DifferentialAction::ACTION_REQUEST:
return pht('Review is already requested for this revision.');
case DifferentialAction::ACTION_RESIGN:
return pht(
'You can not resign from this revision because you are not '.
'a reviewer.');
case DifferentialAction::ACTION_CLAIM:
return pht(
'You can not commandeer this revision because you already own '.
'it.');
case DifferentialAction::ACTION_ACCEPT:
return pht('You have already accepted this revision.');
case DifferentialAction::ACTION_REJECT:
return pht('You have already requested changes to this revision.');
}
break;
}

View file

@ -7,7 +7,7 @@ final class DifferentialReviewersView extends AphrontView {
private $diff;
public function setReviewers(array $reviewers) {
assert_instances_of($reviewers, 'DifferentialReviewerProxy');
assert_instances_of($reviewers, 'DifferentialReviewer');
$this->reviewers = $reviewers;
return $this;
}
@ -25,53 +25,74 @@ final class DifferentialReviewersView extends AphrontView {
public function render() {
$viewer = $this->getUser();
$reviewers = $this->reviewers;
$view = new PHUIStatusListView();
foreach ($this->reviewers as $reviewer) {
// Move resigned reviewers to the bottom.
$head = array();
$tail = array();
foreach ($reviewers as $key => $reviewer) {
if ($reviewer->isResigned()) {
$tail[$key] = $reviewer;
} else {
$head[$key] = $reviewer;
}
}
$reviewers = $head + $tail;
foreach ($reviewers as $reviewer) {
$phid = $reviewer->getReviewerPHID();
$handle = $this->handles[$phid];
// If we're missing either the diff or action information for the
// reviewer, render information as current.
$is_current = (!$this->diff) ||
(!$reviewer->getDiffID()) ||
($this->diff->getID() == $reviewer->getDiffID());
$action_phid = $reviewer->getLastActionDiffPHID();
$is_current_action = $this->isCurrent($action_phid);
$comment_phid = $reviewer->getLastCommentDiffPHID();
$is_current_comment = $this->isCurrent($comment_phid);
$item = new PHUIStatusItemView();
$item->setHighlighted($reviewer->hasAuthority($viewer));
switch ($reviewer->getStatus()) {
switch ($reviewer->getReviewerStatus()) {
case DifferentialReviewerStatus::STATUS_ADDED:
$item->setIcon(
PHUIStatusItemView::ICON_OPEN,
'bluegrey',
pht('Review Requested'));
if ($comment_phid) {
if ($is_current_comment) {
$item->setIcon(
'fa-comment',
'blue',
pht('Commented'));
} else {
$item->setIcon(
'fa-comment-o',
'bluegrey',
pht('Commented Previously'));
}
} else {
$item->setIcon(
PHUIStatusItemView::ICON_OPEN,
'bluegrey',
pht('Review Requested'));
}
break;
case DifferentialReviewerStatus::STATUS_ACCEPTED:
if ($is_current) {
if ($is_current_action) {
$item->setIcon(
PHUIStatusItemView::ICON_ACCEPT,
'green',
pht('Accepted'));
} else {
$item->setIcon(
PHUIStatusItemView::ICON_ACCEPT,
'fa-check-circle-o',
'bluegrey',
pht('Accepted Prior Diff'));
}
break;
case DifferentialReviewerStatus::STATUS_ACCEPTED_OLDER:
$item->setIcon(
'fa-check-circle-o',
'bluegrey',
pht('Accepted Prior Diff'));
break;
case DifferentialReviewerStatus::STATUS_REJECTED:
if ($is_current) {
if ($is_current_action) {
$item->setIcon(
PHUIStatusItemView::ICON_REJECT,
'red',
@ -84,27 +105,6 @@ final class DifferentialReviewersView extends AphrontView {
}
break;
case DifferentialReviewerStatus::STATUS_REJECTED_OLDER:
$item->setIcon(
'fa-times-circle-o',
'bluegrey',
pht('Rejected Prior Diff'));
break;
case DifferentialReviewerStatus::STATUS_COMMENTED:
if ($is_current) {
$item->setIcon(
'fa-question-circle',
'blue',
pht('Commented'));
} else {
$item->setIcon(
'fa-question-circle-o',
'bluegrey',
pht('Commented Previously'));
}
break;
case DifferentialReviewerStatus::STATUS_BLOCKING:
$item->setIcon(
PHUIStatusItemView::ICON_MINUS,
@ -112,11 +112,18 @@ final class DifferentialReviewersView extends AphrontView {
pht('Blocking Review'));
break;
case DifferentialReviewerStatus::STATUS_RESIGNED:
$item->setIcon(
'fa-times',
'grey',
pht('Resigned'));
break;
default:
$item->setIcon(
PHUIStatusItemView::ICON_QUESTION,
'bluegrey',
pht('%s?', $reviewer->getStatus()));
pht('%s?', $reviewer->getReviewerStatus()));
break;
}
@ -128,4 +135,26 @@ final class DifferentialReviewersView extends AphrontView {
return $view;
}
private function isCurrent($action_phid) {
if (!$this->diff) {
echo "A\n";
return true;
}
if (!$action_phid) {
return true;
}
$diff_phid = $this->diff->getPHID();
if (!$diff_phid) {
return true;
}
if ($diff_phid == $action_phid) {
return true;
}
return false;
}
}

View file

@ -52,10 +52,7 @@ final class DifferentialRevisionListView extends AphrontView {
$phids = array();
foreach ($this->revisions as $revision) {
$phids[] = array($revision->getAuthorPHID());
// TODO: Switch to getReviewerStatus(), but not all callers pass us
// revisions with this data loaded.
$phids[] = $revision->getReviewers();
$phids[] = $revision->getReviewerPHIDs();
}
return array_mergev($phids);
}
@ -132,8 +129,7 @@ final class DifferentialRevisionListView extends AphrontView {
}
$reviewers = array();
// TODO: As above, this should be based on `getReviewerStatus()`.
foreach ($revision->getReviewers() as $reviewer) {
foreach ($revision->getReviewerPHIDs() as $reviewer) {
$reviewers[] = $this->handles[$reviewer]->renderLink();
}
if (!$reviewers) {

View file

@ -48,6 +48,72 @@ final class DifferentialRevisionAcceptTransaction
return pht('Accept a revision.');
}
protected function getActionOptions(
PhabricatorUser $viewer,
DifferentialRevision $revision) {
$reviewers = $revision->getReviewers();
$options = array();
$value = array();
// Put the viewer's user reviewer first, if it exists, so that "Accept as
// yourself" is always at the top.
$head = array();
$tail = array();
foreach ($reviewers as $key => $reviewer) {
if ($reviewer->isUser()) {
$head[$key] = $reviewer;
} else {
$tail[$key] = $reviewer;
}
}
$reviewers = $head + $tail;
$diff_phid = $this->getActiveDiffPHID($revision);
$reviewer_phids = array();
// If the viewer isn't a reviewer, add them to the list of options first.
// This happens when you navigate to some revision you aren't involved in:
// you can accept and become a reviewer.
$viewer_phid = $viewer->getPHID();
if ($viewer_phid) {
if (!isset($reviewers[$viewer_phid])) {
$reviewer_phids[$viewer_phid] = $viewer_phid;
}
}
foreach ($reviewers as $reviewer) {
if (!$reviewer->hasAuthority($viewer)) {
// If the viewer doesn't have authority to act on behalf of a reviewer,
// don't include that reviewer as an option.
continue;
}
if ($reviewer->isAccepted($diff_phid)) {
// If a reviewer is already in a full "accepted" state, don't
// include that reviewer as an option.
continue;
}
$reviewer_phid = $reviewer->getReviewerPHID();
$reviewer_phids[$reviewer_phid] = $reviewer_phid;
}
$handles = $viewer->loadHandles($reviewer_phids);
foreach ($reviewer_phids as $reviewer_phid) {
$options[$reviewer_phid] = pht(
'Accept as %s',
$viewer->renderHandle($reviewer_phid));
$value[] = $reviewer_phid;
}
return array($options, $value);
}
public function generateOldValue($object) {
$actor = $this->getActor();
return $this->isViewerFullyAccepted($object, $actor);
@ -87,10 +153,39 @@ final class DifferentialRevisionAcceptTransaction
}
}
protected function validateOptionValue($object, $actor, array $value) {
if (!$value) {
throw new Exception(
pht(
'When accepting a revision, you must accept on behalf of at '.
'least one reviewer.'));
}
list($options) = $this->getActionOptions($actor, $object);
foreach ($value as $phid) {
if (!isset($options[$phid])) {
throw new Exception(
pht(
'Reviewer "%s" is not a valid reviewer which you have authority '.
'to accept on behalf of.',
$phid));
}
}
}
public function getTitle() {
return pht(
'%s accepted this revision.',
$this->renderAuthor());
$new = $this->getNewValue();
if (is_array($new) && $new) {
return pht(
'%s accepted this revision as %s reviewer(s): %s.',
$this->renderAuthor(),
phutil_count($new),
$this->renderHandleList($new));
} else {
return pht(
'%s accepted this revision.',
$this->renderAuthor());
}
}
public function getTitleForFeed() {

View file

@ -19,6 +19,10 @@ abstract class DifferentialRevisionActionTransaction
abstract protected function validateAction($object, PhabricatorUser $viewer);
abstract protected function getRevisionActionLabel();
protected function validateOptionValue($object, $actor, array $value) {
return null;
}
public function getCommandKeyword() {
return null;
}
@ -70,6 +74,15 @@ abstract class DifferentialRevisionActionTransaction
return ($viewer->getPHID() === $revision->getAuthorPHID());
}
protected function getActionOptions(
PhabricatorUser $viewer,
DifferentialRevision $revision) {
return array(
array(),
null,
);
}
public function newEditField(
DifferentialRevision $revision,
PhabricatorUser $viewer) {
@ -107,6 +120,12 @@ abstract class DifferentialRevisionActionTransaction
// It's not clear that these combinations are actually useful, so just
// keep things simple for now.
$field->setActionConflictKey('revision.action');
list($options, $value) = $this->getActionOptions($viewer, $revision);
if (count($options) > 1) {
$field->setOptions($options);
$field->setValue($value);
}
}
}
@ -129,6 +148,20 @@ abstract class DifferentialRevisionActionTransaction
$errors[] = $this->newInvalidError(
$action_exception->getMessage(),
$xaction);
continue;
}
$new = $xaction->getNewValue();
if (!is_array($new)) {
continue;
}
try {
$this->validateOptionValue($object, $actor, $new);
} catch (Exception $ex) {
$errors[] = $this->newInvalidError(
$ex->getMessage(),
$xaction);
}
}

View file

@ -44,7 +44,9 @@ final class DifferentialRevisionResignTransaction
public function generateOldValue($object) {
$actor = $this->getActor();
return !$this->isViewerAnyReviewer($object, $actor);
$resigned = DifferentialReviewerStatus::STATUS_RESIGNED;
return ($this->getViewerReviewerStatus($object, $actor) == $resigned);
}
public function applyExternalEffects($object, $value) {
@ -61,12 +63,19 @@ final class DifferentialRevisionResignTransaction
'been closed. You can only resign from open revisions.'));
}
if (!$this->isViewerAnyReviewer($object, $viewer)) {
$resigned = DifferentialReviewerStatus::STATUS_RESIGNED;
if ($this->getViewerReviewerStatus($object, $viewer) == $resigned) {
throw new Exception(
pht(
'You can not resign from this revision because you have already '.
'resigned.'));
}
if (!$this->isViewerAnyAuthority($object, $viewer)) {
throw new Exception(
pht(
'You can not resign from this revision because you are not a '.
'reviewer. You can only resign from revisions where you are a '.
'reviewer.'));
'reviewer, and do not have authority over any reviewer.'));
}
}

View file

@ -7,12 +7,46 @@ abstract class DifferentialRevisionReviewTransaction
return DifferentialRevisionEditEngine::ACTIONGROUP_REVIEW;
}
public function generateNewValue($object, $value) {
if (!is_array($value)) {
return true;
}
// If the list of options is the same as the default list, just treat this
// as a "take the default action" transaction.
$viewer = $this->getActor();
list($options, $default) = $this->getActionOptions($viewer, $object);
sort($default);
sort($value);
if ($default === $value) {
return true;
}
return $value;
}
protected function isViewerAnyReviewer(
DifferentialRevision $revision,
PhabricatorUser $viewer) {
return ($this->getViewerReviewerStatus($revision, $viewer) !== null);
}
protected function isViewerAnyAuthority(
DifferentialRevision $revision,
PhabricatorUser $viewer) {
$reviewers = $revision->getReviewers();
foreach ($revision->getReviewers() as $reviewer) {
if ($reviewer->hasAuthority($viewer)) {
return true;
}
}
return false;
}
protected function isViewerFullyAccepted(
DifferentialRevision $revision,
PhabricatorUser $viewer) {
@ -21,7 +55,8 @@ abstract class DifferentialRevisionReviewTransaction
$viewer,
array(
DifferentialReviewerStatus::STATUS_ACCEPTED,
));
),
true);
}
protected function isViewerFullyRejected(
@ -32,7 +67,8 @@ abstract class DifferentialRevisionReviewTransaction
$viewer,
array(
DifferentialReviewerStatus::STATUS_REJECTED,
));
),
true);
}
protected function getViewerReviewerStatus(
@ -43,12 +79,12 @@ abstract class DifferentialRevisionReviewTransaction
return null;
}
foreach ($revision->getReviewerStatus() as $reviewer) {
foreach ($revision->getReviewers() as $reviewer) {
if ($reviewer->getReviewerPHID() != $viewer->getPHID()) {
continue;
}
return $reviewer->getStatus();
return $reviewer->getReviewerStatus();
}
return null;
@ -57,7 +93,8 @@ abstract class DifferentialRevisionReviewTransaction
protected function isViewerReviewerStatusFullyAmong(
DifferentialRevision $revision,
PhabricatorUser $viewer,
array $status_list) {
array $status_list,
$require_current) {
// If the user themselves is not a reviewer, the reviews they have
// authority over can not all be in any set of states since their own
@ -67,18 +104,26 @@ abstract class DifferentialRevisionReviewTransaction
return false;
}
$active_phid = $this->getActiveDiffPHID($revision);
// Otherwise, check that all reviews they have authority over are in
// the desired set of states.
$status_map = array_fuse($status_list);
foreach ($revision->getReviewerStatus() as $reviewer) {
foreach ($revision->getReviewers() as $reviewer) {
if (!$reviewer->hasAuthority($viewer)) {
continue;
}
$status = $reviewer->getStatus();
$status = $reviewer->getReviewerStatus();
if (!isset($status_map[$status])) {
return false;
}
if ($require_current) {
if ($reviewer->getLastActionDiffPHID() != $active_phid) {
return false;
}
}
}
return true;
@ -97,7 +142,7 @@ abstract class DifferentialRevisionReviewTransaction
// yourself.
$with_authority = ($status != DifferentialReviewerStatus::STATUS_RESIGNED);
if ($with_authority) {
foreach ($revision->getReviewerStatus() as $reviewer) {
foreach ($revision->getReviewers() as $reviewer) {
if ($reviewer->hasAuthority($viewer)) {
$map[$reviewer->getReviewerPHID()] = $status;
}
@ -107,6 +152,12 @@ abstract class DifferentialRevisionReviewTransaction
// In all cases, you affect yourself.
$map[$viewer->getPHID()] = $status;
// If the user has submitted a specific list of reviewers to act as (by
// unchecking some checkboxes under "Accept"), only affect those reviewers.
if (is_array($value)) {
$map = array_select_keys($map, $value);
}
// Convert reviewer statuses into edge data.
foreach ($map as $reviewer_phid => $reviewer_status) {
$map[$reviewer_phid] = array(
@ -138,6 +189,13 @@ abstract class DifferentialRevisionReviewTransaction
// Now, do the new write.
if ($map) {
$diff = $this->getEditor()->getActiveDiff($revision);
if ($diff) {
$diff_phid = $diff->getPHID();
} else {
$diff_phid = null;
}
$table = new DifferentialReviewer();
$reviewers = $table->loadAllWhere(
@ -154,18 +212,21 @@ abstract class DifferentialRevisionReviewTransaction
->setReviewerPHID($dst_phid);
}
$old_status = $reviewer->getReviewerStatus();
$reviewer->setReviewerStatus($status);
if ($status == DifferentialReviewerStatus::STATUS_RESIGNED) {
if ($reviewer->getID()) {
$reviewer->delete();
}
} else {
try {
$reviewer->save();
} catch (AphrontDuplicateKeyQueryException $ex) {
// At least for now, just ignore it if we lost a race.
}
if ($diff_phid) {
$reviewer->setLastActionDiffPHID($diff_phid);
}
if ($old_status !== $status) {
$reviewer->setLastActorPHID($this->getActingAsPHID());
}
try {
$reviewer->save();
} catch (AphrontDuplicateKeyQueryException $ex) {
// At least for now, just ignore it if we lost a race.
}
}
}

View file

@ -7,8 +7,8 @@ final class DifferentialRevisionReviewersTransaction
const EDITKEY = 'reviewers';
public function generateOldValue($object) {
$reviewers = $object->getReviewerStatus();
$reviewers = mpull($reviewers, 'getStatus', 'getReviewerPHID');
$reviewers = $object->getReviewers();
$reviewers = mpull($reviewers, 'getReviewerStatus', 'getReviewerPHID');
return $reviewers;
}

View file

@ -57,4 +57,16 @@ abstract class DifferentialRevisionTransactionType
$xaction);
}
protected function getActiveDiffPHID(DifferentialRevision $revision) {
try {
$diff = $revision->getActiveDiff();
if (!$diff) {
return null;
}
return $diff->getPHID();
} catch (Exception $ex) {
return null;
}
}
}

View file

@ -1765,7 +1765,7 @@ final class DiffusionBrowseController extends DiffusionController {
->withUpdatedEpochBetween($recent, null)
->setOrder(DifferentialRevisionQuery::ORDER_MODIFIED)
->setLimit(10)
->needRelationships(true)
->needReviewers(true)
->needFlags(true)
->needDrafts(true)
->execute();

View file

@ -20,7 +20,7 @@ final class DiffusionCommitRevisionReviewersHeraldField
return array();
}
return $revision->getReviewers();
return $revision->getReviewerPHIDs();
}
protected function getHeraldFieldStandardType() {

View file

@ -20,8 +20,8 @@ final class DiffusionPreCommitContentRevisionReviewersHeraldField
return array();
}
return $revision->getReviewers();
}
return $revision->getReviewerPHIDs();
}
protected function getHeraldFieldStandardType() {
return self::STANDARD_PHID_LIST;

View file

@ -190,8 +190,7 @@ final class HeraldCommitAdapter
$revision = id(new DifferentialRevisionQuery())
->withIDs(array($revision_id))
->setViewer(PhabricatorUser::getOmnipotentUser())
->needRelationships(true)
->needReviewerStatus(true)
->needReviewers(true)
->executeOne();
if ($revision) {
$this->affectedRevision = $revision;

View file

@ -190,7 +190,7 @@ final class HeraldPreCommitContentAdapter extends HeraldPreCommitAdapter {
$this->revision = id(new DifferentialRevisionQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withIDs(array($revision_id))
->needRelationships(true)
->needReviewers(true)
->executeOne();
}
}

View file

@ -119,6 +119,7 @@ final class PhabricatorFilesComposeAvatarBuiltinFile
foreach ($list as $file) {
$map['alphanumeric/'.$file] = $root.$file;
}
return $map;
}
@ -138,11 +139,11 @@ final class PhabricatorFilesComposeAvatarBuiltinFile
$border_seed = $username.'_border';
$pack_key =
PhabricatorHash::digestToRange($pack_seed, 1, $pack_count);
PhabricatorHash::digestToRange($pack_seed, 0, $pack_count - 1);
$color_key =
PhabricatorHash::digestToRange($color_seed, 1, $color_count);
PhabricatorHash::digestToRange($color_seed, 0, $color_count - 1);
$border_key =
PhabricatorHash::digestToRange($border_seed, 1, $border_count);
PhabricatorHash::digestToRange($border_seed, 0, $border_count - 1);
$pack = $pack_map[$pack_key];
$icon = 'alphanumeric/'.$pack.'/'.$file.'.png';
@ -188,7 +189,7 @@ final class PhabricatorFilesComposeAvatarBuiltinFile
->withFollowSymlinks(false)
->find();
return $map;
return array_values($map);
}
public static function getBorderMap() {

View file

@ -10,13 +10,7 @@ final class PhabricatorPeopleProfileBadgesController
$user = id(new PhabricatorPeopleQuery())
->setViewer($viewer)
->withIDs(array($id))
->needProfile(true)
->needProfileImage(true)
->needAvailability(true)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
))
->executeOne();
if (!$user) {
return new Aphront404Response();
@ -50,6 +44,7 @@ final class PhabricatorPeopleProfileBadgesController
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->setLimit(1)
->execute();
$button = id(new PHUIButtonView())
@ -59,7 +54,7 @@ final class PhabricatorPeopleProfileBadgesController
->setWorkflow(true)
->setHref('/badges/award/'.$user->getID().'/');
if (count($badges)) {
if ($badges) {
$header->addActionLink($button);
}
@ -80,47 +75,43 @@ final class PhabricatorPeopleProfileBadgesController
private function buildBadgesView(PhabricatorUser $user) {
$viewer = $this->getViewer();
$request = $this->getRequest();
$awards = id(new PhabricatorBadgesAwardQuery())
$pager = id(new AphrontCursorPagerView())
->readFromRequest($request);
$query = id(new PhabricatorBadgesAwardQuery())
->setViewer($viewer)
->withRecipientPHIDs(array($user->getPHID()))
->withBadgeStatuses(array(PhabricatorBadgesBadge::STATUS_ACTIVE))
->execute();
$awards = mpull($awards, null, 'getBadgePHID');
->withBadgeStatuses(array(PhabricatorBadgesBadge::STATUS_ACTIVE));
$badges = array();
foreach ($awards as $award) {
$badge = $award->getBadge();
$badges[$award->getBadgePHID()] = $badge;
}
$awards = $query->executeWithCursorPager($pager);
if (count($badges)) {
if ($awards) {
$flex = new PHUIBadgeBoxView();
foreach ($awards as $award) {
$badge = $award->getBadge();
foreach ($badges as $badge) {
if ($badge) {
$awarder_info = array();
$awarder_info = array();
$award = idx($awards, $badge->getPHID(), null);
$awarder_phid = $award->getAwarderPHID();
$awarder_handle = $viewer->renderHandle($awarder_phid);
$awarded_date = phabricator_date($award->getDateCreated(), $viewer);
$awarder_phid = $award->getAwarderPHID();
$awarder_handle = $viewer->renderHandle($awarder_phid);
$awarded_date = phabricator_date($award->getDateCreated(), $viewer);
$awarder_info = pht(
'Awarded by %s',
$awarder_handle->render());
$awarder_info = pht(
'Awarded by %s',
$awarder_handle->render());
$item = id(new PHUIBadgeView())
->setIcon($badge->getIcon())
->setHeader($badge->getName())
->setSubhead($badge->getFlavor())
->setQuality($badge->getQuality())
->setHref($badge->getViewURI())
->addByLine($awarder_info)
->addByLine($awarded_date);
$item = id(new PHUIBadgeView())
->setIcon($badge->getIcon())
->setHeader($badge->getName())
->setSubhead($badge->getFlavor())
->setQuality($badge->getQuality())
->setHref($badge->getViewURI())
->addByLine($awarder_info)
->addByLine($awarded_date);
$flex->addItem($item);
}
$flex->addItem($item);
}
} else {
$flex = id(new PHUIInfoView())
@ -128,6 +119,9 @@ final class PhabricatorPeopleProfileBadgesController
->appendChild(pht('User has not been awarded any badges.'));
}
return $flex;
return array(
$flex,
$pager,
);
}
}

View file

@ -138,6 +138,10 @@ final class PhabricatorProjectApplication extends PhabricatorApplication {
);
}
public function getApplicationOrder() {
return 0.150;
}
public function getHelpDocumentationArticles(PhabricatorUser $viewer) {
return array(
array(

View file

@ -228,7 +228,10 @@ final class PhabricatorRepositoryPullLocalDaemon
continue;
}
$this->waitForUpdates($min_sleep, $retry_after);
$should_hibernate = $this->waitForUpdates($min_sleep, $retry_after);
if ($should_hibernate) {
break;
}
}
}
@ -492,6 +495,10 @@ final class PhabricatorRepositoryPullLocalDaemon
while (($sleep_until - time()) > 0) {
$sleep_duration = ($sleep_until - time());
if ($this->shouldHibernate($sleep_duration)) {
return true;
}
$this->log(
pht(
'Sleeping for %s more second(s)...',
@ -501,7 +508,7 @@ final class PhabricatorRepositoryPullLocalDaemon
if ($this->shouldExit()) {
$this->log(pht('Awakened from sleep by graceful shutdown!'));
return;
return false;
}
if ($this->loadRepositoryUpdateMessages()) {
@ -509,6 +516,8 @@ final class PhabricatorRepositoryPullLocalDaemon
break;
}
}
return false;
}
}

View file

@ -0,0 +1,38 @@
<?php
final class PhabricatorRepositoryPullLocalDaemonModule
extends PhutilDaemonOverseerModule {
private $cursor = 0;
public function shouldWakePool(PhutilDaemonPool $pool) {
$class = $pool->getPoolDaemonClass();
if ($class != 'PhabricatorRepositoryPullLocalDaemon') {
return false;
}
if ($this->shouldThrottle($class, 1)) {
return false;
}
$table = new PhabricatorRepositoryStatusMessage();
$table_name = $table->getTableName();
$conn = $table->establishConnection('r');
$row = queryfx_one(
$conn,
'SELECT id FROM %T WHERE statusType = %s
AND id > %d ORDER BY id DESC LIMIT 1',
$table_name,
PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE,
$this->cursor);
if (!$row) {
return false;
}
$this->cursor = (int)$row['id'];
return true;
}
}

View file

@ -67,7 +67,7 @@ final class PhabricatorRepositoryCommitOwnersWorker
$revision = id(new DifferentialRevisionQuery())
->setViewer($viewer)
->withIDs(array($revision_id))
->needReviewerStatus(true)
->needReviewers(true)
->executeOne();
} else {
$revision = null;
@ -165,7 +165,7 @@ final class PhabricatorRepositoryCommitOwnersWorker
$accepted_statuses = array_fuse($accepted_statuses);
$found_accept = false;
foreach ($revision->getReviewerStatus() as $reviewer) {
foreach ($revision->getReviewers() as $reviewer) {
$reviewer_phid = $reviewer->getReviewerPHID();
// If this reviewer isn't a package owner, just ignore them.
@ -175,7 +175,7 @@ final class PhabricatorRepositoryCommitOwnersWorker
// If this reviewer accepted the revision and owns the package, we're
// all clear and do not need to trigger an audit.
if (isset($accepted_statuses[$reviewer->getStatus()])) {
if (isset($accepted_statuses[$reviewer->getReviewerStatus()])) {
$found_accept = true;
break;
}

View file

@ -178,7 +178,7 @@ abstract class PhabricatorRepositoryCommitMessageParserWorker
$revision_query = id(new DifferentialRevisionQuery())
->withIDs(array($revision_id))
->setViewer($actor)
->needReviewerStatus(true)
->needReviewers(true)
->needActiveDiffs(true);
$revision = $revision_query->executeOne();

View file

@ -555,8 +555,9 @@ final class PhabricatorApplicationSearchController
->setTag('a')
->setHref('#')
->setText(pht('Use Results...'))
->setIcon('fa-road')
->setDropdownMenu($action_list);
->setIcon('fa-bars')
->setDropdownMenu($action_list)
->addClass('dropdown');
}
private function newOverflowingView() {
@ -600,9 +601,32 @@ final class PhabricatorApplicationSearchController
private function newBuiltinUseActions() {
$actions = array();
$request = $this->getRequest();
$viewer = $request->getUser();
$is_dev = PhabricatorEnv::getEnvConfig('phabricator.developer-mode');
$engine = $this->getSearchEngine();
$engine_class = get_class($engine);
$query_key = $this->getQueryKey();
if (!$query_key) {
$query_key = head_key($engine->loadEnabledNamedQueries());
}
$can_use = $engine->canUseInPanelContext();
$is_installed = PhabricatorApplication::isClassInstalledForViewer(
'PhabricatorDashboardApplication',
$viewer);
if ($can_use && $is_installed) {
$dashboard_uri = '/dashboard/install/';
$actions[] = id(new PhabricatorActionView())
->setIcon('fa-dashboard')
->setName(pht('Add to Dasbhoard'))
->setWorkflow(true)
->setHref("/dashboard/panel/install/{$engine_class}/{$query_key}/");
}
if ($is_dev) {
$engine = $this->getSearchEngine();
$nux_uri = $engine->getQueryBaseURI();
@ -610,8 +634,8 @@ final class PhabricatorApplicationSearchController
->setQueryParam('nux', true);
$actions[] = id(new PhabricatorActionView())
->setIcon('fa-bug')
->setName(pht('Developer: Show New User State'))
->setIcon('fa-user-plus')
->setName(pht('DEV: New User State'))
->setHref($nux_uri);
}
@ -620,8 +644,8 @@ final class PhabricatorApplicationSearchController
->setQueryParam('overheated', true);
$actions[] = id(new PhabricatorActionView())
->setIcon('fa-bug')
->setName(pht('Developer: Show Overheated State'))
->setIcon('fa-fire')
->setName(pht('DEV: Overheated State'))
->setHref($overheated_uri);
}

View file

@ -0,0 +1,36 @@
<?php
final class PhabricatorEditEngineCheckboxesCommentAction
extends PhabricatorEditEngineCommentAction {
private $options = array();
public function setOptions(array $options) {
$this->options = $options;
return $this;
}
public function getOptions() {
return $this->options;
}
public function getPHUIXControlType() {
return 'checkboxes';
}
public function getPHUIXControlSpecification() {
$options = $this->getOptions();
$labels = array();
foreach ($options as $key => $option) {
$labels[$key] = hsprintf('%s', $option);
}
return array(
'value' => $this->getValue(),
'keys' => array_keys($options),
'labels' => $labels,
);
}
}

View file

@ -163,15 +163,6 @@ final class PhabricatorEditEngineConfigurationViewController
->setDisabled(!$can_edit));
}
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Change Default Values'))
->setIcon('fa-paint-brush')
->setHref($defaults_uri)
->setWorkflow(!$can_edit)
->setDisabled(!$can_edit));
$disable_uri = "{$base_uri}/disable/{$form_key}/";
if ($config->getIsDisabled()) {

View file

@ -996,8 +996,12 @@ abstract class PhabricatorEditEngine
$config = $this->getEditEngineConfiguration()
->attachEngine($this);
// NOTE: Don't prompt users to override locks when creating objects,
// even if the default settings would create a locked object.
$can_interact = PhabricatorPolicyFilter::canInteract($viewer, $object);
if (!$can_interact &&
!$this->getIsCreate() &&
!$request->getBool('editEngine') &&
!$request->getBool('overrideLock')) {

View file

@ -5,6 +5,7 @@ final class PhabricatorApplyEditField
private $actionDescription;
private $actionConflictKey;
private $options;
protected function newControl() {
return null;
@ -28,8 +29,21 @@ final class PhabricatorApplyEditField
return $this->actionConflictKey;
}
public function setOptions(array $options) {
$this->options = $options;
return $this;
}
public function getOptions() {
return $this->options;
}
protected function newHTTPParameterType() {
return new AphrontBoolHTTPParameterType();
if ($this->getOptions()) {
return new AphrontPHIDListHTTPParameterType();
} else {
return new AphrontBoolHTTPParameterType();
}
}
protected function newConduitParameterType() {
@ -43,9 +57,16 @@ final class PhabricatorApplyEditField
}
protected function newCommentAction() {
return id(new PhabricatorEditEngineStaticCommentAction())
->setDescription($this->getActionDescription())
->setConflictKey($this->getActionConflictKey());
$options = $this->getOptions();
if ($options) {
return id(new PhabricatorEditEngineCheckboxesCommentAction())
->setConflictKey($this->getActionConflictKey())
->setOptions($options);
} else {
return id(new PhabricatorEditEngineStaticCommentAction())
->setConflictKey($this->getActionConflictKey())
->setDescription($this->getActionDescription());
}
}
}

View file

@ -92,6 +92,7 @@ final class PhabricatorApplicationTransactionCommentEditor
$editor
->setContentSource($this->getContentSource())
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true)
->applyTransactions($object, $support_xactions);
}
}

View file

@ -932,7 +932,15 @@ abstract class PhabricatorApplicationTransaction
$type = $this->getMetadata('edge:type');
$type = head($type);
$type_obj = PhabricatorEdgeType::getByConstant($type);
try {
$type_obj = PhabricatorEdgeType::getByConstant($type);
} catch (Exception $ex) {
// Recover somewhat gracefully from edge transactions which
// we don't have the classes for.
return pht(
'%s edited an edge.',
$this->renderHandleLink($author_phid));
}
if ($add && $rem) {
return $type_obj->getTransactionEditString(

View file

@ -121,5 +121,3 @@ Conpherence room on this install, and you can ask questions in
are not upstream support channels and you may not receive a response to
questions, but someone in the community may be able to point you in the right
direction.
There is also a community IRC channel in `#phabricator` on FreeNode.

View file

@ -48,7 +48,7 @@ Once the import completes, disable the **Observe** URI to automatically convert
it into a hosted repository.
**Push to Empty Repository**: Create an activate an empty repository, then push
all of your changes to empty the repository.
all of your changes to the empty repository.
In Git and Mercurial, you can do this with `git push` or `hg push`.

View file

@ -42,10 +42,3 @@ Then set your "Editor Link" to:
lang=uri
txmt://open/?url=file:///Users/alincoln/editor_links/%r/%f&line=%l
== Configuring: Other Editors ==
General instructions for configuring some other editors and environments can be
found here:
http://wiki.nette.org/en/howto-editor-link

View file

@ -10,18 +10,9 @@ final class PhabricatorDaemonOverseerModule
extends PhutilDaemonOverseerModule {
private $configVersion;
private $timestamp;
public function __construct() {
$this->timestamp = PhabricatorTime::getNow();
}
public function shouldReloadDaemons() {
$now = PhabricatorTime::getNow();
$ago = ($now - $this->timestamp);
// Don't check more than once every 10 seconds.
if ($ago < 10) {
if ($this->shouldThrottle('reload', 10)) {
return false;
}
@ -47,25 +38,23 @@ final class PhabricatorDaemonOverseerModule
}
/**
* Update the configuration version and timestamp.
* Check and update the configuration version.
*
* @return bool True if the daemons should restart, otherwise false.
*/
private function updateConfigVersion() {
$config_version = $this->loadConfigVersion();
$this->timestamp = PhabricatorTime::getNow();
$old_version = $this->configVersion;
$new_version = $this->loadConfigVersion();
if (!$this->configVersion) {
$this->configVersion = $config_version;
$this->configVersion = $new_version;
// Don't trigger a reload if we're loading the config for the very
// first time.
if ($old_version === null) {
return false;
}
if ($this->configVersion != $config_version) {
$this->configVersion = $config_version;
return true;
}
return false;
return ($old_version != $new_version);
}
}

View file

@ -43,6 +43,11 @@ final class PhabricatorTaskmasterDaemon extends PhabricatorDaemon {
$sleep = 0;
} else {
if ($this->shouldHibernate(60)) {
break;
}
// When there's no work, sleep for one second. The pool will
// autoscale down if we're continuously idle for an extended period
// of time.

View file

@ -0,0 +1,33 @@
<?php
final class PhabricatorTaskmasterDaemonModule
extends PhutilDaemonOverseerModule {
public function shouldWakePool(PhutilDaemonPool $pool) {
$class = $pool->getPoolDaemonClass();
if ($class != 'PhabricatorTaskmasterDaemon') {
return false;
}
if ($this->shouldThrottle($class, 1)) {
return false;
}
$table = new PhabricatorWorkerActiveTask();
$conn = $table->establishConnection('r');
$row = queryfx_one(
$conn,
'SELECT id FROM %T WHERE leaseOwner IS NULL
OR leaseExpires <= %d LIMIT 1',
$table->getTableName(),
PhabricatorTime::getNow());
if (!$row) {
return false;
}
return true;
}
}

View file

@ -1608,6 +1608,8 @@ final class PhabricatorUSEnglishTranslation
),
),
'%s accepted this revision as %s reviewer(s): %s.' =>
'%s accepted this revision as: %3$s.',
);
}

View file

@ -88,9 +88,10 @@ final class PhabricatorHash extends Phobject {
}
$hash = sha1($string, $raw_output = true);
$value = head(unpack('L', $hash));
// Make sure this ends up positive, even on 32-bit machines.
$value = head(unpack('L', $hash)) & 0x7FFFFFFF;
return $min + ($value % ($max - $min));
return $min + ($value % (1 + $max - $min));
}

View file

@ -216,7 +216,6 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView
require_celerity_resource('phabricator-standard-page-view');
require_celerity_resource('conpherence-durable-column-view');
require_celerity_resource('font-lato');
require_celerity_resource('font-aleo');
Javelin::initBehavior('workflow', array());

View file

@ -9,7 +9,6 @@
.conpherence-header-pane .phui-header-header {
font-size: 16px;
font-family: 'Aleo', {$fontfamily};
color: #000;
}

View file

@ -139,7 +139,6 @@
.phame-next-post-view {
margin: 0 auto;
padding: 12px 0;
font-family: 'Aleo', {$fontfamily};
}
.phame-next {
@ -294,7 +293,6 @@
color: #000;
font-size: 28px;
font-weight: bold;
font-family: 'Aleo', {$fontfamily};
padding-top: 24px;
}
@ -305,7 +303,6 @@
.phame-mega-header .phame-header-subtitle {
color: {$greytext};
font-size: 20px;
font-family: 'Aleo', {$fontfamily};
padding-top: 8px;
}

View file

@ -28,7 +28,6 @@
.project-card-view .phui-header-shell .phui-header-header {
font-size: 18px;
font-family: 'Aleo', {$fontfamily};
width: 290px;
overflow: hidden;
white-space: nowrap;
@ -71,7 +70,6 @@
.project-card-header .project-card-name {
font-size: 20px;
font-family: 'Aleo', {$fontfamily};
font-weight: bold;
color: #000;
margin-bottom: 2px;

View file

@ -1,40 +0,0 @@
/**
* @provides font-aleo
* @requires phui-fontkit-css
*/
@font-face {
font-family: 'Aleo';
font-weight: bold;
font-style: normal;
src: url(/rsrc/externals/font/aleo/aleo-bold.eot);
src: url(/rsrc/externals/font/aleo/aleo-bold.eot?#iefix)
format('embedded-opentype'),
url(/rsrc/externals/font/aleo/aleo-bold.woff2)
format('woff2'),
url(/rsrc/externals/font/aleo/aleo-bold.woff)
format('woff'),
url(/rsrc/externals/font/aleo/aleo-bold.ttf)
format('truetype'),
url(/rsrc/externals/font/aleo/aleo-bold.svg#aleo-bold)
format('svg');
}
@font-face {
font-family: 'Aleo';
font-weight: normal;
font-style: normal;
src: url(/rsrc/externals/font/aleo/aleo-regular.eot);
src: url(/rsrc/externals/font/aleo/aleo-regular.eot?#iefix)
format('embedded-opentype'),
url(/rsrc/externals/font/aleo/aleo-regular.woff2)
format('woff2'),
url(/rsrc/externals/font/aleo/aleo-regular.woff)
format('woff'),
url(/rsrc/externals/font/aleo/aleo-regular.ttf)
format('truetype'),
url(/rsrc/externals/font/aleo/aleo-regular.svg#aleo-regular)
format('svg');
}

View file

@ -2,30 +2,10 @@
* @provides phui-fontkit-css
*/
/* - Roboto Slab ---------------------------------------------------------------
Used as Primary Headers in Object Boxes, Headers in Documents
*/
.diviner-document-section .phui-header-header {
font-family: 'Aleo', {$fontfamily};
color: #000;
}
.phui-document-view .phui-header-tall .phui-header-header {
font-family: 'Aleo', {$fontfamily};
}
.phui-document-view .phabricator-remarkup h1.remarkup-header,
.phui-document-view .phabricator-remarkup h2.remarkup-header,
.phui-document-view .phabricator-remarkup h3.remarkup-header,
.phui-document-view .phabricator-remarkup h4.remarkup-header,
.phui-document-view .phabricator-remarkup h5.remarkup-header,
.phui-document-view .phabricator-remarkup h6.remarkup-header {
font-family: 'Aleo', {$fontfamily};
}
.phui-document-view .phabricator-remarkup .remarkup-header {
margin-bottom: 8px;
}

View file

@ -548,3 +548,16 @@ properly, and submit values. */
padding: 4px;
color: {$bluetext};
}
.phuix-form-checkbox-action {
padding: 4px;
color: {$bluetext};
}
.phuix-form-checkbox-action input[type=checkbox] {
margin: 4px 0;
}
.phuix-form-checkbox-label {
margin-left: 4px;
}

View file

@ -345,7 +345,6 @@ body .phui-header-shell.phui-bleed-header
}
.phui-profile-header.phui-header-shell .phui-header-header {
font-family: 'Aleo', {$fontfamily};
font-size: 24px;
color: #000;
}

View file

@ -18,7 +18,6 @@
.phui-two-column-header .phui-header-header {
font-size: 20px;
font-family: 'Aleo', {$fontfamily};
color: #000;
}

View file

@ -1,203 +0,0 @@
Font data copyright Google 2013
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Binary file not shown.

File diff suppressed because it is too large Load diff

Before

Width:  |  Height:  |  Size: 310 KiB

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show more