From 8a85ee7c1575945a0c649a255e28b951173465c9 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Fri, 6 Jan 2017 20:12:57 -0800 Subject: [PATCH 01/51] Add CustomPHID to PhabricatorProfileMenuEngineConfiguration Summary: Ref T5867, adds a customPHID field, nullable, and lets you query by it... i think? Not fully able to grok all the EditEngine stuff, but I think this is the right place for the query. Test Plan: Not wired to anything, but pulling up project menu, editing, all still works. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T5867 Differential Revision: https://secure.phabricator.com/D17149 --- .../20170106.menu.01.customphd.sql | 2 ++ .../PhabricatorProfileMenuEditEngine.php | 10 +++++++ .../engine/PhabricatorProfileMenuEngine.php | 26 ++++++++++++++++--- ...catorProfileMenuItemConfigurationQuery.php | 13 ++++++++++ ...habricatorProfileMenuItemConfiguration.php | 2 ++ 5 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 resources/sql/autopatches/20170106.menu.01.customphd.sql diff --git a/resources/sql/autopatches/20170106.menu.01.customphd.sql b/resources/sql/autopatches/20170106.menu.01.customphd.sql new file mode 100644 index 0000000000..2fb642ca8b --- /dev/null +++ b/resources/sql/autopatches/20170106.menu.01.customphd.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_search.search_profilepanelconfiguration + ADD customPHID VARBINARY(64); diff --git a/src/applications/search/editor/PhabricatorProfileMenuEditEngine.php b/src/applications/search/editor/PhabricatorProfileMenuEditEngine.php index f7b8f76ec7..70ba141967 100644 --- a/src/applications/search/editor/PhabricatorProfileMenuEditEngine.php +++ b/src/applications/search/editor/PhabricatorProfileMenuEditEngine.php @@ -7,6 +7,7 @@ final class PhabricatorProfileMenuEditEngine private $menuEngine; private $profileObject; + private $customPHID; private $newMenuItemConfiguration; private $isBuiltin; @@ -32,6 +33,15 @@ final class PhabricatorProfileMenuEditEngine return $this->profileObject; } + public function setCustomPHID($custom_phid) { + $this->customPHID = $custom_phid; + return $this; + } + + public function getCustomPHID() { + return $this->customPHID; + } + public function setNewMenuItemConfiguration( PhabricatorProfileMenuItemConfiguration $configuration) { $this->newMenuItemConfiguration = $configuration; diff --git a/src/applications/search/engine/PhabricatorProfileMenuEngine.php b/src/applications/search/engine/PhabricatorProfileMenuEngine.php index d930b31314..a3cc8e34d1 100644 --- a/src/applications/search/engine/PhabricatorProfileMenuEngine.php +++ b/src/applications/search/engine/PhabricatorProfileMenuEngine.php @@ -4,6 +4,7 @@ abstract class PhabricatorProfileMenuEngine extends Phobject { private $viewer; private $profileObject; + private $customPHID; private $items; private $defaultItem; private $controller; @@ -27,6 +28,15 @@ abstract class PhabricatorProfileMenuEngine extends Phobject { return $this->profileObject; } + public function setCustomPHID($custom_phid) { + $this->customPHID = $custom_phid; + return $this; + } + + public function getCustomPHID() { + return $this->customPHID; + } + public function setController(PhabricatorController $controller) { $this->controller = $controller; return $this; @@ -244,10 +254,18 @@ abstract class PhabricatorProfileMenuEngine extends Phobject { $items = $this->loadBuiltinProfileItems(); - $stored_items = id(new PhabricatorProfileMenuItemConfigurationQuery()) - ->setViewer($viewer) - ->withProfilePHIDs(array($object->getPHID())) - ->execute(); + if ($this->getCustomPHID()) { + $stored_items = id(new PhabricatorProfileMenuItemConfigurationQuery()) + ->setViewer($viewer) + ->withProfilePHIDs(array($object->getPHID())) + ->withCustomPHIDs(array($this->getCustomPHID())) + ->execute(); + } else { + $stored_items = id(new PhabricatorProfileMenuItemConfigurationQuery()) + ->setViewer($viewer) + ->withProfilePHIDs(array($object->getPHID())) + ->execute(); + } foreach ($stored_items as $stored_item) { $impl = $stored_item->getMenuItem(); diff --git a/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php b/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php index f8172638c3..f59e20460e 100644 --- a/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php +++ b/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php @@ -6,6 +6,7 @@ final class PhabricatorProfileMenuItemConfigurationQuery private $ids; private $phids; private $profilePHIDs; + private $customPHIDs; public function withIDs(array $ids) { $this->ids = $ids; @@ -22,6 +23,11 @@ final class PhabricatorProfileMenuItemConfigurationQuery return $this; } + public function withCustomPHIDs(array $phids) { + $this->customPHIDs = $phids; + return $this; + } + public function newResultObject() { return new PhabricatorProfileMenuItemConfiguration(); } @@ -54,6 +60,13 @@ final class PhabricatorProfileMenuItemConfigurationQuery $this->profilePHIDs); } + if ($this->customPHIDs !== null) { + $where[] = qsprintf( + $conn, + 'customPHID IN (%Ls)', + $this->customPHIDs); + } + return $where; } diff --git a/src/applications/search/storage/PhabricatorProfileMenuItemConfiguration.php b/src/applications/search/storage/PhabricatorProfileMenuItemConfiguration.php index 3a9c0effc6..9509155e1b 100644 --- a/src/applications/search/storage/PhabricatorProfileMenuItemConfiguration.php +++ b/src/applications/search/storage/PhabricatorProfileMenuItemConfiguration.php @@ -12,6 +12,7 @@ final class PhabricatorProfileMenuItemConfiguration protected $builtinKey; protected $menuItemOrder; protected $visibility; + protected $customPHID; protected $menuItemProperties = array(); private $profileObject = self::ATTACHABLE; @@ -52,6 +53,7 @@ final class PhabricatorProfileMenuItemConfiguration 'menuItemKey' => 'text64', 'builtinKey' => 'text64?', 'menuItemOrder' => 'uint32?', + 'customPHID' => 'phid?', 'visibility' => 'text32', ), self::CONFIG_KEY_SCHEMA => array( From d4248d231bedce85f752115fe553a11b6c05dc86 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sun, 8 Jan 2017 07:52:34 -0800 Subject: [PATCH 02/51] Correct "Manage Password" link in Quickling in Diffusion Summary: Fixes T12080. This was missing a "/", but stop hard-coding these URIs. Test Plan: Clicked both links with Quickling as a logged-in and logged-out user, ended up in the right place. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12080 Differential Revision: https://secure.phabricator.com/D17151 --- .../diffusion/view/DiffusionCloneURIView.php | 12 ++++++++++-- .../controller/PhabricatorSettingsMainController.php | 2 +- .../settings/panel/PhabricatorSettingsPanel.php | 11 +++++++++-- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/applications/diffusion/view/DiffusionCloneURIView.php b/src/applications/diffusion/view/DiffusionCloneURIView.php index fc861ed183..c9dae57563 100644 --- a/src/applications/diffusion/view/DiffusionCloneURIView.php +++ b/src/applications/diffusion/view/DiffusionCloneURIView.php @@ -35,6 +35,8 @@ final class DiffusionCloneURIView extends AphrontView { } public function render() { + $viewer = $this->getViewer(); + require_celerity_resource('diffusion-icons-css'); Javelin::initBehavior('select-content'); @@ -87,12 +89,18 @@ final class DiffusionCloneURIView extends AphrontView { case PhabricatorRepositoryURI::IO_READWRITE: switch ($uri->getBuiltinProtocol()) { case PhabricatorRepositoryURI::BUILTIN_PROTOCOL_SSH: - $auth_uri = '/settings/panel/ssh/'; + $auth_uri = id(new PhabricatorSSHKeysSettingsPanel()) + ->setViewer($viewer) + ->setUser($viewer) + ->getPanelURI(); $auth_tip = pht('Manage SSH Keys'); $auth_disabled = false; break; default: - $auth_uri = '/settings/panel/vcspassword'; + $auth_uri = id(new DiffusionSetPasswordSettingsPanel()) + ->setViewer($viewer) + ->setUser($viewer) + ->getPanelURI(); $auth_tip = pht('Manage Password'); $auth_disabled = false; break; diff --git a/src/applications/settings/controller/PhabricatorSettingsMainController.php b/src/applications/settings/controller/PhabricatorSettingsMainController.php index fada4a0937..841529a051 100644 --- a/src/applications/settings/controller/PhabricatorSettingsMainController.php +++ b/src/applications/settings/controller/PhabricatorSettingsMainController.php @@ -32,7 +32,7 @@ final class PhabricatorSettingsMainController // Redirect "/panel/XYZ/" to the viewer's personal settings panel. This // was the primary URI before global settings were introduced and allows - // generation of viewer-agnostic URIs for email. + // generation of viewer-agnostic URIs for email and logged-out users. $panel = $request->getURIData('panel'); if ($panel) { $panel = phutil_escape_uri($panel); diff --git a/src/applications/settings/panel/PhabricatorSettingsPanel.php b/src/applications/settings/panel/PhabricatorSettingsPanel.php index 7d86eaf243..eea48e540f 100644 --- a/src/applications/settings/panel/PhabricatorSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorSettingsPanel.php @@ -229,8 +229,15 @@ abstract class PhabricatorSettingsPanel extends Phobject { $user = $this->getUser(); if ($user) { - $username = $user->getUsername(); - return "/settings/user/{$username}/page/{$key}/{$path}"; + if ($user->isLoggedIn()) { + $username = $user->getUsername(); + return "/settings/user/{$username}/page/{$key}/{$path}"; + } else { + // For logged-out users, we can't put their username in the URI. This + // page will prompt them to login, then redirect them to the correct + // location. + return "/settings/panel/{$key}/"; + } } else { $builtin = $this->getPreferences()->getBuiltinKey(); return "/settings/builtin/{$builtin}/page/{$key}/{$path}"; From f16778fc18b7ca096093c6f3108b7ea31832fd63 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sun, 8 Jan 2017 10:53:35 -0800 Subject: [PATCH 03/51] Fix excessively strict "Can Use Application" policy filtering Summary: Ref T9058. The stricter filtering is over-filtering Handles. For example, in the Phacility cluster, users can not see Almanac services. So this filtering happens: - The AlmanacServiceQuery filters the service beacuse they can't see the application. - The HandleQuery generates a "you can't see this" handle. - But then the HandleQuery filters that handle! It has a "service" PHID and the user can't see Almanac. This violates the assumption that all application code makes about handles: it's OK to query handles for objects you can't see, and you'll get something back. Instead, don't do application filtering on handles. Test Plan: - Added a failing test and made it pass. - As a user who can not see Almanac, viewed an Instances timeline. - Before patch: fatal on trying to load a handle for a Service. - After patch: smooth sailing. Reviewers: chad Maniphest Tasks: T9058 Differential Revision: https://secure.phabricator.com/D17152 --- .../policy/filter/PhabricatorPolicyFilter.php | 13 ++++++++++++ .../PhabricatorProjectCoreTestCase.php | 20 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/applications/policy/filter/PhabricatorPolicyFilter.php b/src/applications/policy/filter/PhabricatorPolicyFilter.php index 855149f6b3..e0c223cd2c 100644 --- a/src/applications/policy/filter/PhabricatorPolicyFilter.php +++ b/src/applications/policy/filter/PhabricatorPolicyFilter.php @@ -874,6 +874,19 @@ final class PhabricatorPolicyFilter extends Phobject { $viewer = $this->viewer; foreach ($objects as $key => $object) { + // Don't filter handles: users are allowed to see handles from an + // application they can't see even if they can not see objects from + // that application. Note that the application policies still apply to + // the underlying object, so these will be "Restricted Object" handles. + + // If we don't let these through, PhabricatorHandleQuery will completely + // fail to load results for PHIDs that are part of applications which + // the viewer can not see, but a fundamental property of handles is that + // we always load something and they can safely be assumed to load. + if ($object instanceof PhabricatorObjectHandle) { + continue; + } + $phid = $object->getPHID(); if (!$phid) { continue; diff --git a/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php b/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php index cb977dd681..71161b031e 100644 --- a/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php +++ b/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php @@ -51,6 +51,13 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase { $proj, PhabricatorPolicyCapability::CAN_VIEW)); + // This object is visible so its handle should load normally. + $handle = id(new PhabricatorHandleQuery()) + ->setViewer($user) + ->withPHIDs(array($proj->getPHID())) + ->executeOne(); + $this->assertEqual($proj->getPHID(), $handle->getPHID()); + // Change the "Can Use Application" policy for Projecs to "No One". This // should cause filtering checks to fail even when they are executed // directly rather than via a Query. @@ -76,6 +83,19 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase { $proj, PhabricatorPolicyCapability::CAN_VIEW)); + // We should still be able to load a handle for the project, even if we + // can not see the application. + $handle = id(new PhabricatorHandleQuery()) + ->setViewer($user) + ->withPHIDs(array($proj->getPHID())) + ->executeOne(); + + // The handle should load... + $this->assertEqual($proj->getPHID(), $handle->getPHID()); + + // ...but be policy filtered. + $this->assertTrue($handle->getPolicyFiltered()); + unset($env); } From e03103f3492037562476104294d568b3c1d78928 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sun, 8 Jan 2017 11:29:23 -0800 Subject: [PATCH 04/51] Return milestone information in project.search Summary: Ref T12074. - `project.search` now returns milestones by default. - A new constraint, `isMilestone`, allows filtering to milestones, non-milestones, or both (API and web UI). - `project.search` now returns a milestone number for milestones, or `null` for non-milestones. NOTE: Existing custom saved queries in projects which previously did not return milestones now will. I expect this to have little-to-no impact on users, and these queries are easy to correct, but I'll note this in changelogs. Test Plan: - Ran various queries with `project.search` and in the web UI, searching for milestones, non-milestones, and both. - Web UI default behavior (no milestones) is unchanged, but you can now get milestones if you want them. - Queried a milestone by ID/PHID via API. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12074 Differential Revision: https://secure.phabricator.com/D17153 --- .../query/PhabricatorProjectSearchEngine.php | 21 +++++++++++++++++-- .../project/storage/PhabricatorProject.php | 11 ++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/applications/project/query/PhabricatorProjectSearchEngine.php b/src/applications/project/query/PhabricatorProjectSearchEngine.php index cfb1868a61..5df3bbeb1a 100644 --- a/src/applications/project/query/PhabricatorProjectSearchEngine.php +++ b/src/applications/project/query/PhabricatorProjectSearchEngine.php @@ -13,8 +13,7 @@ final class PhabricatorProjectSearchEngine public function newQuery() { return id(new PhabricatorProjectQuery()) - ->needImages(true) - ->withIsMilestone(false); + ->needImages(true); } protected function buildCustomSearchFields() { @@ -34,6 +33,17 @@ final class PhabricatorProjectSearchEngine ->setLabel(pht('Status')) ->setKey('status') ->setOptions($this->getStatusOptions()), + id(new PhabricatorSearchThreeStateField()) + ->setLabel(pht('Milestones')) + ->setKey('isMilestone') + ->setOptions( + pht('(Show All)'), + pht('Show Only Milestones'), + pht('Hide Milestones')) + ->setDescription( + pht( + 'Pass true to find only milestones, or false to omit '. + 'milestones.')), id(new PhabricatorSearchCheckboxesField()) ->setLabel(pht('Icons')) ->setKey('icons') @@ -77,6 +87,10 @@ final class PhabricatorProjectSearchEngine $query->withColors($map['colors']); } + if ($map['isMilestone'] !== null) { + $query->withIsMilestone($map['isMilestone']); + } + return $query; } @@ -103,6 +117,9 @@ final class PhabricatorProjectSearchEngine $viewer_phid = $this->requireViewer()->getPHID(); + // By default, do not show milestones in the list view. + $query->setParameter('isMilestone', false); + switch ($query_key) { case 'all': return $query; diff --git a/src/applications/project/storage/PhabricatorProject.php b/src/applications/project/storage/PhabricatorProject.php index 47e251f3af..c8b5e2e45a 100644 --- a/src/applications/project/storage/PhabricatorProject.php +++ b/src/applications/project/storage/PhabricatorProject.php @@ -741,6 +741,10 @@ final class PhabricatorProject extends PhabricatorProjectDAO ->setKey('slug') ->setType('string') ->setDescription(pht('Primary slug/hashtag.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('milestone') + ->setType('int?') + ->setDescription(pht('For milestones, milestone sequence number.')), id(new PhabricatorConduitSearchFieldSpecification()) ->setKey('icon') ->setType('map') @@ -756,9 +760,16 @@ final class PhabricatorProject extends PhabricatorProjectDAO $color_key = $this->getColor(); $color_name = PhabricatorProjectIconSet::getColorName($color_key); + if ($this->isMilestone()) { + $milestone = (int)$this->getMilestoneNumber(); + } else { + $milestone = null; + } + return array( 'name' => $this->getName(), 'slug' => $this->getPrimarySlug(), + 'milestone' => $milestone, 'icon' => array( 'key' => $this->getDisplayIconKey(), 'name' => $this->getDisplayIconName(), From c0bec6c0ed8137c921e1d34c3ade74d9303f276e Mon Sep 17 00:00:00 2001 From: epriestley Date: Sun, 8 Jan 2017 11:39:57 -0800 Subject: [PATCH 05/51] Add "parent" and "ancestor" information to the project.search API Summary: Ref T12074. - Adds a new "parent" property on main results. This shows an abbreviated version of the project's parent, or `null` if the project is a root project. - Adds a new "ancestor" attachment to pull the entire ancestor list. - Adds a new "depth" property on main results. - You can use "parent" or "depth" to tell if a project is a subproject or not. These attempt to balance convenience, power, and performance: the full ancestor list can be big so I made it an attachment, but the other stuff isn't too big and is cheap and seems reasonable to always include. Test Plan: In API results: - Saw null parent (root projects) and non-null parent (subprojects/milestones). - Used "ancestors" attchment, got full list of ancestors. - Saw appropriate "depth" values. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12074 Differential Revision: https://secure.phabricator.com/D17154 --- src/__phutil_library_map__.php | 2 + ...rojectsAncestorsSearchEngineAttachment.php | 30 +++++++++++++++ .../project/storage/PhabricatorProject.php | 37 +++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 src/applications/project/engineextension/PhabricatorProjectsAncestorsSearchEngineAttachment.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 43ab0407e6..32ac036ef9 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3514,6 +3514,7 @@ phutil_register_library_map(array( 'PhabricatorProjectWatcherListView' => 'applications/project/view/PhabricatorProjectWatcherListView.php', 'PhabricatorProjectWorkboardBackgroundColor' => 'applications/project/constants/PhabricatorProjectWorkboardBackgroundColor.php', 'PhabricatorProjectWorkboardProfileMenuItem' => 'applications/project/menuitem/PhabricatorProjectWorkboardProfileMenuItem.php', + 'PhabricatorProjectsAncestorsSearchEngineAttachment' => 'applications/project/engineextension/PhabricatorProjectsAncestorsSearchEngineAttachment.php', 'PhabricatorProjectsCurtainExtension' => 'applications/project/engineextension/PhabricatorProjectsCurtainExtension.php', 'PhabricatorProjectsEditEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsEditEngineExtension.php', 'PhabricatorProjectsEditField' => 'applications/transactions/editfield/PhabricatorProjectsEditField.php', @@ -8666,6 +8667,7 @@ phutil_register_library_map(array( 'PhabricatorProjectWatcherListView' => 'PhabricatorProjectUserListView', 'PhabricatorProjectWorkboardBackgroundColor' => 'Phobject', 'PhabricatorProjectWorkboardProfileMenuItem' => 'PhabricatorProfileMenuItem', + 'PhabricatorProjectsAncestorsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment', 'PhabricatorProjectsCurtainExtension' => 'PHUICurtainExtension', 'PhabricatorProjectsEditEngineExtension' => 'PhabricatorEditEngineExtension', 'PhabricatorProjectsEditField' => 'PhabricatorTokenizerEditField', diff --git a/src/applications/project/engineextension/PhabricatorProjectsAncestorsSearchEngineAttachment.php b/src/applications/project/engineextension/PhabricatorProjectsAncestorsSearchEngineAttachment.php new file mode 100644 index 0000000000..3113068664 --- /dev/null +++ b/src/applications/project/engineextension/PhabricatorProjectsAncestorsSearchEngineAttachment.php @@ -0,0 +1,30 @@ +getAncestorProjects(); + + // Order ancestors by depth, ascending. + $ancestors = array_reverse($ancestors); + + $results = array(); + foreach ($ancestors as $ancestor) { + $results[] = $ancestor->getRefForConduit(); + } + + return array( + 'ancestors' => $results, + ); + } + +} diff --git a/src/applications/project/storage/PhabricatorProject.php b/src/applications/project/storage/PhabricatorProject.php index c8b5e2e45a..a46ebd3352 100644 --- a/src/applications/project/storage/PhabricatorProject.php +++ b/src/applications/project/storage/PhabricatorProject.php @@ -745,6 +745,20 @@ final class PhabricatorProject extends PhabricatorProjectDAO ->setKey('milestone') ->setType('int?') ->setDescription(pht('For milestones, milestone sequence number.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('parent') + ->setType('map?') + ->setDescription( + pht( + 'For subprojects and milestones, a brief description of the '. + 'parent project.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('depth') + ->setType('int') + ->setDescription( + pht( + 'For subprojects and milestones, depth of this project in the '. + 'tree. Root projects have depth 0.')), id(new PhabricatorConduitSearchFieldSpecification()) ->setKey('icon') ->setType('map') @@ -766,10 +780,19 @@ final class PhabricatorProject extends PhabricatorProjectDAO $milestone = null; } + $parent = $this->getParentProject(); + if ($parent) { + $parent_ref = $parent->getRefForConduit(); + } else { + $parent_ref = null; + } + return array( 'name' => $this->getName(), 'slug' => $this->getPrimarySlug(), 'milestone' => $milestone, + 'depth' => (int)$this->getProjectDepth(), + 'parent' => $parent_ref, 'icon' => array( 'key' => $this->getDisplayIconKey(), 'name' => $this->getDisplayIconName(), @@ -788,6 +811,20 @@ final class PhabricatorProject extends PhabricatorProjectDAO ->setAttachmentKey('members'), id(new PhabricatorProjectsWatchersSearchEngineAttachment()) ->setAttachmentKey('watchers'), + id(new PhabricatorProjectsAncestorsSearchEngineAttachment()) + ->setAttachmentKey('ancestors'), + ); + } + + /** + * Get an abbreviated representation of this project for use in providing + * "parent" and "ancestor" information. + */ + public function getRefForConduit() { + return array( + 'id' => (int)$this->getID(), + 'phid' => $this->getPHID(), + 'name' => $this->getName(), ); } From 9fa7355edc005b97b76dd1de58e3d8e7bd9b5f27 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sun, 8 Jan 2017 11:51:17 -0800 Subject: [PATCH 06/51] Support "parentPHIDs" and "ancestorPHIDs" as constraints in project.search API Summary: Ref T12074. Allows querying for project by direct parent (find only immediate children) or any ancestor (find all descendants) using the API. There's no proper web UI for this since I'm not sure how useful it is, but you can `/project/?parent=PHID-PROJ-...` or `/project/?ancestor=...` for now. We can add UI later if/when use cases arise, but it's not immediately clear to me that this is useful to do from the web. Test Plan: - From API, queried with `parentPHIDs` and `ancestorPHIDs`, finding direct children only and all descendants, respectively. - From web UI, fiddled with `?parent=...` and `?ancestor=...` to make sure they work too. This isn't intended to be a user-facing feature. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12074 Differential Revision: https://secure.phabricator.com/D17155 --- .../query/PhabricatorProjectSearchEngine.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/applications/project/query/PhabricatorProjectSearchEngine.php b/src/applications/project/query/PhabricatorProjectSearchEngine.php index 5df3bbeb1a..49a989eb4b 100644 --- a/src/applications/project/query/PhabricatorProjectSearchEngine.php +++ b/src/applications/project/query/PhabricatorProjectSearchEngine.php @@ -52,6 +52,17 @@ final class PhabricatorProjectSearchEngine ->setLabel(pht('Colors')) ->setKey('colors') ->setOptions($this->getColorOptions()), + id(new PhabricatorPHIDsSearchField()) + ->setLabel(pht('Parent Projects')) + ->setKey('parentPHIDs') + ->setAliases(array('parent', 'parents', 'parentPHID')) + ->setDescription(pht('Find direct subprojects of specified parents.')), + id(new PhabricatorPHIDsSearchField()) + ->setLabel(pht('Ancestor Projects')) + ->setKey('ancestorPHIDs') + ->setAliases(array('ancestor', 'ancestors', 'ancestorPHID')) + ->setDescription( + pht('Find all subprojects beneath specified ancestors.')), ); } @@ -91,6 +102,14 @@ final class PhabricatorProjectSearchEngine $query->withIsMilestone($map['isMilestone']); } + if ($map['parentPHIDs']) { + $query->withParentProjectPHIDs($map['parentPHIDs']); + } + + if ($map['ancestorPHIDs']) { + $query->withAncestorProjectPHIDs($map['ancestorPHIDs']); + } + return $query; } From ad3745c80181300db936c9cef2cb614fef35474c Mon Sep 17 00:00:00 2001 From: epriestley Date: Sun, 8 Jan 2017 12:12:12 -0800 Subject: [PATCH 07/51] Add a "columns" attachment to the maniphest.search API method Summary: Ref T12074. This allows callers to identify which columns an object appears in (currently, always tasks). There are a few major cases: - Object is in a normal column: we return column information. - Object is in a proxy column (subproject or milestone). For example, when you look at the board for "Some Parent Project", the task might show up in a milestone column. I've chosen to not return anything in this case: you can figure out that the task is there by looking at the project structure, and this is kind of an internal artifact of the implementation and probably not useful to callers. - Project does not have a workboard: we return nothing. These seem fairly reasonable, I think? Test Plan: - Queried for tasks, using the "columns" attachment. - Dragged a task across a board, querying it repeatedly. Got expected results for normal column (the column), subprojects with no board (nothing), milestones with no board (nothing) and mielstones/subprojects with a board (the column on //that// board, only, not the proxy column on the parent). Reviewers: chad Reviewed By: chad Maniphest Tasks: T12074 Differential Revision: https://secure.phabricator.com/D17156 --- src/__phutil_library_map__.php | 2 + .../maniphest/storage/ManiphestTask.php | 5 +- ...atorBoardColumnsSearchEngineAttachment.php | 80 +++++++++++++++++++ .../storage/PhabricatorProjectColumn.php | 8 ++ 4 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 src/applications/project/engineextension/PhabricatorBoardColumnsSearchEngineAttachment.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 32ac036ef9..72bbae9a78 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2025,6 +2025,7 @@ phutil_register_library_map(array( 'PhabricatorBcryptPasswordHasher' => 'infrastructure/util/password/PhabricatorBcryptPasswordHasher.php', 'PhabricatorBinariesSetupCheck' => 'applications/config/check/PhabricatorBinariesSetupCheck.php', 'PhabricatorBitbucketAuthProvider' => 'applications/auth/provider/PhabricatorBitbucketAuthProvider.php', + 'PhabricatorBoardColumnsSearchEngineAttachment' => 'applications/project/engineextension/PhabricatorBoardColumnsSearchEngineAttachment.php', 'PhabricatorBoardLayoutEngine' => 'applications/project/engine/PhabricatorBoardLayoutEngine.php', 'PhabricatorBoardRenderingEngine' => 'applications/project/engine/PhabricatorBoardRenderingEngine.php', 'PhabricatorBoardResponseEngine' => 'applications/project/engine/PhabricatorBoardResponseEngine.php', @@ -6926,6 +6927,7 @@ phutil_register_library_map(array( 'PhabricatorBcryptPasswordHasher' => 'PhabricatorPasswordHasher', 'PhabricatorBinariesSetupCheck' => 'PhabricatorSetupCheck', 'PhabricatorBitbucketAuthProvider' => 'PhabricatorOAuth1AuthProvider', + 'PhabricatorBoardColumnsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment', 'PhabricatorBoardLayoutEngine' => 'Phobject', 'PhabricatorBoardRenderingEngine' => 'Phobject', 'PhabricatorBoardResponseEngine' => 'Phobject', diff --git a/src/applications/maniphest/storage/ManiphestTask.php b/src/applications/maniphest/storage/ManiphestTask.php index 4fb9fc2861..76722f6719 100644 --- a/src/applications/maniphest/storage/ManiphestTask.php +++ b/src/applications/maniphest/storage/ManiphestTask.php @@ -505,7 +505,10 @@ final class ManiphestTask extends ManiphestDAO } public function getConduitSearchAttachments() { - return array(); + return array( + id(new PhabricatorBoardColumnsSearchEngineAttachment()) + ->setAttachmentKey('columns'), + ); } diff --git a/src/applications/project/engineextension/PhabricatorBoardColumnsSearchEngineAttachment.php b/src/applications/project/engineextension/PhabricatorBoardColumnsSearchEngineAttachment.php new file mode 100644 index 0000000000..0a54c23b4a --- /dev/null +++ b/src/applications/project/engineextension/PhabricatorBoardColumnsSearchEngineAttachment.php @@ -0,0 +1,80 @@ +getViewer(); + + $objects = mpull($objects, null, 'getPHID'); + $object_phids = array_keys($objects); + + $edge_query = id(new PhabricatorEdgeQuery()) + ->withSourcePHIDs($object_phids) + ->withEdgeTypes( + array( + PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, + )); + $edge_query->execute(); + + $project_phids = $edge_query->getDestinationPHIDs(); + + $engine = id(new PhabricatorBoardLayoutEngine()) + ->setViewer($viewer) + ->setBoardPHIDs($project_phids) + ->setObjectPHIDs($object_phids) + ->executeLayout(); + + $results = array(); + foreach ($objects as $phid => $object) { + $board_phids = $edge_query->getDestinationPHIDs(array($phid)); + + $boards = array(); + foreach ($board_phids as $board_phid) { + $columns = array(); + foreach ($engine->getObjectColumns($board_phid, $phid) as $column) { + if ($column->getProxyPHID()) { + // When an object is in a proxy column, don't return it on this + // attachment. This information can be reconstructed from other + // queries, is something of an implementation detail, and seems + // unlikely to be interesting to API consumers. + continue 2; + } + + $columns[] = $column->getRefForConduit(); + } + + // If a project has no workboard, the object won't appear on any + // columns. Just omit it from the result set. + if (!$columns) { + continue; + } + + $boards[$board_phid] = array( + 'columns' => $columns, + ); + } + + $results[$phid] = $boards; + } + + return $results; + } + + public function getAttachmentForObject($object, $data, $spec) { + $boards = idx($data, $object->getPHID(), array()); + + return array( + 'boards' => $boards, + ); + } + +} diff --git a/src/applications/project/storage/PhabricatorProjectColumn.php b/src/applications/project/storage/PhabricatorProjectColumn.php index 660aafcc1f..7f6417db51 100644 --- a/src/applications/project/storage/PhabricatorProjectColumn.php +++ b/src/applications/project/storage/PhabricatorProjectColumn.php @@ -183,6 +183,14 @@ final class PhabricatorProjectColumn return sprintf('%s%012d', $group, $sequence); } + public function getRefForConduit() { + return array( + 'id' => (int)$this->getID(), + 'phid' => $this->getPHID(), + 'name' => $this->getDisplayName(), + ); + } + /* -( PhabricatorApplicationTransactionInterface )------------------------- */ From 63bfa5ccb5bebb3a9268ec4c2107542fab97fa78 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sun, 8 Jan 2017 12:30:39 -0800 Subject: [PATCH 08/51] Add "project.column.search" for querying workboard column information Summary: Ref T12074. Provide a basic but functional v3 API endpoint for reading workboard column information. There is no equivalent to this in the UI yet, although there may be some day (perhaps adjacent to T5024). Test Plan: - Queried for all columns. - Queried for columns on a particular board using `projectPHIDs`. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12074 Differential Revision: https://secure.phabricator.com/D17157 --- src/__phutil_library_map__.php | 5 ++ .../ProjectColumnSearchConduitAPIMethod.php | 18 +++++ .../PhabricatorProjectColumnSearchEngine.php | 74 +++++++++++++++++++ .../storage/PhabricatorProjectColumn.php | 37 +++++++++- 4 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 src/applications/project/conduit/ProjectColumnSearchConduitAPIMethod.php create mode 100644 src/applications/project/query/PhabricatorProjectColumnSearchEngine.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 72bbae9a78..810f0c6b16 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3427,6 +3427,7 @@ phutil_register_library_map(array( 'PhabricatorProjectColumnPosition' => 'applications/project/storage/PhabricatorProjectColumnPosition.php', 'PhabricatorProjectColumnPositionQuery' => 'applications/project/query/PhabricatorProjectColumnPositionQuery.php', 'PhabricatorProjectColumnQuery' => 'applications/project/query/PhabricatorProjectColumnQuery.php', + 'PhabricatorProjectColumnSearchEngine' => 'applications/project/query/PhabricatorProjectColumnSearchEngine.php', 'PhabricatorProjectColumnTransaction' => 'applications/project/storage/PhabricatorProjectColumnTransaction.php', 'PhabricatorProjectColumnTransactionEditor' => 'applications/project/editor/PhabricatorProjectColumnTransactionEditor.php', 'PhabricatorProjectColumnTransactionQuery' => 'applications/project/query/PhabricatorProjectColumnTransactionQuery.php', @@ -4454,6 +4455,7 @@ phutil_register_library_map(array( 'ProjectAddProjectsEmailCommand' => 'applications/project/command/ProjectAddProjectsEmailCommand.php', 'ProjectBoardTaskCard' => 'applications/project/view/ProjectBoardTaskCard.php', 'ProjectCanLockProjectsCapability' => 'applications/project/capability/ProjectCanLockProjectsCapability.php', + 'ProjectColumnSearchConduitAPIMethod' => 'applications/project/conduit/ProjectColumnSearchConduitAPIMethod.php', 'ProjectConduitAPIMethod' => 'applications/project/conduit/ProjectConduitAPIMethod.php', 'ProjectCreateConduitAPIMethod' => 'applications/project/conduit/ProjectCreateConduitAPIMethod.php', 'ProjectCreateProjectsCapability' => 'applications/project/capability/ProjectCreateProjectsCapability.php', @@ -8565,6 +8567,7 @@ phutil_register_library_map(array( 'PhabricatorPolicyInterface', 'PhabricatorDestructibleInterface', 'PhabricatorExtendedPolicyInterface', + 'PhabricatorConduitResultInterface', ), 'PhabricatorProjectColumnDetailController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectColumnEditController' => 'PhabricatorProjectBoardController', @@ -8576,6 +8579,7 @@ phutil_register_library_map(array( ), 'PhabricatorProjectColumnPositionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorProjectColumnQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', + 'PhabricatorProjectColumnSearchEngine' => 'PhabricatorApplicationSearchEngine', 'PhabricatorProjectColumnTransaction' => 'PhabricatorApplicationTransaction', 'PhabricatorProjectColumnTransactionEditor' => 'PhabricatorApplicationTransactionEditor', 'PhabricatorProjectColumnTransactionQuery' => 'PhabricatorApplicationTransactionQuery', @@ -9849,6 +9853,7 @@ phutil_register_library_map(array( 'ProjectAddProjectsEmailCommand' => 'MetaMTAEmailTransactionCommand', 'ProjectBoardTaskCard' => 'Phobject', 'ProjectCanLockProjectsCapability' => 'PhabricatorPolicyCapability', + 'ProjectColumnSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod', 'ProjectConduitAPIMethod' => 'ConduitAPIMethod', 'ProjectCreateConduitAPIMethod' => 'ProjectConduitAPIMethod', 'ProjectCreateProjectsCapability' => 'PhabricatorPolicyCapability', diff --git a/src/applications/project/conduit/ProjectColumnSearchConduitAPIMethod.php b/src/applications/project/conduit/ProjectColumnSearchConduitAPIMethod.php new file mode 100644 index 0000000000..7cf21922b4 --- /dev/null +++ b/src/applications/project/conduit/ProjectColumnSearchConduitAPIMethod.php @@ -0,0 +1,18 @@ +setLabel(pht('Projects')) + ->setKey('projectPHIDs') + ->setAliases(array('project', 'projects', 'projectPHID')), + ); + } + + + protected function buildQueryFromParameters(array $map) { + $query = $this->newQuery(); + + if ($map['projectPHIDs']) { + $query->withProjectPHIDs($map['projectPHIDs']); + } + + return $query; + } + + protected function getURI($path) { + // NOTE: There's no way to query columns in the web UI, at least for + // the moment. + return null; + } + + protected function getBuiltinQueryNames() { + $names = array(); + + $names['all'] = pht('All'); + + return $names; + } + + public function buildSavedQueryFromBuiltin($query_key) { + $query = $this->newSavedQuery(); + $query->setQueryKey($query_key); + + switch ($query_key) { + case 'all': + return $query; + } + + return parent::buildSavedQueryFromBuiltin($query_key); + } + + protected function renderResultList( + array $projects, + PhabricatorSavedQuery $query, + array $handles) { + assert_instances_of($projects, 'PhabricatorProjectColumn'); + $viewer = $this->requireViewer(); + + return null; + } + +} diff --git a/src/applications/project/storage/PhabricatorProjectColumn.php b/src/applications/project/storage/PhabricatorProjectColumn.php index 7f6417db51..7ddfb4351d 100644 --- a/src/applications/project/storage/PhabricatorProjectColumn.php +++ b/src/applications/project/storage/PhabricatorProjectColumn.php @@ -6,7 +6,8 @@ final class PhabricatorProjectColumn PhabricatorApplicationTransactionInterface, PhabricatorPolicyInterface, PhabricatorDestructibleInterface, - PhabricatorExtendedPolicyInterface { + PhabricatorExtendedPolicyInterface, + PhabricatorConduitResultInterface { const STATUS_ACTIVE = 0; const STATUS_HIDDEN = 1; @@ -183,6 +184,40 @@ final class PhabricatorProjectColumn return sprintf('%s%012d', $group, $sequence); } +/* -( PhabricatorConduitResultInterface )---------------------------------- */ + + public function getFieldSpecificationsForConduit() { + return array( + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('name') + ->setType('string') + ->setDescription(pht('The display name of the column.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('project') + ->setType('map') + ->setDescription(pht('The project the column belongs to.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('proxyPHID') + ->setType('phid?') + ->setDescription( + pht( + 'For columns that proxy another object (like a subproject or '. + 'milestone), the PHID of the object they proxy.')), + ); + } + + public function getFieldValuesForConduit() { + return array( + 'name' => $this->getDisplayName(), + 'proxyPHID' => $this->getProxyPHID(), + 'project' => $this->getProject()->getRefForConduit(), + ); + } + + public function getConduitSearchAttachments() { + return array(); + } + public function getRefForConduit() { return array( 'id' => (int)$this->getID(), From aa6e788f3666c0e4f22d251e0e5e1fa71a4e675d Mon Sep 17 00:00:00 2001 From: epriestley Date: Sun, 8 Jan 2017 12:54:39 -0800 Subject: [PATCH 09/51] Mark "v3" API methods as stable; mark obsoleted methods as "Frozen" Summary: Ref T12074. The "v3" API methods (`*.search`, `*.edit`) are currently marked as "unstable", but they're pretty stable and essentially all new code should be using them. Although these methods are seeing some changes, almost all changes are additive (support for new constraints or attachemnts) and do not break backward compatibility. We have no major, compatibility-breaking changes planned. I don't want to mark the older methods "deprecated" yet since `arc` still uses a lot of them and there are some capabilities not yet available on the v3 methods, but introduce a new "frozen" status with pointers to the new methods. Overall, this should gently push users toward the newer methods. Test Plan: {F2325323} Reviewers: chad Reviewed By: chad Maniphest Tasks: T12074 Differential Revision: https://secure.phabricator.com/D17158 --- .../PhabricatorConduitConsoleController.php | 7 +++++++ src/applications/conduit/method/ConduitAPIMethod.php | 7 ++++--- .../conduit/query/PhabricatorConduitMethodQuery.php | 3 ++- .../conduit/query/PhabricatorConduitSearchEngine.php | 4 ++++ .../conduit/DifferentialCloseConduitAPIMethod.php | 10 ++++++++++ .../DifferentialCreateCommentConduitAPIMethod.php | 10 ++++++++++ .../DifferentialCreateRevisionConduitAPIMethod.php | 10 ++++++++++ .../conduit/DifferentialQueryConduitAPIMethod.php | 10 ++++++++++ .../DifferentialUpdateRevisionConduitAPIMethod.php | 10 ++++++++++ .../conduit/ManiphestCreateTaskConduitAPIMethod.php | 10 ++++++++++ .../conduit/ManiphestInfoConduitAPIMethod.php | 10 ++++++++++ .../conduit/ManiphestQueryConduitAPIMethod.php | 10 ++++++++++ .../conduit/ManiphestUpdateConduitAPIMethod.php | 10 ++++++++++ .../paste/conduit/PasteCreateConduitAPIMethod.php | 10 ++++++++++ .../paste/conduit/PasteQueryConduitAPIMethod.php | 10 ++++++++++ .../people/conduit/UserQueryConduitAPIMethod.php | 10 ++++++++++ .../project/conduit/ProjectCreateConduitAPIMethod.php | 10 ++++++++++ .../project/conduit/ProjectQueryConduitAPIMethod.php | 10 ++++++++++ .../conduit/RepositoryQueryConduitAPIMethod.php | 6 ++++-- .../engine/PhabricatorSearchEngineAPIMethod.php | 11 ----------- .../editengine/PhabricatorEditEngineAPIMethod.php | 11 ----------- 21 files changed, 161 insertions(+), 28 deletions(-) diff --git a/src/applications/conduit/controller/PhabricatorConduitConsoleController.php b/src/applications/conduit/controller/PhabricatorConduitConsoleController.php index b767ead4f0..b696645e55 100644 --- a/src/applications/conduit/controller/PhabricatorConduitConsoleController.php +++ b/src/applications/conduit/controller/PhabricatorConduitConsoleController.php @@ -128,6 +128,13 @@ final class PhabricatorConduitConsoleController $stability_label = pht('Deprecated Method'); $stability_info = nonempty($reason, pht('This method is deprecated.')); break; + case ConduitAPIMethod::METHOD_STATUS_FROZEN: + $stability_icon = 'fa-archive grey'; + $stability_label = pht('Frozen Method'); + $stability_info = nonempty( + $reason, + pht('This method is frozen and will eventually be deprecated.')); + break; default: $stability_label = null; break; diff --git a/src/applications/conduit/method/ConduitAPIMethod.php b/src/applications/conduit/method/ConduitAPIMethod.php index 3a2cc6e30d..05831a782d 100644 --- a/src/applications/conduit/method/ConduitAPIMethod.php +++ b/src/applications/conduit/method/ConduitAPIMethod.php @@ -11,9 +11,10 @@ abstract class ConduitAPIMethod private $viewer; - const METHOD_STATUS_STABLE = 'stable'; - const METHOD_STATUS_UNSTABLE = 'unstable'; - const METHOD_STATUS_DEPRECATED = 'deprecated'; + const METHOD_STATUS_STABLE = 'stable'; + const METHOD_STATUS_UNSTABLE = 'unstable'; + const METHOD_STATUS_DEPRECATED = 'deprecated'; + const METHOD_STATUS_FROZEN = 'frozen'; const SCOPE_NEVER = 'scope.never'; const SCOPE_ALWAYS = 'scope.always'; diff --git a/src/applications/conduit/query/PhabricatorConduitMethodQuery.php b/src/applications/conduit/query/PhabricatorConduitMethodQuery.php index 0409e4ceb3..eb61d8c480 100644 --- a/src/applications/conduit/query/PhabricatorConduitMethodQuery.php +++ b/src/applications/conduit/query/PhabricatorConduitMethodQuery.php @@ -66,7 +66,8 @@ final class PhabricatorConduitMethodQuery } $status = array( - ConduitAPIMethod::METHOD_STATUS_STABLE => $this->isStable, + ConduitAPIMethod::METHOD_STATUS_STABLE => $this->isStable, + ConduitAPIMethod::METHOD_STATUS_FROZEN => $this->isStable, ConduitAPIMethod::METHOD_STATUS_DEPRECATED => $this->isDeprecated, ConduitAPIMethod::METHOD_STATUS_UNSTABLE => $this->isUnstable, ); diff --git a/src/applications/conduit/query/PhabricatorConduitSearchEngine.php b/src/applications/conduit/query/PhabricatorConduitSearchEngine.php index 3c01005722..787c2154d5 100644 --- a/src/applications/conduit/query/PhabricatorConduitSearchEngine.php +++ b/src/applications/conduit/query/PhabricatorConduitSearchEngine.php @@ -166,6 +166,10 @@ final class PhabricatorConduitSearchEngine $item->addIcon('fa-warning', pht('Deprecated')); $item->setStatusIcon('fa-warning red'); break; + case ConduitAPIMethod::METHOD_STATUS_FROZEN: + $item->addIcon('fa-archive', pht('Frozen')); + $item->setStatusIcon('fa-archive grey'); + break; } $list->addItem($item); diff --git a/src/applications/differential/conduit/DifferentialCloseConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialCloseConduitAPIMethod.php index e613647935..d71876961e 100644 --- a/src/applications/differential/conduit/DifferentialCloseConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialCloseConduitAPIMethod.php @@ -11,6 +11,16 @@ final class DifferentialCloseConduitAPIMethod return pht('Close a Differential revision.'); } + public function getMethodStatus() { + return self::METHOD_STATUS_FROZEN; + } + + public function getMethodStatusDescription() { + return pht( + 'This method is frozen and will eventually be deprecated. New code '. + 'should use "differential.revision.edit" instead.'); + } + protected function defineParamTypes() { return array( 'revisionID' => 'required int', diff --git a/src/applications/differential/conduit/DifferentialCreateCommentConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialCreateCommentConduitAPIMethod.php index 8f4b154876..459298c54f 100644 --- a/src/applications/differential/conduit/DifferentialCreateCommentConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialCreateCommentConduitAPIMethod.php @@ -11,6 +11,16 @@ final class DifferentialCreateCommentConduitAPIMethod return pht('Add a comment to a Differential revision.'); } + public function getMethodStatus() { + return self::METHOD_STATUS_FROZEN; + } + + public function getMethodStatusDescription() { + return pht( + 'This method is frozen and will eventually be deprecated. New code '. + 'should use "differential.revision.edit" instead.'); + } + protected function defineParamTypes() { return array( 'revision_id' => 'required revisionid', diff --git a/src/applications/differential/conduit/DifferentialCreateRevisionConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialCreateRevisionConduitAPIMethod.php index 371d57cd9c..532d63680b 100644 --- a/src/applications/differential/conduit/DifferentialCreateRevisionConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialCreateRevisionConduitAPIMethod.php @@ -11,6 +11,16 @@ final class DifferentialCreateRevisionConduitAPIMethod return pht('Create a new Differential revision.'); } + public function getMethodStatus() { + return self::METHOD_STATUS_FROZEN; + } + + public function getMethodStatusDescription() { + return pht( + 'This method is frozen and will eventually be deprecated. New code '. + 'should use "differential.revision.edit" instead.'); + } + protected function defineParamTypes() { return array( // TODO: Arcanist passes this; prevent fatals after D4191 until Conduit diff --git a/src/applications/differential/conduit/DifferentialQueryConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialQueryConduitAPIMethod.php index 1d4bcc2a8d..720361367a 100644 --- a/src/applications/differential/conduit/DifferentialQueryConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialQueryConduitAPIMethod.php @@ -11,6 +11,16 @@ final class DifferentialQueryConduitAPIMethod return pht('Query Differential revisions which match certain criteria.'); } + public function getMethodStatus() { + return self::METHOD_STATUS_FROZEN; + } + + public function getMethodStatusDescription() { + return pht( + 'This method is frozen and will eventually be deprecated. New code '. + 'should use "differential.revision.search" instead.'); + } + protected function defineParamTypes() { $hash_types = ArcanistDifferentialRevisionHash::getTypes(); $hash_const = $this->formatStringConstants($hash_types); diff --git a/src/applications/differential/conduit/DifferentialUpdateRevisionConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialUpdateRevisionConduitAPIMethod.php index 232dbf79b4..d45dc9749e 100644 --- a/src/applications/differential/conduit/DifferentialUpdateRevisionConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialUpdateRevisionConduitAPIMethod.php @@ -11,6 +11,16 @@ final class DifferentialUpdateRevisionConduitAPIMethod return pht('Update a Differential revision.'); } + public function getMethodStatus() { + return self::METHOD_STATUS_FROZEN; + } + + public function getMethodStatusDescription() { + return pht( + 'This method is frozen and will eventually be deprecated. New code '. + 'should use "differential.revision.edit" instead.'); + } + protected function defineParamTypes() { return array( 'id' => 'required revisionid', diff --git a/src/applications/maniphest/conduit/ManiphestCreateTaskConduitAPIMethod.php b/src/applications/maniphest/conduit/ManiphestCreateTaskConduitAPIMethod.php index 3a404774cf..fac96372fe 100644 --- a/src/applications/maniphest/conduit/ManiphestCreateTaskConduitAPIMethod.php +++ b/src/applications/maniphest/conduit/ManiphestCreateTaskConduitAPIMethod.php @@ -11,6 +11,16 @@ final class ManiphestCreateTaskConduitAPIMethod return pht('Create a new Maniphest task.'); } + public function getMethodStatus() { + return self::METHOD_STATUS_FROZEN; + } + + public function getMethodStatusDescription() { + return pht( + 'This method is frozen and will eventually be deprecated. New code '. + 'should use "maniphest.edit" instead.'); + } + protected function defineParamTypes() { return $this->getTaskFields($is_new = true); } diff --git a/src/applications/maniphest/conduit/ManiphestInfoConduitAPIMethod.php b/src/applications/maniphest/conduit/ManiphestInfoConduitAPIMethod.php index 367feff41a..8bd439253f 100644 --- a/src/applications/maniphest/conduit/ManiphestInfoConduitAPIMethod.php +++ b/src/applications/maniphest/conduit/ManiphestInfoConduitAPIMethod.php @@ -10,6 +10,16 @@ final class ManiphestInfoConduitAPIMethod extends ManiphestConduitAPIMethod { return pht('Retrieve information about a Maniphest task, given its ID.'); } + public function getMethodStatus() { + return self::METHOD_STATUS_FROZEN; + } + + public function getMethodStatusDescription() { + return pht( + 'This method is frozen and will eventually be deprecated. New code '. + 'should use "maniphest.search" instead.'); + } + protected function defineParamTypes() { return array( 'task_id' => 'required id', diff --git a/src/applications/maniphest/conduit/ManiphestQueryConduitAPIMethod.php b/src/applications/maniphest/conduit/ManiphestQueryConduitAPIMethod.php index ea8aeb95ed..45373d2587 100644 --- a/src/applications/maniphest/conduit/ManiphestQueryConduitAPIMethod.php +++ b/src/applications/maniphest/conduit/ManiphestQueryConduitAPIMethod.php @@ -10,6 +10,16 @@ final class ManiphestQueryConduitAPIMethod extends ManiphestConduitAPIMethod { return pht('Execute complex searches for Maniphest tasks.'); } + public function getMethodStatus() { + return self::METHOD_STATUS_FROZEN; + } + + public function getMethodStatusDescription() { + return pht( + 'This method is frozen and will eventually be deprecated. New code '. + 'should use "maniphest.search" instead.'); + } + protected function defineParamTypes() { $statuses = array( ManiphestTaskQuery::STATUS_ANY, diff --git a/src/applications/maniphest/conduit/ManiphestUpdateConduitAPIMethod.php b/src/applications/maniphest/conduit/ManiphestUpdateConduitAPIMethod.php index d4adee9570..d8d02becd7 100644 --- a/src/applications/maniphest/conduit/ManiphestUpdateConduitAPIMethod.php +++ b/src/applications/maniphest/conduit/ManiphestUpdateConduitAPIMethod.php @@ -10,6 +10,16 @@ final class ManiphestUpdateConduitAPIMethod extends ManiphestConduitAPIMethod { return pht('Update an existing Maniphest task.'); } + public function getMethodStatus() { + return self::METHOD_STATUS_FROZEN; + } + + public function getMethodStatusDescription() { + return pht( + 'This method is frozen and will eventually be deprecated. New code '. + 'should use "maniphest.edit" instead.'); + } + protected function defineErrorTypes() { return array( 'ERR-BAD-TASK' => pht('No such Maniphest task exists.'), diff --git a/src/applications/paste/conduit/PasteCreateConduitAPIMethod.php b/src/applications/paste/conduit/PasteCreateConduitAPIMethod.php index b6ccd151ba..3badd04da7 100644 --- a/src/applications/paste/conduit/PasteCreateConduitAPIMethod.php +++ b/src/applications/paste/conduit/PasteCreateConduitAPIMethod.php @@ -10,6 +10,16 @@ final class PasteCreateConduitAPIMethod extends PasteConduitAPIMethod { return pht('Create a new paste.'); } + public function getMethodStatus() { + return self::METHOD_STATUS_FROZEN; + } + + public function getMethodStatusDescription() { + return pht( + 'This method is frozen and will eventually be deprecated. New code '. + 'should use "paste.edit" instead.'); + } + protected function defineParamTypes() { return array( 'content' => 'required string', diff --git a/src/applications/paste/conduit/PasteQueryConduitAPIMethod.php b/src/applications/paste/conduit/PasteQueryConduitAPIMethod.php index 6f3f87acf2..f20c18c06a 100644 --- a/src/applications/paste/conduit/PasteQueryConduitAPIMethod.php +++ b/src/applications/paste/conduit/PasteQueryConduitAPIMethod.php @@ -10,6 +10,16 @@ final class PasteQueryConduitAPIMethod extends PasteConduitAPIMethod { return pht('Query Pastes.'); } + public function getMethodStatus() { + return self::METHOD_STATUS_FROZEN; + } + + public function getMethodStatusDescription() { + return pht( + 'This method is frozen and will eventually be deprecated. New code '. + 'should use "paste.search" instead.'); + } + protected function defineParamTypes() { return array( 'ids' => 'optional list', diff --git a/src/applications/people/conduit/UserQueryConduitAPIMethod.php b/src/applications/people/conduit/UserQueryConduitAPIMethod.php index c42414a6be..df0ec65841 100644 --- a/src/applications/people/conduit/UserQueryConduitAPIMethod.php +++ b/src/applications/people/conduit/UserQueryConduitAPIMethod.php @@ -10,6 +10,16 @@ final class UserQueryConduitAPIMethod extends UserConduitAPIMethod { return pht('Query users.'); } + public function getMethodStatus() { + return self::METHOD_STATUS_FROZEN; + } + + public function getMethodStatusDescription() { + return pht( + 'This method is frozen and will eventually be deprecated. New code '. + 'should use "user.search" instead.'); + } + protected function defineParamTypes() { return array( 'usernames' => 'optional list', diff --git a/src/applications/project/conduit/ProjectCreateConduitAPIMethod.php b/src/applications/project/conduit/ProjectCreateConduitAPIMethod.php index aefade63a2..66075e7248 100644 --- a/src/applications/project/conduit/ProjectCreateConduitAPIMethod.php +++ b/src/applications/project/conduit/ProjectCreateConduitAPIMethod.php @@ -10,6 +10,16 @@ final class ProjectCreateConduitAPIMethod extends ProjectConduitAPIMethod { return pht('Create a project.'); } + public function getMethodStatus() { + return self::METHOD_STATUS_FROZEN; + } + + public function getMethodStatusDescription() { + return pht( + 'This method is frozen and will eventually be deprecated. New code '. + 'should use "project.edit" instead.'); + } + protected function defineParamTypes() { return array( 'name' => 'required string', diff --git a/src/applications/project/conduit/ProjectQueryConduitAPIMethod.php b/src/applications/project/conduit/ProjectQueryConduitAPIMethod.php index 3116ecb7f9..0a02088413 100644 --- a/src/applications/project/conduit/ProjectQueryConduitAPIMethod.php +++ b/src/applications/project/conduit/ProjectQueryConduitAPIMethod.php @@ -10,6 +10,16 @@ final class ProjectQueryConduitAPIMethod extends ProjectConduitAPIMethod { return pht('Execute searches for Projects.'); } + public function getMethodStatus() { + return self::METHOD_STATUS_FROZEN; + } + + public function getMethodStatusDescription() { + return pht( + 'This method is frozen and will eventually be deprecated. New code '. + 'should use "project.search" instead.'); + } + protected function defineParamTypes() { $statuses = array( diff --git a/src/applications/repository/conduit/RepositoryQueryConduitAPIMethod.php b/src/applications/repository/conduit/RepositoryQueryConduitAPIMethod.php index a3c1061e6a..61435e2a1b 100644 --- a/src/applications/repository/conduit/RepositoryQueryConduitAPIMethod.php +++ b/src/applications/repository/conduit/RepositoryQueryConduitAPIMethod.php @@ -8,11 +8,13 @@ final class RepositoryQueryConduitAPIMethod } public function getMethodStatus() { - return self::METHOD_STATUS_UNSTABLE; + return self::METHOD_STATUS_FROZEN; } public function getMethodStatusDescription() { - return pht('Repository methods are new and subject to change.'); + return pht( + 'This method is frozen and will eventually be deprecated. New code '. + 'should use "diffusion.repository.query" instead.'); } public function getMethodDescription() { diff --git a/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php b/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php index d050e48bff..3d13f2c3d9 100644 --- a/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php +++ b/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php @@ -32,17 +32,6 @@ abstract class PhabricatorSearchEngineAPIMethod return PhabricatorApplication::getByClass($class); } - public function getMethodStatus() { - return self::METHOD_STATUS_UNSTABLE; - } - - public function getMethodStatusDescription() { - return pht( - 'ApplicationSearch methods are fairly stable, but were introduced '. - 'relatively recently and may continue to evolve as more applications '. - 'adopt them.'); - } - final protected function defineParamTypes() { return array( 'queryKey' => 'optional string', diff --git a/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php b/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php index de693a4049..3a6dd4b70c 100644 --- a/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php @@ -11,17 +11,6 @@ abstract class PhabricatorEditEngineAPIMethod return PhabricatorApplication::getByClass($class); } - public function getMethodStatus() { - return self::METHOD_STATUS_UNSTABLE; - } - - public function getMethodStatusDescription() { - return pht( - 'ApplicationEditor methods are fairly stable, but were introduced '. - 'relatively recently and may continue to evolve as more applications '. - 'adopt them.'); - } - final protected function defineParamTypes() { return array( 'transactions' => 'list>', From 425deeb52391462521ea20f1715498afad724fa6 Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 9 Jan 2017 08:00:07 -0800 Subject: [PATCH 10/51] Fix an issue which could prevent blocking reviewers from being removed from revisions Summary: Ref T11114. After evaluating typeahead tokens, we could process blocking reviewer removals incorrectly: we may get structures back. Test Plan: Removed blocking reviewers from the web UI. Reviewers: chad Reviewed By: chad Maniphest Tasks: T11114 Differential Revision: https://secure.phabricator.com/D17163 --- .../xaction/DifferentialRevisionReviewersTransaction.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/applications/differential/xaction/DifferentialRevisionReviewersTransaction.php b/src/applications/differential/xaction/DifferentialRevisionReviewersTransaction.php index 6ecf738162..1e1b35c1c3 100644 --- a/src/applications/differential/xaction/DifferentialRevisionReviewersTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionReviewersTransaction.php @@ -24,7 +24,13 @@ final class DifferentialRevisionReviewersTransaction // First, remove any reviewers we're getting rid of. $rem = idx($value, '-', array()); $rem = $datasource->evaluateTokens($rem); - foreach ($rem as $phid) { + foreach ($rem as $spec) { + if (!is_array($spec)) { + $phid = $spec; + } else { + $phid = $spec['phid']; + } + unset($reviewers[$phid]); } From b08c9b3ffa8a5bc9578a7dad3730c8ce4b86570b Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 9 Jan 2017 08:16:56 -0800 Subject: [PATCH 11/51] Remove extra container tag on HandleListViews rendering from ModularTransactions in text mode Summary: Fixes T12082. Ref T11114. When modular transaction render a handle list, they use HandleListView, which has a text mode. However, the HandleListView is a TagView, and currently TagViews always render a tag of some kind. Allow them to return `null` to decline to render any tag. Test Plan: - Added a pile of debugging stuff to `ApplicationTransactionEditor` to throw during mail generation. - Added a reviewer to a revision. - Used `bin/worker execute --id ...` to hit the mail generation repeatedly. - Before patch: mail generated with a , even in text mode. - After patch: clean mail generation. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12082, T11114 Differential Revision: https://secure.phabricator.com/D17162 --- src/applications/phid/view/PHUIHandleListView.php | 10 +++++++--- src/view/AphrontTagView.php | 11 ++++++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/applications/phid/view/PHUIHandleListView.php b/src/applications/phid/view/PHUIHandleListView.php index f7f02a391b..ed57da611b 100644 --- a/src/applications/phid/view/PHUIHandleListView.php +++ b/src/applications/phid/view/PHUIHandleListView.php @@ -38,9 +38,13 @@ final class PHUIHandleListView } protected function getTagName() { - // TODO: It would be nice to render this with a proper
    , at least in - // block mode, but don't stir the waters up too much for now. - return 'span'; + if ($this->getAsText()) { + return null; + } else { + // TODO: It would be nice to render this with a proper
      , at least + // in block mode, but don't stir the waters up too much for now. + return 'span'; + } } protected function getTagContent() { diff --git a/src/view/AphrontTagView.php b/src/view/AphrontTagView.php index a6eb722383..a055fb16ad 100644 --- a/src/view/AphrontTagView.php +++ b/src/view/AphrontTagView.php @@ -92,6 +92,15 @@ abstract class AphrontTagView extends AphrontView { final public function render() { $this->willRender(); + // A tag view may render no tag at all. For example, the HandleListView is + // a container which renders a tag in HTML mode, but can also render in + // text mode without producing a tag. When a tag view has no tag name, just + // return the tag content as though the view did not exist. + $tag_name = $this->getTagName(); + if ($tag_name === null) { + return $this->getTagContent(); + } + $attributes = $this->getTagAttributes(); $implode = array('class', 'sigil'); @@ -147,7 +156,7 @@ abstract class AphrontTagView extends AphrontView { } return javelin_tag( - $this->getTagName(), + $tag_name, $attributes, $this->getTagContent()); } From 27ecedd1d5d839bdf5259eab9a7824491d595e28 Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 9 Jan 2017 07:35:54 -0800 Subject: [PATCH 12/51] Use some more human-readable Conduit keys in updated API methods Summary: Ref T12074. This uses more consistent Conduit keys for constraint names. This is a minor compatibility break on watchers/members but since these methods are more useful now this is probably a good time to try to get away with it, and a more consistent API is better in the long run. I need to issue compatibility guidance for the milestones thing anyway and that one isn't avoidable, so try to rip the bandage off all in one go. Test Plan: Reviewed new constraint names from console, called methods using them. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12074 Differential Revision: https://secure.phabricator.com/D17161 --- .../project/query/PhabricatorProjectColumnSearchEngine.php | 1 + .../project/query/PhabricatorProjectSearchEngine.php | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/applications/project/query/PhabricatorProjectColumnSearchEngine.php b/src/applications/project/query/PhabricatorProjectColumnSearchEngine.php index 421ba37929..0658ec75b0 100644 --- a/src/applications/project/query/PhabricatorProjectColumnSearchEngine.php +++ b/src/applications/project/query/PhabricatorProjectColumnSearchEngine.php @@ -20,6 +20,7 @@ final class PhabricatorProjectColumnSearchEngine id(new PhabricatorPHIDsSearchField()) ->setLabel(pht('Projects')) ->setKey('projectPHIDs') + ->setConduitKey('projects') ->setAliases(array('project', 'projects', 'projectPHID')), ); } diff --git a/src/applications/project/query/PhabricatorProjectSearchEngine.php b/src/applications/project/query/PhabricatorProjectSearchEngine.php index 49a989eb4b..5c153be5a4 100644 --- a/src/applications/project/query/PhabricatorProjectSearchEngine.php +++ b/src/applications/project/query/PhabricatorProjectSearchEngine.php @@ -24,10 +24,12 @@ final class PhabricatorProjectSearchEngine id(new PhabricatorUsersSearchField()) ->setLabel(pht('Members')) ->setKey('memberPHIDs') + ->setConduitKey('members') ->setAliases(array('member', 'members')), id(new PhabricatorUsersSearchField()) ->setLabel(pht('Watchers')) ->setKey('watcherPHIDs') + ->setConduitKey('watchers') ->setAliases(array('watcher', 'watchers')), id(new PhabricatorSearchSelectField()) ->setLabel(pht('Status')) @@ -55,11 +57,13 @@ final class PhabricatorProjectSearchEngine id(new PhabricatorPHIDsSearchField()) ->setLabel(pht('Parent Projects')) ->setKey('parentPHIDs') + ->setConduitKey('parents') ->setAliases(array('parent', 'parents', 'parentPHID')) ->setDescription(pht('Find direct subprojects of specified parents.')), id(new PhabricatorPHIDsSearchField()) ->setLabel(pht('Ancestor Projects')) ->setKey('ancestorPHIDs') + ->setConduitKey('ancestors') ->setAliases(array('ancestor', 'ancestors', 'ancestorPHID')) ->setDescription( pht('Find all subprojects beneath specified ancestors.')), From 2dfe79cfc7134940f0be1e97b74b02c4c0544080 Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 9 Jan 2017 11:39:17 -0800 Subject: [PATCH 13/51] When updating revisions in response to commits, reuse previously generated diffs Summary: Fixes T10968. In rare situations, we can generate a diff, then hit an error which causes this update to fail. When it does, we tend to get stuck in a loop creating diffs, which can fill the database up with garbage. We saw this once in the Phacility cluster, and one instance hit it, too. Instead: when we create a diff, keep track of which commit we generated it from. The next time through, reuse it if we already built it. Test Plan: - Used `bin/differential attach-commit ` to hit this code. - Simulated a filesystem write failure, saw the diff get reused. - Also did a normal update, which worked properly. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10968 Differential Revision: https://secure.phabricator.com/D17164 --- .../autopatches/20170109.diff.01.commit.sql | 2 + .../DifferentialDiffExtractionEngine.php | 14 +++++++ .../query/DifferentialDiffQuery.php | 37 +++++++++++++++++-- .../differential/storage/DifferentialDiff.php | 5 +++ 4 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 resources/sql/autopatches/20170109.diff.01.commit.sql diff --git a/resources/sql/autopatches/20170109.diff.01.commit.sql b/resources/sql/autopatches/20170109.diff.01.commit.sql new file mode 100644 index 0000000000..2a28900272 --- /dev/null +++ b/resources/sql/autopatches/20170109.diff.01.commit.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_differential.differential_diff + ADD commitPHID VARBINARY(64); diff --git a/src/applications/differential/engine/DifferentialDiffExtractionEngine.php b/src/applications/differential/engine/DifferentialDiffExtractionEngine.php index 05e19bbe71..952c76c63f 100644 --- a/src/applications/differential/engine/DifferentialDiffExtractionEngine.php +++ b/src/applications/differential/engine/DifferentialDiffExtractionEngine.php @@ -26,6 +26,19 @@ final class DifferentialDiffExtractionEngine extends Phobject { public function newDiffFromCommit(PhabricatorRepositoryCommit $commit) { $viewer = $this->getViewer(); + // If we already have an unattached diff for this commit, just reuse it. + // This stops us from repeatedly generating diffs if something goes wrong + // later in the process. See T10968 for context. + $existing_diffs = id(new DifferentialDiffQuery()) + ->setViewer($viewer) + ->withCommitPHIDs(array($commit->getPHID())) + ->withHasRevision(false) + ->needChangesets(true) + ->execute(); + if ($existing_diffs) { + return head($existing_diffs); + } + $repository = $commit->getRepository(); $identifier = $commit->getCommitIdentifier(); $monogram = $commit->getMonogram(); @@ -73,6 +86,7 @@ final class DifferentialDiffExtractionEngine extends Phobject { $diff = DifferentialDiff::newFromRawChanges($viewer, $changes) ->setRepositoryPHID($repository->getPHID()) + ->setCommitPHID($commit->getPHID()) ->setCreationMethod('commit') ->setSourceControlSystem($repository->getVersionControlSystem()) ->setLintStatus(DifferentialLintStatus::LINT_AUTO_SKIP) diff --git a/src/applications/differential/query/DifferentialDiffQuery.php b/src/applications/differential/query/DifferentialDiffQuery.php index 23c016446c..f779e40c26 100644 --- a/src/applications/differential/query/DifferentialDiffQuery.php +++ b/src/applications/differential/query/DifferentialDiffQuery.php @@ -6,6 +6,8 @@ final class DifferentialDiffQuery private $ids; private $phids; private $revisionIDs; + private $commitPHIDs; + private $hasRevision; private $needChangesets = false; private $needProperties; @@ -25,6 +27,16 @@ final class DifferentialDiffQuery return $this; } + public function withCommitPHIDs(array $phids) { + $this->commitPHIDs = $phids; + return $this; + } + + public function withHasRevision($has_revision) { + $this->hasRevision = $has_revision; + return $this; + } + public function needChangesets($bool) { $this->needChangesets = $bool; return $this; @@ -108,27 +120,46 @@ final class DifferentialDiffQuery protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); - if ($this->ids) { + if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } - if ($this->phids) { + if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } - if ($this->revisionIDs) { + if ($this->revisionIDs !== null) { $where[] = qsprintf( $conn, 'revisionID IN (%Ld)', $this->revisionIDs); } + if ($this->commitPHIDs !== null) { + $where[] = qsprintf( + $conn, + 'commitPHID IN (%Ls)', + $this->commitPHIDs); + } + + if ($this->hasRevision !== null) { + if ($this->hasRevision) { + $where[] = qsprintf( + $conn, + 'revisionID IS NOT NULL'); + } else { + $where[] = qsprintf( + $conn, + 'revisionID IS NULL'); + } + } + return $where; } diff --git a/src/applications/differential/storage/DifferentialDiff.php b/src/applications/differential/storage/DifferentialDiff.php index 891af35982..d8c2ea9786 100644 --- a/src/applications/differential/storage/DifferentialDiff.php +++ b/src/applications/differential/storage/DifferentialDiff.php @@ -13,6 +13,7 @@ final class DifferentialDiff protected $revisionID; protected $authorPHID; protected $repositoryPHID; + protected $commitPHID; protected $sourceMachine; protected $sourcePath; @@ -62,6 +63,7 @@ final class DifferentialDiff 'branch' => 'text255?', 'bookmark' => 'text255?', 'repositoryUUID' => 'text64?', + 'commitPHID' => 'phid?', // T6203/NULLABILITY // These should be non-null; all diffs should have a creation method @@ -73,6 +75,9 @@ final class DifferentialDiff 'revisionID' => array( 'columns' => array('revisionID'), ), + 'key_commit' => array( + 'columns' => array('commitPHID'), + ), ), ) + parent::getConfiguration(); } From 0e1388340c5a0e560a27a948572ea8d9d77323cc Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 9 Jan 2017 11:57:32 -0800 Subject: [PATCH 14/51] Make profile menu `/edit/` requests explicitly 404 Summary: See D17160. Previously, the `/edit/` route was never linked, but fataled when accessed. Make it 404 instead. Also, fix an issue where editing "Application" menu items would fail because they didn't have a viewer. Test Plan: - Hit `/edit/`, got a 404. - Edited an "Application" item. - Moved, added, deleted, and edited other items. Reviewers: chad Reviewed By: chad Differential Revision: https://secure.phabricator.com/D17165 --- .../search/engine/PhabricatorProfileMenuEngine.php | 10 ++++++++++ .../PhabricatorProfileMenuItemConfigurationQuery.php | 1 + 2 files changed, 11 insertions(+) diff --git a/src/applications/search/engine/PhabricatorProfileMenuEngine.php b/src/applications/search/engine/PhabricatorProfileMenuEngine.php index a3cc8e34d1..05aa0d181b 100644 --- a/src/applications/search/engine/PhabricatorProfileMenuEngine.php +++ b/src/applications/search/engine/PhabricatorProfileMenuEngine.php @@ -114,6 +114,16 @@ abstract class PhabricatorProfileMenuEngine extends Phobject { return new Aphront404Response(); } break; + case 'edit': + if (!$request->getURIData('id')) { + // If we continue along the "edit" pathway without an ID, we hit an + // unrelated exception because we can not build a new menu item out + // of thin air. For menus, new items are created via the "new" + // action. Just catch this case and 404 early since there's currently + // no clean way to make EditEngine aware of this. + return new Aphront404Response(); + } + break; } $navigation = $this->buildNavigation(); diff --git a/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php b/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php index f59e20460e..eb09044d63 100644 --- a/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php +++ b/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php @@ -80,6 +80,7 @@ final class PhabricatorProfileMenuItemConfigurationQuery continue; } $item_type = clone $item_type; + $item_type->setViewer($this->getViewer()); $item->attachMenuItem($item_type); } From fda83094ac591df8bbdde6af8e4190fdf962c166 Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 9 Jan 2017 12:55:13 -0800 Subject: [PATCH 15/51] Restore missing behavior for Differential keyboard navigation Summary: Fixes T12086. This got dropped by accident while cleaning up haunting. Test Plan: Loaed a revision, hit "?", hit n/j/p/etc Reviewers: chad Reviewed By: chad Maniphest Tasks: T12086 Differential Revision: https://secure.phabricator.com/D17166 --- .../controller/DifferentialRevisionViewController.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/applications/differential/controller/DifferentialRevisionViewController.php b/src/applications/differential/controller/DifferentialRevisionViewController.php index 6bf9aaa44f..5c476ef985 100644 --- a/src/applications/differential/controller/DifferentialRevisionViewController.php +++ b/src/applications/differential/controller/DifferentialRevisionViewController.php @@ -464,6 +464,12 @@ final class DifferentialRevisionViewController extends DifferentialController { Javelin::initBehavior('differential-user-select'); + Javelin::initBehavior( + 'differential-keyboard-navigation', + array( + 'haunt' => null, + )); + $view = id(new PHUITwoColumnView()) ->setHeader($header) ->setSubheader($subheader) From 7ea1bd5a5a44361ebec617b0ae17e1fac965f711 Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 9 Jan 2017 17:40:42 -0800 Subject: [PATCH 16/51] Fix two cluster repository documentation typos Summary: Ref T12087. Caught these while re-reading. Test Plan: O.O Reviewers: chad Reviewed By: chad Maniphest Tasks: T12087 Differential Revision: https://secure.phabricator.com/D17167 --- src/docs/user/cluster/cluster_repositories.diviner | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/docs/user/cluster/cluster_repositories.diviner b/src/docs/user/cluster/cluster_repositories.diviner index e7e8031677..463814c890 100644 --- a/src/docs/user/cluster/cluster_repositories.diviner +++ b/src/docs/user/cluster/cluster_repositories.diviner @@ -37,7 +37,7 @@ Before responding to a write, replicas obtain a global lock, perform the same version check and fetch if necessary, then allow the write to continue. Additionally, repositories passively check other nodes for updates and -replicate changes in the background. After you push a change to a repositroy, +replicate changes in the background. After you push a change to a repository, it will usually spread passively to all other repository nodes within a few minutes. @@ -381,8 +381,8 @@ this: Here, all of the "leader" devices with the most up-to-date copy of the repository have been lost. Phabricator will freeze the repository refuse to -serve requests because it can not serve it consistently, and can not accept new -writes without data loss. +serve requests because it can not serve reads consistently and can not accept +new writes without data loss. The most straightforward way to resolve this issue is to restore any leader to service. The change will be able to replicate to other devices once a leader From 6816974d574c54f883eb927dadc6293f15f4fa40 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Tue, 10 Jan 2017 11:20:26 -0800 Subject: [PATCH 17/51] Basic Favorites application Summary: Ref T5867. Rough in a Favorites application, not wired to anything. Test Plan: tbd. currently 404s so... I messed up something. Tossing up to read. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T5867 Differential Revision: https://secure.phabricator.com/D17160 --- src/__phutil_library_map__.php | 14 ++++ .../PhabricatorFavoritesApplication.php | 39 +++++++++++ .../PhabricatorFavoritesConstants.php | 8 +++ .../PhabricatorFavoritesController.php | 3 + .../PhabricatorFavoritesMainController.php | 58 +++++++++++++++++ ...PhabricatorFavoritesMenuItemController.php | 29 +++++++++ .../PhabricatorFavoritesProfileMenuEngine.php | 36 ++++++++++ ...bricatorFavoritesManageProfileMenuItem.php | 65 +++++++++++++++++++ .../engine/PhabricatorProfileMenuEngine.php | 7 +- ...habricatorProfileMenuItemConfiguration.php | 6 +- 10 files changed, 262 insertions(+), 3 deletions(-) create mode 100644 src/applications/favorites/application/PhabricatorFavoritesApplication.php create mode 100644 src/applications/favorites/constants/PhabricatorFavoritesConstants.php create mode 100644 src/applications/favorites/controller/PhabricatorFavoritesController.php create mode 100644 src/applications/favorites/controller/PhabricatorFavoritesMainController.php create mode 100644 src/applications/favorites/controller/PhabricatorFavoritesMenuItemController.php create mode 100644 src/applications/favorites/engine/PhabricatorFavoritesProfileMenuEngine.php create mode 100644 src/applications/favorites/menuitem/PhabricatorFavoritesManageProfileMenuItem.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 810f0c6b16..bd052d18f2 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2654,6 +2654,13 @@ phutil_register_library_map(array( 'PhabricatorFactSimpleSpec' => 'applications/fact/spec/PhabricatorFactSimpleSpec.php', 'PhabricatorFactSpec' => 'applications/fact/spec/PhabricatorFactSpec.php', 'PhabricatorFactUpdateIterator' => 'applications/fact/extract/PhabricatorFactUpdateIterator.php', + 'PhabricatorFavoritesApplication' => 'applications/favorites/application/PhabricatorFavoritesApplication.php', + 'PhabricatorFavoritesConstants' => 'applications/favorites/constants/PhabricatorFavoritesConstants.php', + 'PhabricatorFavoritesController' => 'applications/favorites/controller/PhabricatorFavoritesController.php', + 'PhabricatorFavoritesMainController' => 'applications/favorites/controller/PhabricatorFavoritesMainController.php', + 'PhabricatorFavoritesManageProfileMenuItem' => 'applications/favorites/menuitem/PhabricatorFavoritesManageProfileMenuItem.php', + 'PhabricatorFavoritesMenuItemController' => 'applications/favorites/controller/PhabricatorFavoritesMenuItemController.php', + 'PhabricatorFavoritesProfileMenuEngine' => 'applications/favorites/engine/PhabricatorFavoritesProfileMenuEngine.php', 'PhabricatorFaxContentSource' => 'infrastructure/contentsource/PhabricatorFaxContentSource.php', 'PhabricatorFeedApplication' => 'applications/feed/application/PhabricatorFeedApplication.php', 'PhabricatorFeedBuilder' => 'applications/feed/builder/PhabricatorFeedBuilder.php', @@ -7650,6 +7657,13 @@ phutil_register_library_map(array( 'PhabricatorFactSimpleSpec' => 'PhabricatorFactSpec', 'PhabricatorFactSpec' => 'Phobject', 'PhabricatorFactUpdateIterator' => 'PhutilBufferedIterator', + 'PhabricatorFavoritesApplication' => 'PhabricatorApplication', + 'PhabricatorFavoritesConstants' => 'PhabricatorFavoritesController', + 'PhabricatorFavoritesController' => 'PhabricatorController', + 'PhabricatorFavoritesMainController' => 'PhabricatorFavoritesController', + 'PhabricatorFavoritesManageProfileMenuItem' => 'PhabricatorProfileMenuItem', + 'PhabricatorFavoritesMenuItemController' => 'PhabricatorFavoritesController', + 'PhabricatorFavoritesProfileMenuEngine' => 'PhabricatorProfileMenuEngine', 'PhabricatorFaxContentSource' => 'PhabricatorContentSource', 'PhabricatorFeedApplication' => 'PhabricatorApplication', 'PhabricatorFeedBuilder' => 'Phobject', diff --git a/src/applications/favorites/application/PhabricatorFavoritesApplication.php b/src/applications/favorites/application/PhabricatorFavoritesApplication.php new file mode 100644 index 0000000000..3c5211fec4 --- /dev/null +++ b/src/applications/favorites/application/PhabricatorFavoritesApplication.php @@ -0,0 +1,39 @@ + array( + '' => 'PhabricatorFavoritesMainController', + '(?Pglobal|personal)/item/' => $this->getProfileMenuRouting( + 'PhabricatorFavoritesMenuItemController'), + ), + ); + } + + public function isLaunchable() { + return false; + } + + public function getApplicationOrder() { + return 9; + } + +} diff --git a/src/applications/favorites/constants/PhabricatorFavoritesConstants.php b/src/applications/favorites/constants/PhabricatorFavoritesConstants.php new file mode 100644 index 0000000000..5da0862985 --- /dev/null +++ b/src/applications/favorites/constants/PhabricatorFavoritesConstants.php @@ -0,0 +1,8 @@ +getViewer(); + + $menu = id(new PHUIObjectItemListView()) + ->setUser($viewer); + + $menu->addItem( + id(new PHUIObjectItemView()) + ->setHeader(pht('Personal Menu Items')) + ->setHref($this->getApplicationURI('personal/item/configure/')) + ->setImageURI($viewer->getProfileImageURI()) + ->addAttribute(pht('Edit favorites for your personal account.'))); + + $icon = id(new PHUIIconView()) + ->setIcon('fa-globe') + ->setBackground('bg-blue'); + + $menu->addItem( + id(new PHUIObjectItemView()) + ->setHeader(pht('Global Menu Items')) + ->setHref($this->getApplicationURI('global/item/configure/')) + ->setImageIcon($icon) + ->addAttribute(pht('Edit global default favorites for all users.'))); + + $crumbs = $this->buildApplicationCrumbs(); + $crumbs->addTextCrumb(pht('Manage')); + $crumbs->setBorder(true); + + $box = id(new PHUIObjectBoxView()) + ->setObjectList($menu); + + $header = id(new PHUIHeaderView()) + ->setHeader(pht('Manage Favorites')) + ->setHeaderIcon('fa-star-o'); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter(array( + $box, + )); + + return $this->newPage() + ->setTitle(pht('Manage')) + ->setCrumbs($crumbs) + ->appendChild($view); + + } + +} diff --git a/src/applications/favorites/controller/PhabricatorFavoritesMenuItemController.php b/src/applications/favorites/controller/PhabricatorFavoritesMenuItemController.php new file mode 100644 index 0000000000..a398cf6ee8 --- /dev/null +++ b/src/applications/favorites/controller/PhabricatorFavoritesMenuItemController.php @@ -0,0 +1,29 @@ +getViewer(); + $type = $request->getURIData('type'); + $custom_phid = null; + if ($type == 'personal') { + $custom_phid = $viewer->getPHID(); + } + + $application = 'PhabricatorFavoritesApplication'; + $favorites = id(new PhabricatorApplicationQuery()) + ->setViewer($viewer) + ->withClasses(array($application)) + ->withInstalled(true) + ->executeOne(); + + $engine = id(new PhabricatorFavoritesProfileMenuEngine()) + ->setProfileObject($favorites) + ->setCustomPHID($custom_phid) + ->setController($this); + + return $engine->buildResponse(); + } + +} diff --git a/src/applications/favorites/engine/PhabricatorFavoritesProfileMenuEngine.php b/src/applications/favorites/engine/PhabricatorFavoritesProfileMenuEngine.php new file mode 100644 index 0000000000..114235b76b --- /dev/null +++ b/src/applications/favorites/engine/PhabricatorFavoritesProfileMenuEngine.php @@ -0,0 +1,36 @@ +getProfileObject(); + $custom = $this->getCustomPHID(); + + if ($custom) { + return "/favorites/personal/item/{$path}"; + } else { + return "/favorites/global/item/{$path}"; + } + } + + protected function getBuiltinProfileItems($object) { + $items = array(); + + $custom = $this->getCustomPHID(); + + if ($custom) { + $items[] = $this->newItem() + ->setBuiltinKey(PhabricatorFavoritesConstants::ITEM_MANAGE) + ->setMenuItemKey( + PhabricatorFavoritesManageProfileMenuItem::MENUITEMKEY); + } + + return $items; + } + +} diff --git a/src/applications/favorites/menuitem/PhabricatorFavoritesManageProfileMenuItem.php b/src/applications/favorites/menuitem/PhabricatorFavoritesManageProfileMenuItem.php new file mode 100644 index 0000000000..0f0a3d10a6 --- /dev/null +++ b/src/applications/favorites/menuitem/PhabricatorFavoritesManageProfileMenuItem.php @@ -0,0 +1,65 @@ +getMenuItemProperty('name'); + + if (strlen($name)) { + return $name; + } + + return $this->getDefaultName(); + } + + public function buildEditEngineFields( + PhabricatorProfileMenuItemConfiguration $config) { + return array( + id(new PhabricatorTextEditField()) + ->setKey('name') + ->setLabel(pht('Name')) + ->setPlaceholder($this->getDefaultName()) + ->setValue($config->getMenuItemProperty('name')), + ); + } + + protected function newNavigationMenuItems( + PhabricatorProfileMenuItemConfiguration $config) { + + $name = $this->getDisplayName($config); + $icon = 'fa-pencil'; + $href = '/favorites/personal/item/configure/'; + + $item = $this->newItem() + ->setHref($href) + ->setName($name) + ->setIcon($icon); + + return array( + $item, + ); + } + +} diff --git a/src/applications/search/engine/PhabricatorProfileMenuEngine.php b/src/applications/search/engine/PhabricatorProfileMenuEngine.php index 05aa0d181b..c1d4df3b2a 100644 --- a/src/applications/search/engine/PhabricatorProfileMenuEngine.php +++ b/src/applications/search/engine/PhabricatorProfileMenuEngine.php @@ -748,9 +748,11 @@ abstract class PhabricatorProfileMenuEngine extends Phobject { return new Aphront404Response(); } + $custom_phid = $this->getCustomPHID(); $configuration = PhabricatorProfileMenuItemConfiguration::initializeNewItem( $object, - $item_type); + $item_type, + $custom_phid); $viewer = $this->getViewer(); @@ -765,6 +767,7 @@ abstract class PhabricatorProfileMenuEngine extends Phobject { ->setMenuEngine($this) ->setProfileObject($object) ->setNewMenuItemConfiguration($configuration) + ->setCustomPHID($custom_phid) ->setController($controller) ->buildResponse(); } @@ -778,6 +781,7 @@ abstract class PhabricatorProfileMenuEngine extends Phobject { ->setMenuEngine($this) ->setProfileObject($object) ->setController($controller) + ->setCustomPHID($this->getCustomPHID()) ->buildResponse(); } @@ -810,6 +814,7 @@ abstract class PhabricatorProfileMenuEngine extends Phobject { ->setProfileObject($object) ->setNewMenuItemConfiguration($configuration) ->setController($controller) + ->setCustomPHID($this->getCustomPHID()) ->buildResponse(); } diff --git a/src/applications/search/storage/PhabricatorProfileMenuItemConfiguration.php b/src/applications/search/storage/PhabricatorProfileMenuItemConfiguration.php index 9509155e1b..78fc380604 100644 --- a/src/applications/search/storage/PhabricatorProfileMenuItemConfiguration.php +++ b/src/applications/search/storage/PhabricatorProfileMenuItemConfiguration.php @@ -34,13 +34,15 @@ final class PhabricatorProfileMenuItemConfiguration public static function initializeNewItem( $profile_object, - PhabricatorProfileMenuItem $item) { + PhabricatorProfileMenuItem $item, + $custom_phid) { return self::initializeNewBuiltin() ->setProfilePHID($profile_object->getPHID()) ->setMenuItemKey($item->getMenuItemKey()) ->attachMenuItem($item) - ->attachProfileObject($profile_object); + ->attachProfileObject($profile_object) + ->setCustomPHID($custom_phid); } protected function getConfiguration() { From 11f3a8cfca83dd3b12a0add8df094c20e6a095c5 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 10 Jan 2017 11:53:14 -0800 Subject: [PATCH 18/51] Force 'changes' to a string in hunk migration Summary: Fixes T12090. In obscure situations lost to the mists of time, the `changes` column could be `null`. Force a string cast so the migration finishes, even though these changesets are likely meaningless. Test Plan: I did a force-reapply as a sanity check: ``` $ ./bin/storage upgrade -f --apply phabricator:20161213.diff.01.hunks.php ``` That went cleanly; it would only have caught dramatic errors, but I didn't completely butcher things. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12090 Differential Revision: https://secure.phabricator.com/D17168 --- resources/sql/autopatches/20161213.diff.01.hunks.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/sql/autopatches/20161213.diff.01.hunks.php b/resources/sql/autopatches/20161213.diff.01.hunks.php index c6394defce..574adb3b4f 100644 --- a/resources/sql/autopatches/20161213.diff.01.hunks.php +++ b/resources/sql/autopatches/20161213.diff.01.hunks.php @@ -28,7 +28,8 @@ foreach (new LiskRawMigrationIterator($conn, $src_table) as $row) { DifferentialModernHunk::DATATYPE_TEXT, 'utf8', DifferentialModernHunk::DATAFORMAT_RAW, - $row['changes'], + // In rare cases, this could be NULL. See T12090. + (string)$row['changes'], $row['dateCreated'], $row['dateModified']); } From 00e2755eab62564eb7863c2ab1df63e0e9056515 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 10 Jan 2017 12:32:07 -0800 Subject: [PATCH 19/51] Provide tailored strings for revision creation Summary: See D17169. Ref T11114. Test Plan: {F2333825} Reviewers: chad Reviewed By: chad Maniphest Tasks: T11114 Differential Revision: https://secure.phabricator.com/D17170 --- .../differential/editor/DifferentialTransactionEditor.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/applications/differential/editor/DifferentialTransactionEditor.php b/src/applications/differential/editor/DifferentialTransactionEditor.php index eedcd6b7bb..85a44669c8 100644 --- a/src/applications/differential/editor/DifferentialTransactionEditor.php +++ b/src/applications/differential/editor/DifferentialTransactionEditor.php @@ -18,6 +18,14 @@ final class DifferentialTransactionEditor return pht('Differential Revisions'); } + public function getCreateObjectTitle($author, $object) { + return pht('%s created this revision.', $author); + } + + public function getCreateObjectTitleForFeed($author, $object) { + return pht('%s created %s.', $author, $object); + } + public function getDiffUpdateTransaction(array $xactions) { $type_update = DifferentialTransaction::TYPE_UPDATE; From ccff47682f7c6b6d93b284f8558e72cd47b319bf Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 10 Jan 2017 12:21:22 -0800 Subject: [PATCH 20/51] Provide more useful guidance if a repository is clusterized into an existing multi-device cluster Summary: Fixes T12087. When transitioning into a clustered configuration for the first time, the documentation recommends using a one-device cluster as a transitional step. However, installs may not do this for whatever reason, and we aren't as clear as we could be in warning about clusterizing directly into a multi-device cluster. Roughly, when you do this, we end up believing that working copies exist on several different devices, but have no information about which copy or copies are up to date. //Usually// they all were already synchronized and are all up to date, but we can't make this assumption safely without risking data. Instead, we err on the side of caution, and require a human to tell us which copy we should consider to be up-to-date, using `bin/repository thaw --promote`. Test Plan: ``` $ ./bin/repository clusterize rLOCKS --service repos001.phacility.net Service "repos001.phacility.net" is actively bound to more than one device (local002.local, local001.phacility.net). If you clusterize a repository onto this service it will be unclear which devices have up-to-date copies of the repository. This leader/follower ambiguity will freeze the repository. You may need to manually promote a device to unfreeze it. See "Ambiguous Leaders" in the documentation for discussion. Continue anyway? [y/N] ``` Read other changes. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12087 Differential Revision: https://secure.phabricator.com/D17169 --- .../DiffusionRepositoryClusterEngine.php | 5 ++- ...RepositoryManagementClusterizeWorkflow.php | 35 ++++++++++++++++++- .../user/cluster/cluster_repositories.diviner | 13 ++++--- 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/src/applications/diffusion/protocol/DiffusionRepositoryClusterEngine.php b/src/applications/diffusion/protocol/DiffusionRepositoryClusterEngine.php index 8937c1f205..4bb9ca6255 100644 --- a/src/applications/diffusion/protocol/DiffusionRepositoryClusterEngine.php +++ b/src/applications/diffusion/protocol/DiffusionRepositoryClusterEngine.php @@ -251,9 +251,8 @@ final class DiffusionRepositoryClusterEngine extends Phobject { pht( 'Repository "%s" exists on more than one device, but no device '. 'has any repository version information. Phabricator can not '. - 'guess which copy of the existing data is authoritative. Remove '. - 'all but one device from service to mark the remaining device '. - 'as the authority.', + 'guess which copy of the existing data is authoritative. Promote '. + 'a device or see "Ambigous Leaders" in the documentation.', $repository->getDisplayName())); } diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementClusterizeWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementClusterizeWorkflow.php index 7424b84eae..7178564067 100644 --- a/src/applications/repository/management/PhabricatorRepositoryManagementClusterizeWorkflow.php +++ b/src/applications/repository/management/PhabricatorRepositoryManagementClusterizeWorkflow.php @@ -61,6 +61,7 @@ final class PhabricatorRepositoryManagementClusterizeWorkflow array( AlmanacClusterRepositoryServiceType::SERVICETYPE, )) + ->needBindings(true) ->executeOne(); if (!$service) { throw new PhutilArgumentUsageException( @@ -70,9 +71,41 @@ final class PhabricatorRepositoryManagementClusterizeWorkflow } } - if ($service) { $service_phid = $service->getPHID(); + + $bindings = $service->getActiveBindings(); + + $unique_devices = array(); + foreach ($bindings as $binding) { + $unique_devices[$binding->getDevicePHID()] = $binding->getDevice(); + } + + if (count($unique_devices) > 1) { + $device_names = mpull($unique_devices, 'getName'); + + echo id(new PhutilConsoleBlock()) + ->addParagraph( + pht( + 'Service "%s" is actively bound to more than one device (%s).', + $service_name, + implode(', ', $device_names))) + ->addParagraph( + pht( + 'If you clusterize a repository onto this service it may be '. + 'unclear which devices have up-to-date copies of the '. + 'repository. If so, leader/follower ambiguity will freeze the '. + 'repository. You may need to manually promote a device to '. + 'unfreeze it. See "Ambiguous Leaders" in the documentation '. + 'for discussion.')) + ->drawConsoleString(); + + $prompt = pht('Continue anyway?'); + if (!phutil_console_confirm($prompt)) { + throw new PhutilArgumentUsageException( + pht('User aborted the workflow.')); + } + } } else { $service_phid = null; } diff --git a/src/docs/user/cluster/cluster_repositories.diviner b/src/docs/user/cluster/cluster_repositories.diviner index 463814c890..21aba731cc 100644 --- a/src/docs/user/cluster/cluster_repositories.diviner +++ b/src/docs/user/cluster/cluster_repositories.diviner @@ -422,17 +422,22 @@ Ambiguous Leaders ================= Repository clusters can also freeze if the leader devices are ambiguous. This -can happen if you replace an entire cluster with new devices suddenly, or -make a mistake with the `--demote` flag. This generally arises from some kind -of operator error, like this: +can happen if you replace an entire cluster with new devices suddenly, or make +a mistake with the `--demote` flag. This may arise from some kind of operator +error, like these: - Someone accidentally uses `bin/repository thaw ... --demote` to demote every device in a cluster. - Someone accidentally deletes all the version information for a repository from the database by making a mistake with a `DELETE` or `UPDATE` query. - - Someone accidentally disable all of the devices in a cluster, then add + - Someone accidentally disables all of the devices in a cluster, then adds entirely new ones before repositories can propagate. +If you are moving repositories into cluster services, you can also reach this +state if you use `clusterize` to associate a repository with a service that is +bound to multiple active devices. In this case, Phabricator will not know which +device or devices have up-to-date information. + When Phabricator can not tell which device in a cluster is a leader, it freezes the cluster because it is possible that some devices have less data and others have more, and if it choses a leader arbitrarily it may destroy some data From 63af2275ca05576c837ee37ead61bc7dfc21f7e9 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Tue, 10 Jan 2017 13:13:47 -0800 Subject: [PATCH 21/51] Re-darken full width diff colors Summary: Ref T12089. This reverts back to using "bright" colors for full width changes in green and red. Test Plan: Review diffs, see full width colors. {F2333933} Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T12089 Differential Revision: https://secure.phabricator.com/D17171 --- resources/celerity/map.php | 12 ++++++------ .../css/application/differential/changeset-view.css | 10 ++++------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 62b6b48482..0c29ea1053 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -12,7 +12,7 @@ return array( 'core.pkg.css' => '9c725fa0', 'core.pkg.js' => 'a2ead3fe', 'darkconsole.pkg.js' => 'e7393ebb', - 'differential.pkg.css' => 'f69afb45', + 'differential.pkg.css' => 'e0517745', 'differential.pkg.js' => '40b18f35', 'diffusion.pkg.css' => '91c5d3a6', 'diffusion.pkg.js' => '84c8f8fd', @@ -59,7 +59,7 @@ return array( 'rsrc/css/application/dashboard/dashboard.css' => 'bc6f2127', 'rsrc/css/application/diff/inline-comment-summary.css' => '51efda3a', 'rsrc/css/application/differential/add-comment.css' => 'c47f8c40', - 'rsrc/css/application/differential/changeset-view.css' => '11395d9c', + 'rsrc/css/application/differential/changeset-view.css' => 'a5a96310', 'rsrc/css/application/differential/core.css' => '5b7b8ff4', 'rsrc/css/application/differential/phui-inline-comment.css' => '5953c28e', 'rsrc/css/application/differential/revision-comment.css' => '14b8565a', @@ -574,7 +574,7 @@ return array( 'conpherence-thread-manager' => 'c8b5ee6f', 'conpherence-transaction-css' => '85129c68', 'd3' => 'a11a5ff2', - 'differential-changeset-view-css' => '11395d9c', + 'differential-changeset-view-css' => 'a5a96310', 'differential-core-view-css' => '5b7b8ff4', 'differential-inline-comment-editor' => '2e3f9738', 'differential-revision-add-comment-css' => 'c47f8c40', @@ -1032,9 +1032,6 @@ return array( 'javelin-dom', 'javelin-typeahead-normalizer', ), - '11395d9c' => array( - 'phui-inline-comment-view-css', - ), '12884df9' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1759,6 +1756,9 @@ return array( 'javelin-uri', 'phabricator-notification', ), + 'a5a96310' => array( + 'phui-inline-comment-view-css', + ), 'a5c57c24' => array( 'javelin-behavior', 'javelin-stratcom', diff --git a/webroot/rsrc/css/application/differential/changeset-view.css b/webroot/rsrc/css/application/differential/changeset-view.css index ae01add690..60885d2366 100644 --- a/webroot/rsrc/css/application/differential/changeset-view.css +++ b/webroot/rsrc/css/application/differential/changeset-view.css @@ -122,22 +122,18 @@ } .differential-diff th.old { - background: #ffdddd; border-right-color: #f8cbcb; } .differential-diff th.new { - background: #dbffdb; border-right-color: #a6f3a6; } -.differential-diff td.old, -.differential-diff td.old-full { +.differential-diff td.old { background: #ffecec; } -.differential-diff td.new, -.differential-diff td.new-full { +.differential-diff td.new { background: #eaffea; } @@ -150,11 +146,13 @@ } .differential-diff td.old span.bright, +.differential-diff td.old-full, .prose-diff span.old { background: #f8cbcb; } .differential-diff td.new span.bright, +.differential-diff td.new-full, .prose-diff span.new { background: #a6f3a6; } From 52d563f8b8cf2bb7734a69b47a330d198961ddd8 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 10 Jan 2017 13:43:26 -0800 Subject: [PATCH 22/51] Make differential.querydiffs more liberal about arguments Summary: Fixes T12092. D17164 made `DiffQuery` more strict about arguments using modern conventions, but `differential.querydiffs` uses bizarre ancient conventions. Give it more modern conventions instead. Test Plan: Made a `querydiffs` call with only revision IDs. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12092 Differential Revision: https://secure.phabricator.com/D17172 --- ...DifferentialQueryDiffsConduitAPIMethod.php | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/applications/differential/conduit/DifferentialQueryDiffsConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialQueryDiffsConduitAPIMethod.php index 836181d1df..7ff8600f81 100644 --- a/src/applications/differential/conduit/DifferentialQueryDiffsConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialQueryDiffsConduitAPIMethod.php @@ -26,16 +26,27 @@ final class DifferentialQueryDiffsConduitAPIMethod $ids = $request->getValue('ids', array()); $revision_ids = $request->getValue('revisionIDs', array()); - $diffs = array(); - if ($ids || $revision_ids) { - $diffs = id(new DifferentialDiffQuery()) - ->setViewer($request->getUser()) - ->withIDs($ids) - ->withRevisionIDs($revision_ids) - ->needChangesets(true) - ->execute(); + if (!$ids && !$revision_ids) { + // This method just returns nothing if you pass no constraints because + // pagination hadn't been invented yet in 2008 when this method was + // written. + return array(); } + $query = id(new DifferentialDiffQuery()) + ->setViewer($request->getUser()) + ->needChangesets(true); + + if ($ids) { + $query->withIDs($ids); + } + + if ($revision_ids) { + $query->withRevisionIDs($revision_ids); + } + + $diffs = $query->execute(); + return mpull($diffs, 'getDiffDict', 'getID'); } From 1e1a0182cac110ab5a1d07b28577b52c7618cad4 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Tue, 10 Jan 2017 14:45:58 -0800 Subject: [PATCH 23/51] Add basic diff coloring to CelerityDefaultProcessor Summary: Moves basic colors into the processor. Test Plan: Review a diff in sandbox with and without change. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D17173 --- resources/celerity/map.php | 12 ++++++------ .../CelerityDefaultPostprocessor.php | 8 ++++++++ .../application/differential/changeset-view.css | 16 ++++++++-------- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 0c29ea1053..3835e4b2d6 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -12,7 +12,7 @@ return array( 'core.pkg.css' => '9c725fa0', 'core.pkg.js' => 'a2ead3fe', 'darkconsole.pkg.js' => 'e7393ebb', - 'differential.pkg.css' => 'e0517745', + 'differential.pkg.css' => '9535a7e6', 'differential.pkg.js' => '40b18f35', 'diffusion.pkg.css' => '91c5d3a6', 'diffusion.pkg.js' => '84c8f8fd', @@ -59,7 +59,7 @@ return array( 'rsrc/css/application/dashboard/dashboard.css' => 'bc6f2127', 'rsrc/css/application/diff/inline-comment-summary.css' => '51efda3a', 'rsrc/css/application/differential/add-comment.css' => 'c47f8c40', - 'rsrc/css/application/differential/changeset-view.css' => 'a5a96310', + 'rsrc/css/application/differential/changeset-view.css' => 'e1621fd5', 'rsrc/css/application/differential/core.css' => '5b7b8ff4', 'rsrc/css/application/differential/phui-inline-comment.css' => '5953c28e', 'rsrc/css/application/differential/revision-comment.css' => '14b8565a', @@ -574,7 +574,7 @@ return array( 'conpherence-thread-manager' => 'c8b5ee6f', 'conpherence-transaction-css' => '85129c68', 'd3' => 'a11a5ff2', - 'differential-changeset-view-css' => 'a5a96310', + 'differential-changeset-view-css' => 'e1621fd5', 'differential-core-view-css' => '5b7b8ff4', 'differential-inline-comment-editor' => '2e3f9738', 'differential-revision-add-comment-css' => 'c47f8c40', @@ -1756,9 +1756,6 @@ return array( 'javelin-uri', 'phabricator-notification', ), - 'a5a96310' => array( - 'phui-inline-comment-view-css', - ), 'a5c57c24' => array( 'javelin-behavior', 'javelin-stratcom', @@ -2094,6 +2091,9 @@ return array( 'javelin-dom', 'phabricator-prefab', ), + 'e1621fd5' => array( + 'phui-inline-comment-view-css', + ), 'e1d25dfb' => array( 'javelin-behavior', 'javelin-stratcom', diff --git a/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php b/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php index 7b2fed7686..be5bfc14c2 100644 --- a/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php +++ b/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php @@ -192,6 +192,14 @@ final class CelerityDefaultPostprocessor 'sh-disabledtext' => '#a6a6a6', 'sh-disabledbackground' => '#f3f3f3', + // Diffs + 'new-background' => '#eaffea', + 'new-bright' => '#a6f3a6', + 'old-background' => '#ffecec', + 'old-bright' => '#f8cbcb', + 'move-background' => '#fdf5d4', + 'copy-background' => '#f1c40f', + // Background color for "most" themes. 'page.background' => '#f8f8fb', 'page.sidenav' => '#f0f0f2', diff --git a/webroot/rsrc/css/application/differential/changeset-view.css b/webroot/rsrc/css/application/differential/changeset-view.css index 60885d2366..1bd7d48635 100644 --- a/webroot/rsrc/css/application/differential/changeset-view.css +++ b/webroot/rsrc/css/application/differential/changeset-view.css @@ -122,19 +122,19 @@ } .differential-diff th.old { - border-right-color: #f8cbcb; + border-right-color: {$old-bright}; } .differential-diff th.new { - border-right-color: #a6f3a6; + border-right-color: {$new-bright}; } .differential-diff td.old { - background: #ffecec; + background: {$old-background}; } .differential-diff td.new { - background: #eaffea; + background: {$new-background}; } .differential-diff td.old-rebase { @@ -148,13 +148,13 @@ .differential-diff td.old span.bright, .differential-diff td.old-full, .prose-diff span.old { - background: #f8cbcb; + background: {$old-bright}; } .differential-diff td.new span.bright, .differential-diff td.new-full, .prose-diff span.new { - background: #a6f3a6; + background: {$new-bright}; } .differential-diff td.copy { @@ -165,12 +165,12 @@ .differential-diff td.new-copy, .differential-diff td.new-copy span.bright { - background: {$lightyellow}; + background: {$copy-background}; } .differential-diff td.new-move, .differential-diff td.new-move span.bright { - background: {$yellow}; + background: {$move-background}; } .differential-diff td.comment { From 452f5bce18e59693d71b672697529d38d1aa0d06 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Wed, 11 Jan 2017 08:36:30 -0800 Subject: [PATCH 24/51] Make some defaults for Quick Create / Favorites Summary: Add in some basic defaults, Tasks, Projects, Repositories... anything else? Also switches "manage" context if you are an admin or user. Hides link if you are not logged in. Test Plan: Review Global/Personal in Favorites app, click on each link. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D17174 --- .../PhabricatorFavoritesConstants.php | 3 ++ .../PhabricatorFavoritesProfileMenuEngine.php | 44 ++++++++++++++++--- ...bricatorFavoritesManageProfileMenuItem.php | 21 ++++++--- .../editengine/PhabricatorEditEngine.php | 4 ++ 4 files changed, 60 insertions(+), 12 deletions(-) diff --git a/src/applications/favorites/constants/PhabricatorFavoritesConstants.php b/src/applications/favorites/constants/PhabricatorFavoritesConstants.php index 5da0862985..c11a435eef 100644 --- a/src/applications/favorites/constants/PhabricatorFavoritesConstants.php +++ b/src/applications/favorites/constants/PhabricatorFavoritesConstants.php @@ -3,6 +3,9 @@ final class PhabricatorFavoritesConstants extends PhabricatorFavoritesController { + const ITEM_TASK = 'favorites.task'; + const ITEM_PROJECT = 'favorites.project'; + const ITEM_REPOSITORY = 'favorites.repository'; const ITEM_MANAGE = 'favorites.manage'; } diff --git a/src/applications/favorites/engine/PhabricatorFavoritesProfileMenuEngine.php b/src/applications/favorites/engine/PhabricatorFavoritesProfileMenuEngine.php index 114235b76b..a12922a6e8 100644 --- a/src/applications/favorites/engine/PhabricatorFavoritesProfileMenuEngine.php +++ b/src/applications/favorites/engine/PhabricatorFavoritesProfileMenuEngine.php @@ -20,16 +20,50 @@ final class PhabricatorFavoritesProfileMenuEngine protected function getBuiltinProfileItems($object) { $items = array(); + $custom_phid = $this->getCustomPHID(); - $custom = $this->getCustomPHID(); + // Built-in Global Defaults + if (!$custom_phid) { + $create_task = array( + 'name' => null, + 'formKey' => + id(new ManiphestEditEngine())->getProfileMenuItemDefault(), + ); + + $create_project = array( + 'name' => null, + 'formKey' => + id(new PhabricatorProjectEditEngine())->getProfileMenuItemDefault(), + ); + + $create_repository = array( + 'name' => null, + 'formKey' => + id(new DiffusionRepositoryEditEngine())->getProfileMenuItemDefault(), + ); - if ($custom) { $items[] = $this->newItem() - ->setBuiltinKey(PhabricatorFavoritesConstants::ITEM_MANAGE) - ->setMenuItemKey( - PhabricatorFavoritesManageProfileMenuItem::MENUITEMKEY); + ->setBuiltinKey(PhabricatorFavoritesConstants::ITEM_TASK) + ->setMenuItemKey(PhabricatorEditEngineProfileMenuItem::MENUITEMKEY) + ->setMenuItemProperties($create_task); + + $items[] = $this->newItem() + ->setBuiltinKey(PhabricatorFavoritesConstants::ITEM_PROJECT) + ->setMenuItemKey(PhabricatorEditEngineProfileMenuItem::MENUITEMKEY) + ->setMenuItemProperties($create_project); + + $items[] = $this->newItem() + ->setBuiltinKey(PhabricatorFavoritesConstants::ITEM_REPOSITORY) + ->setMenuItemKey(PhabricatorEditEngineProfileMenuItem::MENUITEMKEY) + ->setMenuItemProperties($create_repository); } + // Single Manage Item, switches URI based on admin/user + $items[] = $this->newItem() + ->setBuiltinKey(PhabricatorFavoritesConstants::ITEM_MANAGE) + ->setMenuItemKey( + PhabricatorFavoritesManageProfileMenuItem::MENUITEMKEY); + return $items; } diff --git a/src/applications/favorites/menuitem/PhabricatorFavoritesManageProfileMenuItem.php b/src/applications/favorites/menuitem/PhabricatorFavoritesManageProfileMenuItem.php index 0f0a3d10a6..36847061bf 100644 --- a/src/applications/favorites/menuitem/PhabricatorFavoritesManageProfileMenuItem.php +++ b/src/applications/favorites/menuitem/PhabricatorFavoritesManageProfileMenuItem.php @@ -47,15 +47,22 @@ final class PhabricatorFavoritesManageProfileMenuItem protected function newNavigationMenuItems( PhabricatorProfileMenuItemConfiguration $config) { + $viewer = $this->getViewer(); - $name = $this->getDisplayName($config); - $icon = 'fa-pencil'; - $href = '/favorites/personal/item/configure/'; + if ($viewer->isLoggedIn()) { + $admin = $viewer->getIsAdmin(); + $name = $this->getDisplayName($config); + $icon = 'fa-pencil'; + $href = '/favorites/personal/item/configure/'; + if ($admin) { + $href = '/favorites/'; + } - $item = $this->newItem() - ->setHref($href) - ->setName($name) - ->setIcon($icon); + $item = $this->newItem() + ->setHref($href) + ->setName($name) + ->setIcon($icon); + } return array( $item, diff --git a/src/applications/transactions/editengine/PhabricatorEditEngine.php b/src/applications/transactions/editengine/PhabricatorEditEngine.php index 29c78a4e9f..30e87fa0ad 100644 --- a/src/applications/transactions/editengine/PhabricatorEditEngine.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngine.php @@ -107,6 +107,10 @@ abstract class PhabricatorEditEngine return $this->hideHeader; } + public function getProfileMenuItemDefault() { + return $this->getEngineKey().'/'.self::EDITENGINECONFIG_DEFAULT; + } + /* -( Managing Fields )---------------------------------------------------- */ From 7ff0be1bde2f9d6ddad877a7a8bba97047e82073 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 11 Jan 2017 08:23:19 -0800 Subject: [PATCH 25/51] Bring very basic EditEngine support to commits Summary: Ref T10978. After T11114, we have some features (like the old code for the haunted comment panel) which are only used by Diffusion. I want to modernize it so I can nuke them. T10978 also describes many bugs which are only fixable after modernizing. This adds very basic EditEngine support for commits/audit. You can't create new commits with this workflow, just tag/update existing ones. Test Plan: {F2340347} Reviewers: chad Reviewed By: chad Maniphest Tasks: T10978 Differential Revision: https://secure.phabricator.com/D17175 --- src/__phutil_library_map__.php | 4 ++ .../PhabricatorDiffusionApplication.php | 5 ++ .../DiffusionCommitEditProController.php | 12 ++++ .../editor/DiffusionCommitEditEngine.php | 70 +++++++++++++++++++ 4 files changed, 91 insertions(+) create mode 100644 src/applications/diffusion/controller/DiffusionCommitEditProController.php create mode 100644 src/applications/diffusion/editor/DiffusionCommitEditEngine.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index bd052d18f2..a9c59f23f8 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -624,6 +624,8 @@ phutil_register_library_map(array( 'DiffusionCommitDiffContentRemovedHeraldField' => 'applications/diffusion/herald/DiffusionCommitDiffContentRemovedHeraldField.php', 'DiffusionCommitDiffEnormousHeraldField' => 'applications/diffusion/herald/DiffusionCommitDiffEnormousHeraldField.php', 'DiffusionCommitEditController' => 'applications/diffusion/controller/DiffusionCommitEditController.php', + 'DiffusionCommitEditEngine' => 'applications/diffusion/editor/DiffusionCommitEditEngine.php', + 'DiffusionCommitEditProController' => 'applications/diffusion/controller/DiffusionCommitEditProController.php', 'DiffusionCommitFulltextEngine' => 'applications/repository/search/DiffusionCommitFulltextEngine.php', 'DiffusionCommitHasRevisionEdgeType' => 'applications/diffusion/edge/DiffusionCommitHasRevisionEdgeType.php', 'DiffusionCommitHasRevisionRelationship' => 'applications/diffusion/relationships/DiffusionCommitHasRevisionRelationship.php', @@ -5316,6 +5318,8 @@ phutil_register_library_map(array( 'DiffusionCommitDiffContentRemovedHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitDiffEnormousHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitEditController' => 'DiffusionController', + 'DiffusionCommitEditEngine' => 'PhabricatorEditEngine', + 'DiffusionCommitEditProController' => 'DiffusionController', 'DiffusionCommitFulltextEngine' => 'PhabricatorFulltextEngine', 'DiffusionCommitHasRevisionEdgeType' => 'PhabricatorEdgeType', 'DiffusionCommitHasRevisionRelationship' => 'DiffusionCommitRelationship', diff --git a/src/applications/diffusion/application/PhabricatorDiffusionApplication.php b/src/applications/diffusion/application/PhabricatorDiffusionApplication.php index bcbb5cdbfa..1b86181d86 100644 --- a/src/applications/diffusion/application/PhabricatorDiffusionApplication.php +++ b/src/applications/diffusion/application/PhabricatorDiffusionApplication.php @@ -131,6 +131,11 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication { 'symbol/(?P[^/]+)/' => 'DiffusionSymbolController', 'external/' => 'DiffusionExternalController', 'lint/' => 'DiffusionLintController', + + 'commit/' => array( + $this->getEditRoutePattern('edit/') => + 'DiffusionCommitEditProController', + ), ), ); } diff --git a/src/applications/diffusion/controller/DiffusionCommitEditProController.php b/src/applications/diffusion/controller/DiffusionCommitEditProController.php new file mode 100644 index 0000000000..d13e1751a9 --- /dev/null +++ b/src/applications/diffusion/controller/DiffusionCommitEditProController.php @@ -0,0 +1,12 @@ +setController($this) + ->buildResponse(); + } + +} diff --git a/src/applications/diffusion/editor/DiffusionCommitEditEngine.php b/src/applications/diffusion/editor/DiffusionCommitEditEngine.php new file mode 100644 index 0000000000..95f6f22a05 --- /dev/null +++ b/src/applications/diffusion/editor/DiffusionCommitEditEngine.php @@ -0,0 +1,70 @@ +getDisplayName()); + } + + protected function getObjectEditShortText($object) { + return $object->getDisplayName(); + } + + protected function getObjectName() { + return pht('Commit'); + } + + protected function getObjectViewURI($object) { + return $object->getURI(); + } + + protected function getCreateNewObjectPolicy() { + return PhabricatorPolicies::POLICY_NOONE; + } + + protected function buildCustomEditFields($object) { + $viewer = $this->getViewer(); + + return array(); + } + +} From a27c824da67de2c802ab628f2872b7002026c768 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 11 Jan 2017 10:09:15 -0800 Subject: [PATCH 26/51] Draw project PHIDs from repositories when evaluating Herald object rules for commits Summary: Fixes T12097. In D16413, I simplified this code but caused us to load the //commit's// projects instead of the //repository's// projects, which is incorrect. Normally, commits don't have any project tags when Herald evaluates, so using the commit's projects is generally meaningless. Test Plan: - Tagged a repository with `#X`. - Created a Herald object rule for commits with `#X` as the object ("Always ... do nothing.") - Ran a commit from the repository. - Before patch: rule failed to evaluate. - After patch: rule evaluated and passed. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12097 Differential Revision: https://secure.phabricator.com/D17179 --- .../diffusion/herald/HeraldCommitAdapter.php | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/applications/diffusion/herald/HeraldCommitAdapter.php b/src/applications/diffusion/herald/HeraldCommitAdapter.php index 7b4d26b77c..9d63b9db3d 100644 --- a/src/applications/diffusion/herald/HeraldCommitAdapter.php +++ b/src/applications/diffusion/herald/HeraldCommitAdapter.php @@ -104,12 +104,26 @@ final class HeraldCommitAdapter public function getTriggerObjectPHIDs() { $project_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST; - return array_merge( - array( - $this->getRepository()->getPHID(), - $this->getPHID(), - ), - $this->loadEdgePHIDs($project_type)); + $repository_phid = $this->getRepository()->getPHID(); + $commit_phid = $this->getObject()->getPHID(); + + $phids = array(); + $phids[] = $commit_phid; + $phids[] = $repository_phid; + + // NOTE: This is projects for the repository, not for the commit. When + // Herald evalutes, commits normally can not have any project tags yet. + $repository_project_phids = PhabricatorEdgeQuery::loadDestinationPHIDs( + $repository_phid, + $project_type); + foreach ($repository_project_phids as $phid) { + $phids[] = $phid; + } + + $phids = array_unique($phids); + $phids = array_values($phids); + + return $phids; } public function explainValidTriggerObjects() { From 5e073588268caf97303c52f41f7318864633e1cc Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 11 Jan 2017 09:38:35 -0800 Subject: [PATCH 27/51] Preserve "Autoclose?" information on new Commit edit flow Summary: Ref T10978. The current "Edit" flow has some autoclose info. This isn't necessarily the best place to put it in the long run, but preseve it for now since the documentation refers to it. Test Plan: {F2340658} Reviewers: chad Reviewed By: chad Maniphest Tasks: T10978 Differential Revision: https://secure.phabricator.com/D17176 --- .../editor/DiffusionCommitEditEngine.php | 46 ++++++++++++++++++- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/src/applications/diffusion/editor/DiffusionCommitEditEngine.php b/src/applications/diffusion/editor/DiffusionCommitEditEngine.php index 95f6f22a05..10acd24949 100644 --- a/src/applications/diffusion/editor/DiffusionCommitEditEngine.php +++ b/src/applications/diffusion/editor/DiffusionCommitEditEngine.php @@ -30,7 +30,8 @@ final class DiffusionCommitEditEngine } protected function newObjectQuery() { - return new DiffusionCommitQuery(); + return id(new DiffusionCommitQuery()) + ->needCommitData(true); } protected function getObjectCreateTitleText($object) { @@ -63,8 +64,49 @@ final class DiffusionCommitEditEngine protected function buildCustomEditFields($object) { $viewer = $this->getViewer(); + $data = $object->getCommitData(); - return array(); + $fields = array(); + + $reason = $data->getCommitDetail('autocloseReason', false); + $reason = PhabricatorRepository::BECAUSE_AUTOCLOSE_FORCED; + if ($reason !== false) { + switch ($reason) { + case PhabricatorRepository::BECAUSE_REPOSITORY_IMPORTING: + $desc = pht('No, Repository Importing'); + break; + case PhabricatorRepository::BECAUSE_AUTOCLOSE_DISABLED: + $desc = pht('No, Autoclose Disabled'); + break; + case PhabricatorRepository::BECAUSE_NOT_ON_AUTOCLOSE_BRANCH: + $desc = pht('No, Not On Autoclose Branch'); + break; + case PhabricatorRepository::BECAUSE_AUTOCLOSE_FORCED: + $desc = pht('Yes, Forced Via bin/repository CLI Tool.'); + break; + case null: + $desc = pht('Yes'); + break; + default: + $desc = pht('Unknown'); + break; + } + + $doc_href = PhabricatorEnv::getDoclink('Diffusion User Guide: Autoclose'); + $doc_link = phutil_tag( + 'a', + array( + 'href' => $doc_href, + 'target' => '_blank', + ), + pht('Learn More')); + + $fields[] = id(new PhabricatorStaticEditField()) + ->setLabel(pht('Autoclose?')) + ->setValue(array($desc, " \xC2\xB7 ", $doc_link)); + } + + return $fields; } } From 279273dc1c93ef3a4c9aaf25b5f8d3b5b7211ab1 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 11 Jan 2017 09:43:22 -0800 Subject: [PATCH 28/51] Replace old commit edit controller with new EditEngine controller Summary: Ref T10978. The new controller now does everything the old one did, so swap 'em and nuke the old one. Test Plan: Edited a commit, hit the new controller, things worked real good. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10978 Differential Revision: https://secure.phabricator.com/D17177 --- src/__phutil_library_map__.php | 2 - .../PhabricatorDiffusionApplication.php | 4 +- .../controller/DiffusionCommitController.php | 6 +- .../DiffusionCommitEditController.php | 118 +----------------- .../DiffusionCommitEditProController.php | 12 -- 5 files changed, 9 insertions(+), 133 deletions(-) delete mode 100644 src/applications/diffusion/controller/DiffusionCommitEditProController.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index a9c59f23f8..5ebfc76a44 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -625,7 +625,6 @@ phutil_register_library_map(array( 'DiffusionCommitDiffEnormousHeraldField' => 'applications/diffusion/herald/DiffusionCommitDiffEnormousHeraldField.php', 'DiffusionCommitEditController' => 'applications/diffusion/controller/DiffusionCommitEditController.php', 'DiffusionCommitEditEngine' => 'applications/diffusion/editor/DiffusionCommitEditEngine.php', - 'DiffusionCommitEditProController' => 'applications/diffusion/controller/DiffusionCommitEditProController.php', 'DiffusionCommitFulltextEngine' => 'applications/repository/search/DiffusionCommitFulltextEngine.php', 'DiffusionCommitHasRevisionEdgeType' => 'applications/diffusion/edge/DiffusionCommitHasRevisionEdgeType.php', 'DiffusionCommitHasRevisionRelationship' => 'applications/diffusion/relationships/DiffusionCommitHasRevisionRelationship.php', @@ -5319,7 +5318,6 @@ phutil_register_library_map(array( 'DiffusionCommitDiffEnormousHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitEditController' => 'DiffusionController', 'DiffusionCommitEditEngine' => 'PhabricatorEditEngine', - 'DiffusionCommitEditProController' => 'DiffusionController', 'DiffusionCommitFulltextEngine' => 'PhabricatorFulltextEngine', 'DiffusionCommitHasRevisionEdgeType' => 'PhabricatorEdgeType', 'DiffusionCommitHasRevisionRelationship' => 'DiffusionCommitRelationship', diff --git a/src/applications/diffusion/application/PhabricatorDiffusionApplication.php b/src/applications/diffusion/application/PhabricatorDiffusionApplication.php index 1b86181d86..0e2b669eed 100644 --- a/src/applications/diffusion/application/PhabricatorDiffusionApplication.php +++ b/src/applications/diffusion/application/PhabricatorDiffusionApplication.php @@ -63,8 +63,6 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication { => 'DiffusionCommitBranchesController', 'commit/(?P[a-z0-9]+)/tags/' => 'DiffusionCommitTagsController', - 'commit/(?P[a-z0-9]+)/edit/' - => 'DiffusionCommitEditController', 'compare/' => 'DiffusionCompareController', 'manage/(?:(?P[^/]+)/)?' => 'DiffusionRepositoryManagePanelsController', @@ -134,7 +132,7 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication { 'commit/' => array( $this->getEditRoutePattern('edit/') => - 'DiffusionCommitEditProController', + 'DiffusionCommitEditController', ), ), ); diff --git a/src/applications/diffusion/controller/DiffusionCommitController.php b/src/applications/diffusion/controller/DiffusionCommitController.php index 3ec28d31a5..6da285aedc 100644 --- a/src/applications/diffusion/controller/DiffusionCommitController.php +++ b/src/applications/diffusion/controller/DiffusionCommitController.php @@ -997,12 +997,12 @@ final class DiffusionCommitController extends DiffusionController { $commit, PhabricatorPolicyCapability::CAN_EDIT); - $identifier = $commit->getCommitIdentifier(); - $uri = $repository->getPathURI("commit/{$identifier}/edit/"); + $id = $commit->getID(); + $edit_uri = $this->getApplicationURI("/commit/edit/{$id}/"); $action = id(new PhabricatorActionView()) ->setName(pht('Edit Commit')) - ->setHref($uri) + ->setHref($edit_uri) ->setIcon('fa-pencil') ->setDisabled(!$can_edit) ->setWorkflow(!$can_edit); diff --git a/src/applications/diffusion/controller/DiffusionCommitEditController.php b/src/applications/diffusion/controller/DiffusionCommitEditController.php index 895eda4c6d..cc1d1e1c59 100644 --- a/src/applications/diffusion/controller/DiffusionCommitEditController.php +++ b/src/applications/diffusion/controller/DiffusionCommitEditController.php @@ -1,120 +1,12 @@ loadDiffusionContext(); - if ($response) { - return $response; - } - - $viewer = $this->getViewer(); - $drequest = $this->getDiffusionRequest(); - $repository = $drequest->getRepository(); - $commit = $drequest->loadCommit(); - - if (!$commit) { - return new Aphront404Response(); - } - - $data = $commit->loadCommitData(); - $page_title = pht('Edit Diffusion Commit'); - - $commit_phid = $commit->getPHID(); - $edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST; - $current_proj_phids = PhabricatorEdgeQuery::loadDestinationPHIDs( - $commit_phid, - $edge_type); - - if ($request->isFormPost()) { - $xactions = array(); - $proj_phids = $request->getArr('projects'); - $xactions[] = id(new PhabricatorAuditTransaction()) - ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) - ->setMetadataValue('edge:type', $edge_type) - ->setNewValue(array('=' => array_fuse($proj_phids))); - - $editor = id(new PhabricatorAuditEditor()) - ->setActor($viewer) - ->setContinueOnNoEffect(true) - ->setContentSourceFromRequest($request); - - $editor->applyTransactions($commit, $xactions); - - return id(new AphrontRedirectResponse()) - ->setURI($commit->getURI()); - } - - $tokenizer_id = celerity_generate_unique_node_id(); - $form = id(new AphrontFormView()) - ->setUser($viewer) - ->setAction($request->getRequestURI()->getPath()) - ->appendControl( - id(new AphrontFormTokenizerControl()) - ->setLabel(pht('Projects')) - ->setName('projects') - ->setValue($current_proj_phids) - ->setID($tokenizer_id) - ->setDatasource(new PhabricatorProjectDatasource())); - - $reason = $data->getCommitDetail('autocloseReason', false); - $reason = PhabricatorRepository::BECAUSE_AUTOCLOSE_FORCED; - if ($reason !== false) { - switch ($reason) { - case PhabricatorRepository::BECAUSE_REPOSITORY_IMPORTING: - $desc = pht('No, Repository Importing'); - break; - case PhabricatorRepository::BECAUSE_AUTOCLOSE_DISABLED: - $desc = pht('No, Autoclose Disabled'); - break; - case PhabricatorRepository::BECAUSE_NOT_ON_AUTOCLOSE_BRANCH: - $desc = pht('No, Not On Autoclose Branch'); - break; - case PhabricatorRepository::BECAUSE_AUTOCLOSE_FORCED: - $desc = pht('Yes, Forced Via bin/repository CLI Tool.'); - break; - case null: - $desc = pht('Yes'); - break; - default: - $desc = pht('Unknown'); - break; - } - - $doc_href = PhabricatorEnv::getDoclink('Diffusion User Guide: Autoclose'); - $doc_link = phutil_tag( - 'a', - array( - 'href' => $doc_href, - 'target' => '_blank', - ), - pht('Learn More')); - - $form->appendChild( - id(new AphrontFormMarkupControl()) - ->setLabel(pht('Autoclose?')) - ->setValue(array($desc, " \xC2\xB7 ", $doc_link))); - } - - $form->appendControl( - id(new AphrontFormSubmitControl()) - ->setValue(pht('Save')) - ->addCancelButton($commit->getURI())); - - $crumbs = $this->buildCrumbs( - array( - 'commit' => true, - )); - $crumbs->addTextCrumb(pht('Edit')); - - $form_box = id(new PHUIObjectBoxView()) - ->setHeaderText($page_title) - ->setForm($form); - - return $this->newPage() - ->setTitle($page_title) - ->setCrumbs($crumbs) - ->appendChild($form_box); + return id(new DiffusionCommitEditEngine()) + ->setController($this) + ->buildResponse(); } } diff --git a/src/applications/diffusion/controller/DiffusionCommitEditProController.php b/src/applications/diffusion/controller/DiffusionCommitEditProController.php deleted file mode 100644 index d13e1751a9..0000000000 --- a/src/applications/diffusion/controller/DiffusionCommitEditProController.php +++ /dev/null @@ -1,12 +0,0 @@ -setController($this) - ->buildResponse(); - } - -} From 2941b34acbbaaa0b12a1e9911fa6ec07821390af Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 11 Jan 2017 09:45:37 -0800 Subject: [PATCH 29/51] Add "diffusion.commit.edit", a v3 edit API endpoint for commits Summary: Ref T10978. This currently does almost nothing, but gets it in place so I can add stuff to it. Test Plan: Made a comment on a commit using the API. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10978 Differential Revision: https://secure.phabricator.com/D17178 --- src/__phutil_library_map__.php | 2 ++ .../DiffusionCommitEditConduitAPIMethod.php | 20 +++++++++++++++++++ .../editor/DiffusionCommitEditEngine.php | 11 +++++++++- 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 src/applications/diffusion/conduit/DiffusionCommitEditConduitAPIMethod.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 5ebfc76a44..58383fd8d4 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -623,6 +623,7 @@ phutil_register_library_map(array( 'DiffusionCommitDiffContentHeraldField' => 'applications/diffusion/herald/DiffusionCommitDiffContentHeraldField.php', 'DiffusionCommitDiffContentRemovedHeraldField' => 'applications/diffusion/herald/DiffusionCommitDiffContentRemovedHeraldField.php', 'DiffusionCommitDiffEnormousHeraldField' => 'applications/diffusion/herald/DiffusionCommitDiffEnormousHeraldField.php', + 'DiffusionCommitEditConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionCommitEditConduitAPIMethod.php', 'DiffusionCommitEditController' => 'applications/diffusion/controller/DiffusionCommitEditController.php', 'DiffusionCommitEditEngine' => 'applications/diffusion/editor/DiffusionCommitEditEngine.php', 'DiffusionCommitFulltextEngine' => 'applications/repository/search/DiffusionCommitFulltextEngine.php', @@ -5316,6 +5317,7 @@ phutil_register_library_map(array( 'DiffusionCommitDiffContentHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitDiffContentRemovedHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitDiffEnormousHeraldField' => 'DiffusionCommitHeraldField', + 'DiffusionCommitEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod', 'DiffusionCommitEditController' => 'DiffusionController', 'DiffusionCommitEditEngine' => 'PhabricatorEditEngine', 'DiffusionCommitFulltextEngine' => 'PhabricatorFulltextEngine', diff --git a/src/applications/diffusion/conduit/DiffusionCommitEditConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionCommitEditConduitAPIMethod.php new file mode 100644 index 0000000000..442784a366 --- /dev/null +++ b/src/applications/diffusion/conduit/DiffusionCommitEditConduitAPIMethod.php @@ -0,0 +1,20 @@ +attachRepository($repository) + ->attachCommitData($data); } protected function newObjectQuery() { From dfee1352e98aa6325a5daee30052aea1293e7bbc Mon Sep 17 00:00:00 2001 From: Chad Little Date: Wed, 11 Jan 2017 12:35:16 -0800 Subject: [PATCH 30/51] Basic structure for MenuItem on Home Summary: Ref T11957, builds out `/home/menu/` as a basic structure for adding/editing the homepage menu. Test Plan: visit `/home/menu/` and add items to global and personal. Not wired to anything. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T11957 Differential Revision: https://secure.phabricator.com/D17180 --- src/__phutil_library_map__.php | 10 +++ .../PhabricatorHomeApplication.php | 5 ++ .../constants/PhabricatorHomeConstants.php | 9 +++ .../PhabricatorHomeMenuController.php | 57 +++++++++++++++ .../PhabricatorHomeMenuItemController.php | 29 ++++++++ .../PhabricatorHomeProfileMenuEngine.php | 58 +++++++++++++++ .../PhabricatorHomeManageProfileMenuItem.php | 72 +++++++++++++++++++ 7 files changed, 240 insertions(+) create mode 100644 src/applications/home/constants/PhabricatorHomeConstants.php create mode 100644 src/applications/home/controller/PhabricatorHomeMenuController.php create mode 100644 src/applications/home/controller/PhabricatorHomeMenuItemController.php create mode 100644 src/applications/home/engine/PhabricatorHomeProfileMenuEngine.php create mode 100644 src/applications/home/menuitem/PhabricatorHomeManageProfileMenuItem.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 58383fd8d4..05e2a6473e 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2820,9 +2820,14 @@ phutil_register_library_map(array( 'PhabricatorHeraldContentSource' => 'applications/herald/contentsource/PhabricatorHeraldContentSource.php', 'PhabricatorHighSecurityRequestExceptionHandler' => 'aphront/handler/PhabricatorHighSecurityRequestExceptionHandler.php', 'PhabricatorHomeApplication' => 'applications/home/application/PhabricatorHomeApplication.php', + 'PhabricatorHomeConstants' => 'applications/home/constants/PhabricatorHomeConstants.php', 'PhabricatorHomeController' => 'applications/home/controller/PhabricatorHomeController.php', 'PhabricatorHomeMainController' => 'applications/home/controller/PhabricatorHomeMainController.php', + 'PhabricatorHomeManageProfileMenuItem' => 'applications/home/menuitem/PhabricatorHomeManageProfileMenuItem.php', + 'PhabricatorHomeMenuController' => 'applications/home/controller/PhabricatorHomeMenuController.php', + 'PhabricatorHomeMenuItemController' => 'applications/home/controller/PhabricatorHomeMenuItemController.php', 'PhabricatorHomePreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorHomePreferencesSettingsPanel.php', + 'PhabricatorHomeProfileMenuEngine' => 'applications/home/engine/PhabricatorHomeProfileMenuEngine.php', 'PhabricatorHomeQuickCreateController' => 'applications/home/controller/PhabricatorHomeQuickCreateController.php', 'PhabricatorHovercardEngineExtension' => 'applications/search/engineextension/PhabricatorHovercardEngineExtension.php', 'PhabricatorHovercardEngineExtensionModule' => 'applications/search/engineextension/PhabricatorHovercardEngineExtensionModule.php', @@ -7861,9 +7866,14 @@ phutil_register_library_map(array( 'PhabricatorHeraldContentSource' => 'PhabricatorContentSource', 'PhabricatorHighSecurityRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler', 'PhabricatorHomeApplication' => 'PhabricatorApplication', + 'PhabricatorHomeConstants' => 'PhabricatorHomeController', 'PhabricatorHomeController' => 'PhabricatorController', 'PhabricatorHomeMainController' => 'PhabricatorHomeController', + 'PhabricatorHomeManageProfileMenuItem' => 'PhabricatorProfileMenuItem', + 'PhabricatorHomeMenuController' => 'PhabricatorHomeController', + 'PhabricatorHomeMenuItemController' => 'PhabricatorHomeController', 'PhabricatorHomePreferencesSettingsPanel' => 'PhabricatorSettingsPanel', + 'PhabricatorHomeProfileMenuEngine' => 'PhabricatorProfileMenuEngine', 'PhabricatorHomeQuickCreateController' => 'PhabricatorHomeController', 'PhabricatorHovercardEngineExtension' => 'Phobject', 'PhabricatorHovercardEngineExtensionModule' => 'PhabricatorConfigModule', diff --git a/src/applications/home/application/PhabricatorHomeApplication.php b/src/applications/home/application/PhabricatorHomeApplication.php index 9b31ee537f..8d1b97a797 100644 --- a/src/applications/home/application/PhabricatorHomeApplication.php +++ b/src/applications/home/application/PhabricatorHomeApplication.php @@ -27,6 +27,11 @@ final class PhabricatorHomeApplication extends PhabricatorApplication { '/(?Phome)/' => 'PhabricatorHomeMainController', '/home/' => array( 'create/' => 'PhabricatorHomeQuickCreateController', + 'menu/' => array( + '' => 'PhabricatorHomeMenuController', + '(?Pglobal|personal)/item/' => $this->getProfileMenuRouting( + 'PhabricatorHomeMenuItemController'), + ), ), ); } diff --git a/src/applications/home/constants/PhabricatorHomeConstants.php b/src/applications/home/constants/PhabricatorHomeConstants.php new file mode 100644 index 0000000000..d1613acf58 --- /dev/null +++ b/src/applications/home/constants/PhabricatorHomeConstants.php @@ -0,0 +1,9 @@ +getViewer(); + + $menu = id(new PHUIObjectItemListView()) + ->setUser($viewer); + + $menu->addItem( + id(new PHUIObjectItemView()) + ->setHeader(pht('Personal Menu Items')) + ->setHref($this->getApplicationURI('menu/personal/item/configure/')) + ->setImageURI($viewer->getProfileImageURI()) + ->addAttribute(pht('Edit the menu for your personal account.'))); + + $icon = id(new PHUIIconView()) + ->setIcon('fa-globe') + ->setBackground('bg-blue'); + + $menu->addItem( + id(new PHUIObjectItemView()) + ->setHeader(pht('Global Menu Items')) + ->setHref($this->getApplicationURI('menu/global/item/configure/')) + ->setImageIcon($icon) + ->addAttribute(pht('Edit the global default menu for all users.'))); + + $crumbs = $this->buildApplicationCrumbs(); + $crumbs->addTextCrumb(pht('Manage')); + $crumbs->setBorder(true); + + $box = id(new PHUIObjectBoxView()) + ->setObjectList($menu); + + $header = id(new PHUIHeaderView()) + ->setHeader(pht('Manage Home Menu')) + ->setHeaderIcon('fa-home'); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter(array( + $box, + )); + + return $this->newPage() + ->setTitle(pht('Manage Home Menu')) + ->setCrumbs($crumbs) + ->appendChild($view); + + } + +} diff --git a/src/applications/home/controller/PhabricatorHomeMenuItemController.php b/src/applications/home/controller/PhabricatorHomeMenuItemController.php new file mode 100644 index 0000000000..51842dd881 --- /dev/null +++ b/src/applications/home/controller/PhabricatorHomeMenuItemController.php @@ -0,0 +1,29 @@ +getViewer(); + $type = $request->getURIData('type'); + $custom_phid = null; + if ($type == 'personal') { + $custom_phid = $viewer->getPHID(); + } + + $application = 'PhabricatorHomeApplication'; + $home_app = id(new PhabricatorApplicationQuery()) + ->setViewer($viewer) + ->withClasses(array($application)) + ->withInstalled(true) + ->executeOne(); + + $engine = id(new PhabricatorHomeProfileMenuEngine()) + ->setProfileObject($home_app) + ->setCustomPHID($custom_phid) + ->setController($this); + + return $engine->buildResponse(); + } + +} diff --git a/src/applications/home/engine/PhabricatorHomeProfileMenuEngine.php b/src/applications/home/engine/PhabricatorHomeProfileMenuEngine.php new file mode 100644 index 0000000000..ebcc92b189 --- /dev/null +++ b/src/applications/home/engine/PhabricatorHomeProfileMenuEngine.php @@ -0,0 +1,58 @@ +getProfileObject(); + $custom = $this->getCustomPHID(); + + if ($custom) { + return "/home/menu/personal/item/{$path}"; + } else { + return "/home/menu/global/item/{$path}"; + } + } + + protected function getBuiltinProfileItems($object) { + $viewer = $this->getViewer(); + $items = array(); + $custom_phid = $this->getCustomPHID(); + + $applications = id(new PhabricatorApplicationQuery()) + ->setViewer($viewer) + ->withInstalled(true) + ->withUnlisted(false) + ->withLaunchable(true) + ->execute(); + + foreach ($applications as $application) { + if (!$application->isPinnedByDefault($viewer)) { + continue; + } + + $properties = array( + 'name' => $application->getName(), + 'application' => $application->getPHID(), + ); + + $items[] = $this->newItem() + ->setBuiltinKey($application->getPHID()) + ->setMenuItemKey(PhabricatorApplicationProfileMenuItem::MENUITEMKEY) + ->setMenuItemProperties($properties); + } + + // Single Manage Item, switches URI based on admin/user + $items[] = $this->newItem() + ->setBuiltinKey(PhabricatorHomeConstants::ITEM_MANAGE) + ->setMenuItemKey( + PhabricatorHomeManageProfileMenuItem::MENUITEMKEY); + + return $items; + } + +} diff --git a/src/applications/home/menuitem/PhabricatorHomeManageProfileMenuItem.php b/src/applications/home/menuitem/PhabricatorHomeManageProfileMenuItem.php new file mode 100644 index 0000000000..514cd8e026 --- /dev/null +++ b/src/applications/home/menuitem/PhabricatorHomeManageProfileMenuItem.php @@ -0,0 +1,72 @@ +getMenuItemProperty('name'); + + if (strlen($name)) { + return $name; + } + + return $this->getDefaultName(); + } + + public function buildEditEngineFields( + PhabricatorProfileMenuItemConfiguration $config) { + return array( + id(new PhabricatorTextEditField()) + ->setKey('name') + ->setLabel(pht('Name')) + ->setPlaceholder($this->getDefaultName()) + ->setValue($config->getMenuItemProperty('name')), + ); + } + + protected function newNavigationMenuItems( + PhabricatorProfileMenuItemConfiguration $config) { + $viewer = $this->getViewer(); + + if ($viewer->isLoggedIn()) { + $admin = $viewer->getIsAdmin(); + $name = $this->getDisplayName($config); + $icon = 'fa-pencil'; + $href = '/home/menu/personal/item/configure/'; + if ($admin) { + $href = '/home/menu/'; + } + + $item = $this->newItem() + ->setHref($href) + ->setName($name) + ->setIcon($icon); + } + + return array( + $item, + ); + } + +} From 255e3fb1e4bfa744effa7aded0e63a7ac9866166 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 11 Jan 2017 10:49:39 -0800 Subject: [PATCH 31/51] Allow auditors to be added and removed from commits in a modern way Summary: Ref T10978. Ref T7676. Make auditors work more like reviewers, so they can be freely added or removed. Test Plan: - Interacted with auditors via "Edit Commit" and API. - Comment area is still oldschool and doesn't work yet. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10978, T7676 Differential Revision: https://secure.phabricator.com/D17181 --- src/__phutil_library_map__.php | 6 +- .../storage/PhabricatorAuditTransaction.php | 6 +- .../editor/DiffusionCommitEditEngine.php | 19 +- .../DiffusionCommitAuditorsTransaction.php | 231 ++++++++++++++++++ .../DiffusionCommitTransactionType.php | 4 + .../storage/PhabricatorRepositoryCommit.php | 5 + 6 files changed, 267 insertions(+), 4 deletions(-) create mode 100644 src/applications/diffusion/xaction/DiffusionCommitAuditorsTransaction.php create mode 100644 src/applications/diffusion/xaction/DiffusionCommitTransactionType.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 05e2a6473e..a260855071 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -613,6 +613,7 @@ phutil_register_library_map(array( 'DiffusionCommandEngine' => 'applications/diffusion/protocol/DiffusionCommandEngine.php', 'DiffusionCommandEngineTestCase' => 'applications/diffusion/protocol/__tests__/DiffusionCommandEngineTestCase.php', 'DiffusionCommitAffectedFilesHeraldField' => 'applications/diffusion/herald/DiffusionCommitAffectedFilesHeraldField.php', + 'DiffusionCommitAuditorsTransaction' => 'applications/diffusion/xaction/DiffusionCommitAuditorsTransaction.php', 'DiffusionCommitAuthorHeraldField' => 'applications/diffusion/herald/DiffusionCommitAuthorHeraldField.php', 'DiffusionCommitAutocloseHeraldField' => 'applications/diffusion/herald/DiffusionCommitAutocloseHeraldField.php', 'DiffusionCommitBranchesController' => 'applications/diffusion/controller/DiffusionCommitBranchesController.php', @@ -659,6 +660,7 @@ phutil_register_library_map(array( 'DiffusionCommitRevisionReviewersHeraldField' => 'applications/diffusion/herald/DiffusionCommitRevisionReviewersHeraldField.php', 'DiffusionCommitRevisionSubscribersHeraldField' => 'applications/diffusion/herald/DiffusionCommitRevisionSubscribersHeraldField.php', 'DiffusionCommitTagsController' => 'applications/diffusion/controller/DiffusionCommitTagsController.php', + 'DiffusionCommitTransactionType' => 'applications/diffusion/xaction/DiffusionCommitTransactionType.php', 'DiffusionCompareController' => 'applications/diffusion/controller/DiffusionCompareController.php', 'DiffusionConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionConduitAPIMethod.php', 'DiffusionController' => 'applications/diffusion/controller/DiffusionController.php', @@ -5312,6 +5314,7 @@ phutil_register_library_map(array( 'DiffusionCommandEngine' => 'Phobject', 'DiffusionCommandEngineTestCase' => 'PhabricatorTestCase', 'DiffusionCommitAffectedFilesHeraldField' => 'DiffusionCommitHeraldField', + 'DiffusionCommitAuditorsTransaction' => 'DiffusionCommitTransactionType', 'DiffusionCommitAuthorHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitAutocloseHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitBranchesController' => 'DiffusionController', @@ -5358,6 +5361,7 @@ phutil_register_library_map(array( 'DiffusionCommitRevisionReviewersHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitRevisionSubscribersHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitTagsController' => 'DiffusionController', + 'DiffusionCommitTransactionType' => 'PhabricatorModularTransactionType', 'DiffusionCompareController' => 'DiffusionController', 'DiffusionConduitAPIMethod' => 'ConduitAPIMethod', 'DiffusionController' => 'PhabricatorController', @@ -6761,7 +6765,7 @@ phutil_register_library_map(array( 'PhabricatorAuditPreviewController' => 'PhabricatorAuditController', 'PhabricatorAuditReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler', 'PhabricatorAuditStatusConstants' => 'Phobject', - 'PhabricatorAuditTransaction' => 'PhabricatorApplicationTransaction', + 'PhabricatorAuditTransaction' => 'PhabricatorModularTransaction', 'PhabricatorAuditTransactionComment' => 'PhabricatorApplicationTransactionComment', 'PhabricatorAuditTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhabricatorAuditTransactionView' => 'PhabricatorApplicationTransactionView', diff --git a/src/applications/audit/storage/PhabricatorAuditTransaction.php b/src/applications/audit/storage/PhabricatorAuditTransaction.php index e1f7a9d08a..1ee1833009 100644 --- a/src/applications/audit/storage/PhabricatorAuditTransaction.php +++ b/src/applications/audit/storage/PhabricatorAuditTransaction.php @@ -1,7 +1,7 @@ attachRepository($repository) - ->attachCommitData($data); + ->attachCommitData($data) + ->attachAudits(array()); } protected function newObjectQuery() { return id(new DiffusionCommitQuery()) - ->needCommitData(true); + ->needCommitData(true) + ->needAuditRequests(true); } protected function getObjectCreateTitleText($object) { @@ -77,6 +79,19 @@ final class DiffusionCommitEditEngine $fields = array(); + $fields[] = id(new PhabricatorDatasourceEditField()) + ->setKey('auditors') + ->setLabel(pht('Auditors')) + ->setDatasource(new DiffusionAuditorDatasource()) + ->setUseEdgeTransactions(true) + ->setTransactionType( + DiffusionCommitAuditorsTransaction::TRANSACTIONTYPE) + ->setCommentActionLabel(pht('Change Auditors')) + ->setDescription(pht('Auditors for this commit.')) + ->setConduitDescription(pht('Change the auditors for this commit.')) + ->setConduitTypeDescription(pht('New auditors.')) + ->setValue($object->getAuditorPHIDsForEdit()); + $reason = $data->getCommitDetail('autocloseReason', false); $reason = PhabricatorRepository::BECAUSE_AUTOCLOSE_FORCED; if ($reason !== false) { diff --git a/src/applications/diffusion/xaction/DiffusionCommitAuditorsTransaction.php b/src/applications/diffusion/xaction/DiffusionCommitAuditorsTransaction.php new file mode 100644 index 0000000000..a9ea6655c1 --- /dev/null +++ b/src/applications/diffusion/xaction/DiffusionCommitAuditorsTransaction.php @@ -0,0 +1,231 @@ +getAudits(); + return mpull($auditors, 'getAuditStatus', 'getAuditorPHID'); + } + + public function generateNewValue($object, $value) { + $actor = $this->getActor(); + + $auditors = $this->generateOldValue($object); + $old_auditors = $auditors; + + $request_status = PhabricatorAuditStatusConstants::AUDIT_REQUESTED; + + $rem = idx($value, '-', array()); + foreach ($rem as $phid) { + unset($auditors[$phid]); + } + + $add = idx($value, '+', array()); + $add_map = array(); + foreach ($add as $phid) { + $add_map[$phid] = $request_status; + } + + $set = idx($value, '=', null); + if ($set !== null) { + foreach ($set as $phid) { + $add_map[$phid] = $request_status; + } + + $auditors = array(); + } + + foreach ($add_map as $phid => $new_status) { + $old_status = idx($old_auditors, $phid); + + if ($old_status) { + $auditors[$phid] = $old_status; + continue; + } + + $auditors[$phid] = $new_status; + } + + return $auditors; + } + + public function getTransactionHasEffect($object, $old, $new) { + ksort($old); + ksort($new); + return ($old !== $new); + } + + public function applyExternalEffects($object, $value) { + $src_phid = $object->getPHID(); + + $old = $this->generateOldValue($object); + $new = $value; + + $auditors = $object->getAudits(); + $auditors = mpull($auditors, null, 'getAuditorPHID'); + + $rem = array_diff_key($old, $new); + foreach ($rem as $phid => $status) { + $auditor = idx($auditors, $phid); + if ($auditor) { + $auditor->delete(); + } + } + + foreach ($new as $phid => $status) { + $auditor = idx($auditors, $phid); + if (!$auditor) { + $auditor = id(new PhabricatorRepositoryAuditRequest()) + ->setAuditorPHID($phid) + ->setCommitPHID($object->getPHID()); + } else { + if ($auditor->getAuditStatus() === $status) { + continue; + } + } + + $auditor + ->setAuditStatus($status) + ->save(); + } + } + + public function getTitle() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + $rem = array_diff_key($old, $new); + $add = array_diff_key($new, $old); + $rem_phids = array_keys($rem); + $add_phids = array_keys($add); + $total_count = count($rem) + count($add); + + if ($rem && $add) { + return pht( + '%s edited %s auditor(s), removed %s: %s; added %s: %s.', + $this->renderAuthor(), + new PhutilNumber($total_count), + phutil_count($rem_phids), + $this->renderHandleList($rem_phids), + phutil_count($add_phids), + $this->renderHandleList($add_phids)); + } else if ($add) { + return pht( + '%s added %s auditor(s): %s.', + $this->renderAuthor(), + phutil_count($add_phids), + $this->renderHandleList($add_phids)); + } else { + return pht( + '%s removed %s auditor(s): %s.', + $this->renderAuthor(), + phutil_count($rem_phids), + $this->renderHandleList($rem_phids)); + } + } + + public function getTitleForFeed() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + $rem = array_diff_key($old, $new); + $add = array_diff_key($new, $old); + $rem_phids = array_keys($rem); + $add_phids = array_keys($add); + $total_count = count($rem) + count($add); + + if ($rem && $add) { + return pht( + '%s edited %s auditor(s) for %s, removed %s: %s; added %s: %s.', + $this->renderAuthor(), + new PhutilNumber($total_count), + $this->renderObject(), + phutil_count($rem_phids), + $this->renderHandleList($rem_phids), + phutil_count($add_phids), + $this->renderHandleList($add_phids)); + } else if ($add) { + return pht( + '%s added %s auditor(s) for %s: %s.', + $this->renderAuthor(), + phutil_count($add_phids), + $this->renderObject(), + $this->renderHandleList($add_phids)); + } else { + return pht( + '%s removed %s auditor(s) for %s: %s.', + $this->renderAuthor(), + phutil_count($rem_phids), + $this->renderObject(), + $this->renderHandleList($rem_phids)); + } + } + + public function validateTransactions($object, array $xactions) { + $actor = $this->getActor(); + $errors = array(); + + if (!$xactions) { + return $errors; + } + + $author_phid = $object->getAuthorPHID(); + $can_author_close_key = 'audit.can-author-close-audit'; + $can_author_close = PhabricatorEnv::getEnvConfig($can_author_close_key); + + $old = $this->generateOldValue($object); + foreach ($xactions as $xaction) { + $new = $this->generateNewValue($object, $xaction->getNewValue()); + + $add = array_diff_key($new, $old); + if (!$add) { + continue; + } + + $objects = id(new PhabricatorObjectQuery()) + ->setViewer($actor) + ->withPHIDs(array_keys($add)) + ->execute(); + $objects = mpull($objects, null, 'getPHID'); + + foreach ($add as $phid => $status) { + if (!isset($objects[$phid])) { + $errors[] = $this->newInvalidError( + pht( + 'Auditor "%s" is not a valid object.', + $phid), + $xaction); + continue; + } + + switch (phid_get_type($phid)) { + case PhabricatorPeopleUserPHIDType::TYPECONST: + case PhabricatorOwnersPackagePHIDType::TYPECONST: + case PhabricatorProjectProjectPHIDType::TYPECONST: + break; + default: + $errors[] = $this->newInvalidError( + pht( + 'Auditor "%s" must be a user, a package, or a project.', + $phid), + $xaction); + continue 2; + } + + $is_self = ($phid === $author_phid); + if ($is_self && !$can_author_close) { + $errors[] = $this->newInvalidError( + pht('The author of a commit can not be an auditor.'), + $xaction); + continue; + } + } + } + + return $errors; + } + +} diff --git a/src/applications/diffusion/xaction/DiffusionCommitTransactionType.php b/src/applications/diffusion/xaction/DiffusionCommitTransactionType.php new file mode 100644 index 0000000000..200a675089 --- /dev/null +++ b/src/applications/diffusion/xaction/DiffusionCommitTransactionType.php @@ -0,0 +1,4 @@ +getAudits(); + return mpull($audits, 'getAuditorPHID'); + } + public function save() { if (!$this->mailKey) { $this->mailKey = Filesystem::readRandomCharacters(20); From 82c891f58663239f762722c570368e0f2890b83d Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 11 Jan 2017 12:05:31 -0800 Subject: [PATCH 32/51] Add modern "Accept", "Raise Concern" and "Resign" transactions to Audit Summary: Ref T10978. This prepares for swapping the comment UI to stacked actions. These are only accessible via the API. Test Plan: Used the API to accept, raise concern with, and reject commits. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10978 Differential Revision: https://secure.phabricator.com/D17182 --- src/__phutil_library_map__.php | 10 ++ .../editor/DiffusionCommitEditEngine.php | 10 ++ .../DiffusionCommitAcceptTransaction.php | 74 +++++++++++ .../DiffusionCommitActionTransaction.php | 118 ++++++++++++++++++ .../DiffusionCommitAuditTransaction.php | 101 +++++++++++++++ .../DiffusionCommitAuditorsTransaction.php | 17 +-- .../DiffusionCommitConcernTransaction.php | 70 +++++++++++ .../DiffusionCommitResignTransaction.php | 62 +++++++++ .../DiffusionCommitTransactionType.php | 35 +++++- 9 files changed, 480 insertions(+), 17 deletions(-) create mode 100644 src/applications/diffusion/xaction/DiffusionCommitAcceptTransaction.php create mode 100644 src/applications/diffusion/xaction/DiffusionCommitActionTransaction.php create mode 100644 src/applications/diffusion/xaction/DiffusionCommitAuditTransaction.php create mode 100644 src/applications/diffusion/xaction/DiffusionCommitConcernTransaction.php create mode 100644 src/applications/diffusion/xaction/DiffusionCommitResignTransaction.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index a260855071..c48f7d6bed 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -612,13 +612,17 @@ phutil_register_library_map(array( 'DiffusionCloneURIView' => 'applications/diffusion/view/DiffusionCloneURIView.php', 'DiffusionCommandEngine' => 'applications/diffusion/protocol/DiffusionCommandEngine.php', 'DiffusionCommandEngineTestCase' => 'applications/diffusion/protocol/__tests__/DiffusionCommandEngineTestCase.php', + 'DiffusionCommitAcceptTransaction' => 'applications/diffusion/xaction/DiffusionCommitAcceptTransaction.php', + 'DiffusionCommitActionTransaction' => 'applications/diffusion/xaction/DiffusionCommitActionTransaction.php', 'DiffusionCommitAffectedFilesHeraldField' => 'applications/diffusion/herald/DiffusionCommitAffectedFilesHeraldField.php', + 'DiffusionCommitAuditTransaction' => 'applications/diffusion/xaction/DiffusionCommitAuditTransaction.php', 'DiffusionCommitAuditorsTransaction' => 'applications/diffusion/xaction/DiffusionCommitAuditorsTransaction.php', 'DiffusionCommitAuthorHeraldField' => 'applications/diffusion/herald/DiffusionCommitAuthorHeraldField.php', 'DiffusionCommitAutocloseHeraldField' => 'applications/diffusion/herald/DiffusionCommitAutocloseHeraldField.php', 'DiffusionCommitBranchesController' => 'applications/diffusion/controller/DiffusionCommitBranchesController.php', 'DiffusionCommitBranchesHeraldField' => 'applications/diffusion/herald/DiffusionCommitBranchesHeraldField.php', 'DiffusionCommitCommitterHeraldField' => 'applications/diffusion/herald/DiffusionCommitCommitterHeraldField.php', + 'DiffusionCommitConcernTransaction' => 'applications/diffusion/xaction/DiffusionCommitConcernTransaction.php', 'DiffusionCommitController' => 'applications/diffusion/controller/DiffusionCommitController.php', 'DiffusionCommitDiffContentAddedHeraldField' => 'applications/diffusion/herald/DiffusionCommitDiffContentAddedHeraldField.php', 'DiffusionCommitDiffContentHeraldField' => 'applications/diffusion/herald/DiffusionCommitDiffContentHeraldField.php', @@ -652,6 +656,7 @@ phutil_register_library_map(array( 'DiffusionCommitRemarkupRuleTestCase' => 'applications/diffusion/remarkup/__tests__/DiffusionCommitRemarkupRuleTestCase.php', 'DiffusionCommitRepositoryHeraldField' => 'applications/diffusion/herald/DiffusionCommitRepositoryHeraldField.php', 'DiffusionCommitRepositoryProjectsHeraldField' => 'applications/diffusion/herald/DiffusionCommitRepositoryProjectsHeraldField.php', + 'DiffusionCommitResignTransaction' => 'applications/diffusion/xaction/DiffusionCommitResignTransaction.php', 'DiffusionCommitRevertedByCommitEdgeType' => 'applications/diffusion/edge/DiffusionCommitRevertedByCommitEdgeType.php', 'DiffusionCommitRevertsCommitEdgeType' => 'applications/diffusion/edge/DiffusionCommitRevertsCommitEdgeType.php', 'DiffusionCommitReviewerHeraldField' => 'applications/diffusion/herald/DiffusionCommitReviewerHeraldField.php', @@ -5313,13 +5318,17 @@ phutil_register_library_map(array( 'DiffusionCloneURIView' => 'AphrontView', 'DiffusionCommandEngine' => 'Phobject', 'DiffusionCommandEngineTestCase' => 'PhabricatorTestCase', + 'DiffusionCommitAcceptTransaction' => 'DiffusionCommitAuditTransaction', + 'DiffusionCommitActionTransaction' => 'DiffusionCommitTransactionType', 'DiffusionCommitAffectedFilesHeraldField' => 'DiffusionCommitHeraldField', + 'DiffusionCommitAuditTransaction' => 'DiffusionCommitActionTransaction', 'DiffusionCommitAuditorsTransaction' => 'DiffusionCommitTransactionType', 'DiffusionCommitAuthorHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitAutocloseHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitBranchesController' => 'DiffusionController', 'DiffusionCommitBranchesHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitCommitterHeraldField' => 'DiffusionCommitHeraldField', + 'DiffusionCommitConcernTransaction' => 'DiffusionCommitAuditTransaction', 'DiffusionCommitController' => 'DiffusionController', 'DiffusionCommitDiffContentAddedHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitDiffContentHeraldField' => 'DiffusionCommitHeraldField', @@ -5353,6 +5362,7 @@ phutil_register_library_map(array( 'DiffusionCommitRemarkupRuleTestCase' => 'PhabricatorTestCase', 'DiffusionCommitRepositoryHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitRepositoryProjectsHeraldField' => 'DiffusionCommitHeraldField', + 'DiffusionCommitResignTransaction' => 'DiffusionCommitAuditTransaction', 'DiffusionCommitRevertedByCommitEdgeType' => 'PhabricatorEdgeType', 'DiffusionCommitRevertsCommitEdgeType' => 'PhabricatorEdgeType', 'DiffusionCommitReviewerHeraldField' => 'DiffusionCommitHeraldField', diff --git a/src/applications/diffusion/editor/DiffusionCommitEditEngine.php b/src/applications/diffusion/editor/DiffusionCommitEditEngine.php index c072b1a4b2..3f5f5f3cfe 100644 --- a/src/applications/diffusion/editor/DiffusionCommitEditEngine.php +++ b/src/applications/diffusion/editor/DiffusionCommitEditEngine.php @@ -5,6 +5,9 @@ final class DiffusionCommitEditEngine const ENGINECONST = 'diffusion.commit'; + const ACTIONGROUP_AUDIT = 'audit'; + const ACTIONGROUP_COMMIT = 'commit'; + public function isEngineConfigurable() { return false; } @@ -130,6 +133,13 @@ final class DiffusionCommitEditEngine ->setValue(array($desc, " \xC2\xB7 ", $doc_link)); } + $actions = DiffusionCommitActionTransaction::loadAllActions(); + $actions = msortv($actions, 'getCommitActionOrderVector'); + + foreach ($actions as $key => $action) { + $fields[] = $action->newEditField($object, $viewer); + } + return $fields; } diff --git a/src/applications/diffusion/xaction/DiffusionCommitAcceptTransaction.php b/src/applications/diffusion/xaction/DiffusionCommitAcceptTransaction.php new file mode 100644 index 0000000000..e04310a53e --- /dev/null +++ b/src/applications/diffusion/xaction/DiffusionCommitAcceptTransaction.php @@ -0,0 +1,74 @@ +getActor(); + return $this->isViewerAcceptingAuditor($object, $actor); + } + + public function applyExternalEffects($object, $value) { + $status = PhabricatorAuditStatusConstants::ACCEPTED; + $actor = $this->getActor(); + $this->applyAuditorEffect($object, $actor, $value, $status); + } + + protected function validateAction($object, PhabricatorUser $viewer) { + $config_key = 'audit.can-author-close-audit'; + if (!PhabricatorEnv::getEnvConfig($config_key)) { + if ($this->isViewerCommitAuthor($object, $viewer)) { + throw new Exception( + pht( + 'You can not accept this commit because you are the commit '. + 'author. You can only accept commits you did not author. You can '. + 'change this behavior by adjusting the "%s" setting in Config.', + $config_key)); + } + } + + if ($this->isViewerAcceptingAuditor($object, $viewer)) { + throw new Exception( + pht( + 'You can not accept this commit because you have already '. + 'accepted it.')); + } + } + + public function getTitle() { + return pht( + '%s accepted this commit.', + $this->renderAuthor()); + } + + public function getTitleForFeed() { + return pht( + '%s accepted %s.', + $this->renderAuthor(), + $this->renderObject()); + } + +} diff --git a/src/applications/diffusion/xaction/DiffusionCommitActionTransaction.php b/src/applications/diffusion/xaction/DiffusionCommitActionTransaction.php new file mode 100644 index 0000000000..4756f914e3 --- /dev/null +++ b/src/applications/diffusion/xaction/DiffusionCommitActionTransaction.php @@ -0,0 +1,118 @@ +getPhobjectClassConstant('ACTIONKEY', 32); + } + + public function isActionAvailable($object, PhabricatorUser $viewer) { + try { + $this->validateAction($object, $viewer); + return true; + } catch (Exception $ex) { + return false; + } + } + + abstract protected function validateAction($object, PhabricatorUser $viewer); + abstract protected function getCommitActionLabel(); + + public function getCommandKeyword() { + return null; + } + + public function getCommandAliases() { + return array(); + } + + public function getCommandSummary() { + return null; + } + + protected function getCommitActionOrder() { + return 1000; + } + + public function getCommitActionOrderVector() { + return id(new PhutilSortVector()) + ->addInt($this->getCommitActionOrder()); + } + + protected function getCommitActionGroupKey() { + return DiffusionCommitEditEngine::ACTIONGROUP_COMMIT; + } + + protected function getCommitActionDescription() { + return null; + } + + public static function loadAllActions() { + return id(new PhutilClassMapQuery()) + ->setAncestorClass(__CLASS__) + ->setUniqueMethod('getCommitActionKey') + ->execute(); + } + + protected function isViewerCommitAuthor( + PhabricatorRepositoryCommit $commit, + PhabricatorUser $viewer) { + + if (!$viewer->getPHID()) { + return false; + } + + return ($viewer->getPHID() === $commit->getAuthorPHID()); + } + + public function newEditField( + PhabricatorRepositoryCommit $commit, + PhabricatorUser $viewer) { + + $field = id(new PhabricatorApplyEditField()) + ->setKey($this->getCommitActionKey()) + ->setTransactionType($this->getTransactionTypeConstant()) + ->setValue(true); + + if ($this->isActionAvailable($commit, $viewer)) { + $label = $this->getCommitActionLabel(); + if ($label !== null) { + $field->setCommentActionLabel($label); + + $description = $this->getCommitActionDescription(); + $field->setActionDescription($description); + + $group_key = $this->getCommitActionGroupKey(); + $field->setCommentActionGroupKey($group_key); + + $field->setActionConflictKey('commit.action'); + } + } + + return $field; + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + $actor = $this->getActor(); + + $action_exception = null; + try { + $this->validateAction($object, $actor); + } catch (Exception $ex) { + $action_exception = $ex; + } + + foreach ($xactions as $xaction) { + if ($action_exception) { + $errors[] = $this->newInvalidError( + $action_exception->getMessage(), + $xaction); + } + } + + return $errors; + } + +} diff --git a/src/applications/diffusion/xaction/DiffusionCommitAuditTransaction.php b/src/applications/diffusion/xaction/DiffusionCommitAuditTransaction.php new file mode 100644 index 0000000000..64d32d1700 --- /dev/null +++ b/src/applications/diffusion/xaction/DiffusionCommitAuditTransaction.php @@ -0,0 +1,101 @@ +getViewerAuditStatus($commit, $viewer) !== null); + } + + protected function isViewerAcceptingAuditor( + PhabricatorRepositoryCommit $commit, + PhabricatorUser $viewer) { + return $this->isViewerAuditStatusAmong( + $commit, + $viewer, + array( + PhabricatorAuditStatusConstants::ACCEPTED, + )); + } + + protected function isViewerRejectingAuditor( + PhabricatorRepositoryCommit $commit, + PhabricatorUser $viewer) { + return $this->isViewerAuditStatusAmong( + $commit, + $viewer, + array( + PhabricatorAuditStatusConstants::CONCERNED, + )); + } + + protected function getViewerAuditStatus( + PhabricatorRepositoryCommit $commit, + PhabricatorUser $viewer) { + + if (!$viewer->getPHID()) { + return null; + } + + foreach ($commit->getAudits() as $audit) { + if ($audit->getAuditorPHID() != $viewer->getPHID()) { + continue; + } + + return $audit->getAuditStatus(); + } + + return null; + } + + protected function isViewerAuditStatusAmong( + PhabricatorRepositoryCommit $commit, + PhabricatorUser $viewer, + array $status_list) { + + $status = $this->getViewerAuditStatus($commit, $viewer); + if ($status === null) { + return false; + } + + $status_map = array_fuse($status_list); + return isset($status_map[$status]); + } + + protected function applyAuditorEffect( + PhabricatorRepositoryCommit $commit, + PhabricatorUser $viewer, + $value, + $status) { + + $audits = $commit->getAudits(); + $audits = mpull($audits, null, 'getAuditorPHID'); + + $map = array(); + + $with_authority = ($status != PhabricatorAuditStatusConstants::RESIGNED); + if ($with_authority) { + $has_authority = PhabricatorAuditCommentEditor::loadAuditPHIDsForUser( + $viewer); + $has_authority = array_fuse($has_authority); + foreach ($audits as $audit) { + $auditor_phid = $audit->getAuditorPHID(); + if (isset($has_authority[$auditor_phid])) { + $map[$auditor_phid] = $status; + } + } + } + + // In all cases, you affect yourself. + $map[$viewer->getPHID()] = $status; + + $this->updateAudits($commit, $map); + } + +} diff --git a/src/applications/diffusion/xaction/DiffusionCommitAuditorsTransaction.php b/src/applications/diffusion/xaction/DiffusionCommitAuditorsTransaction.php index a9ea6655c1..a4b1d817b4 100644 --- a/src/applications/diffusion/xaction/DiffusionCommitAuditorsTransaction.php +++ b/src/applications/diffusion/xaction/DiffusionCommitAuditorsTransaction.php @@ -75,22 +75,7 @@ final class DiffusionCommitAuditorsTransaction } } - foreach ($new as $phid => $status) { - $auditor = idx($auditors, $phid); - if (!$auditor) { - $auditor = id(new PhabricatorRepositoryAuditRequest()) - ->setAuditorPHID($phid) - ->setCommitPHID($object->getPHID()); - } else { - if ($auditor->getAuditStatus() === $status) { - continue; - } - } - - $auditor - ->setAuditStatus($status) - ->save(); - } + $this->updateAudits($object, $new); } public function getTitle() { diff --git a/src/applications/diffusion/xaction/DiffusionCommitConcernTransaction.php b/src/applications/diffusion/xaction/DiffusionCommitConcernTransaction.php new file mode 100644 index 0000000000..01d4db01ad --- /dev/null +++ b/src/applications/diffusion/xaction/DiffusionCommitConcernTransaction.php @@ -0,0 +1,70 @@ +getActor(); + return $this->isViewerRejectingAuditor($object, $actor); + } + + public function applyExternalEffects($object, $value) { + $status = PhabricatorAuditStatusConstants::CONCERNED; + $actor = $this->getActor(); + $this->applyAuditorEffect($object, $actor, $value, $status); + } + + protected function validateAction($object, PhabricatorUser $viewer) { + if ($this->isViewerCommitAuthor($object, $viewer)) { + throw new Exception( + pht( + 'You can not raise a concern with this commit because you are '. + 'the commit author. You can only raise concerns with commits '. + 'you did not author.')); + } + + if ($this->isViewerRejectingAuditor($object, $viewer)) { + throw new Exception( + pht( + 'You can not raise a concern with this commit because you have '. + 'already raised a concern with it.')); + } + } + + public function getTitle() { + return pht( + '%s raised a concern with this commit.', + $this->renderAuthor()); + } + + public function getTitleForFeed() { + return pht( + '%s raised a concern with %s.', + $this->renderAuthor(), + $this->renderObject()); + } + +} diff --git a/src/applications/diffusion/xaction/DiffusionCommitResignTransaction.php b/src/applications/diffusion/xaction/DiffusionCommitResignTransaction.php new file mode 100644 index 0000000000..1a553e3471 --- /dev/null +++ b/src/applications/diffusion/xaction/DiffusionCommitResignTransaction.php @@ -0,0 +1,62 @@ +getActor(); + return !$this->isViewerAnyAuditor($object, $actor); + } + + public function applyExternalEffects($object, $value) { + $status = PhabricatorAuditStatusConstants::RESIGNED; + $actor = $this->getActor(); + $this->applyAuditorEffect($object, $actor, $value, $status); + } + + protected function validateAction($object, PhabricatorUser $viewer) { + if (!$this->isViewerAnyAuditor($object, $viewer)) { + throw new Exception( + pht( + 'You can not resign from this commit because you are not an '. + 'auditor.')); + } + } + + public function getTitle() { + return pht( + '%s resigned from this commit.', + $this->renderAuthor()); + } + + public function getTitleForFeed() { + return pht( + '%s resigned from %s.', + $this->renderAuthor(), + $this->renderObject()); + } + +} diff --git a/src/applications/diffusion/xaction/DiffusionCommitTransactionType.php b/src/applications/diffusion/xaction/DiffusionCommitTransactionType.php index 200a675089..34d476d3d1 100644 --- a/src/applications/diffusion/xaction/DiffusionCommitTransactionType.php +++ b/src/applications/diffusion/xaction/DiffusionCommitTransactionType.php @@ -1,4 +1,37 @@ getAudits(); + $audits = mpull($audits, null, 'getAuditorPHID'); + + foreach ($new as $phid => $status) { + $audit = idx($audits, $phid); + if (!$audit) { + $audit = id(new PhabricatorRepositoryAuditRequest()) + ->setAuditorPHID($phid) + ->setCommitPHID($commit->getPHID()); + + $audits[$phid] = $audit; + } else { + if ($audit->getAuditStatus() === $status) { + continue; + } + } + + $audit + ->setAuditStatus($status) + ->save(); + } + + $commit->attachAudits($audits); + + return $audits; + } + +} From b5722a99635adb513036b9741fccdfc61b5e4472 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 11 Jan 2017 14:01:59 -0800 Subject: [PATCH 33/51] Use EditEngine stacked comments in Diffusion Summary: Ref T10978. Ref T8739. Fixes T10446. Converts Diffusion to modern comment/preview code, like Differential. Test Plan: {F2342933} Reviewers: chad Reviewed By: chad Maniphest Tasks: T10978, T10446, T8739 Differential Revision: https://secure.phabricator.com/D17183 --- resources/celerity/map.php | 26 ++-- resources/celerity/packages.php | 1 - src/__phutil_library_map__.php | 4 - .../PhabricatorAuditApplication.php | 2 - .../PhabricatorAuditAddCommentController.php | 90 ----------- .../PhabricatorAuditPreviewController.php | 82 ---------- .../audit/editor/PhabricatorAuditEditor.php | 17 ++- .../storage/PhabricatorAuditInlineComment.php | 7 +- .../view/PhabricatorAuditTransactionView.php | 7 +- .../DifferentialRevisionViewController.php | 7 +- .../controller/DiffusionCommitController.php | 144 ++---------------- .../editor/DiffusionCommitEditEngine.php | 87 +++++++++++ .../PHUIDiffInlineCommentPreviewListView.php | 11 +- .../behavior-add-reviewers-and-ccs.js | 47 ------ .../differential/behavior-keyboard-nav.js | 16 -- 15 files changed, 144 insertions(+), 404 deletions(-) delete mode 100644 src/applications/audit/controller/PhabricatorAuditAddCommentController.php delete mode 100644 src/applications/audit/controller/PhabricatorAuditPreviewController.php delete mode 100644 webroot/rsrc/js/application/differential/behavior-add-reviewers-and-ccs.js diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 3835e4b2d6..2eeba419ee 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -13,7 +13,7 @@ return array( 'core.pkg.js' => 'a2ead3fe', 'darkconsole.pkg.js' => 'e7393ebb', 'differential.pkg.css' => '9535a7e6', - 'differential.pkg.js' => '40b18f35', + 'differential.pkg.js' => 'ddfeb49b', 'diffusion.pkg.css' => '91c5d3a6', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -399,13 +399,12 @@ return array( 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', 'rsrc/js/application/differential/ChangesetViewManager.js' => 'a2828756', 'rsrc/js/application/differential/DifferentialInlineCommentEditor.js' => '2e3f9738', - 'rsrc/js/application/differential/behavior-add-reviewers-and-ccs.js' => 'e10f8e18', 'rsrc/js/application/differential/behavior-comment-jump.js' => '4fdb476d', 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', 'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1', 'rsrc/js/application/differential/behavior-dropdown-menus.js' => '9a6b9324', 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '4fbbc3e9', - 'rsrc/js/application/differential/behavior-keyboard-nav.js' => '2c426492', + 'rsrc/js/application/differential/behavior-keyboard-nav.js' => '92904457', 'rsrc/js/application/differential/behavior-populate.js' => '8694b1df', 'rsrc/js/application/differential/behavior-toggle-files.js' => 'ca3f91eb', 'rsrc/js/application/differential/behavior-user-select.js' => 'a8d8459d', @@ -627,13 +626,12 @@ return array( 'javelin-behavior-detect-timezone' => '4c193c96', 'javelin-behavior-device' => 'bb1dd507', 'javelin-behavior-diff-preview-link' => '051c7832', - 'javelin-behavior-differential-add-reviewers-and-ccs' => 'e10f8e18', 'javelin-behavior-differential-comment-jump' => '4fdb476d', 'javelin-behavior-differential-diff-radios' => 'e1ff79b1', 'javelin-behavior-differential-dropdown-menus' => '9a6b9324', 'javelin-behavior-differential-edit-inline-comments' => '4fbbc3e9', 'javelin-behavior-differential-feedback-preview' => 'b064af76', - 'javelin-behavior-differential-keyboard-navigation' => '2c426492', + 'javelin-behavior-differential-keyboard-navigation' => '92904457', 'javelin-behavior-differential-populate' => '8694b1df', 'javelin-behavior-differential-toggle-files' => 'ca3f91eb', 'javelin-behavior-differential-user-select' => 'a8d8459d', @@ -1144,12 +1142,6 @@ return array( 'javelin-install', 'javelin-util', ), - '2c426492' => array( - 'javelin-behavior', - 'javelin-dom', - 'javelin-stratcom', - 'phabricator-keyboard-shortcut', - ), '2caa8fb8' => array( 'javelin-install', 'javelin-event', @@ -1651,6 +1643,12 @@ return array( 'javelin-dom', 'javelin-request', ), + 92904457 => array( + 'javelin-behavior', + 'javelin-dom', + 'javelin-stratcom', + 'phabricator-keyboard-shortcut', + ), '92b9ec77' => array( 'javelin-behavior', 'javelin-stratcom', @@ -2086,11 +2084,6 @@ return array( 'javelin-request', 'javelin-util', ), - 'e10f8e18' => array( - 'javelin-behavior', - 'javelin-dom', - 'phabricator-prefab', - ), 'e1621fd5' => array( 'phui-inline-comment-view-css', ), @@ -2463,7 +2456,6 @@ return array( 'javelin-behavior-differential-populate', 'javelin-behavior-differential-diff-radios', 'javelin-behavior-differential-comment-jump', - 'javelin-behavior-differential-add-reviewers-and-ccs', 'javelin-behavior-differential-keyboard-navigation', 'javelin-behavior-aphront-drag-and-drop-textarea', 'javelin-behavior-phabricator-object-selector', diff --git a/resources/celerity/packages.php b/resources/celerity/packages.php index 95cf5303ef..caa4512e34 100644 --- a/resources/celerity/packages.php +++ b/resources/celerity/packages.php @@ -195,7 +195,6 @@ return array( 'javelin-behavior-differential-populate', 'javelin-behavior-differential-diff-radios', 'javelin-behavior-differential-comment-jump', - 'javelin-behavior-differential-add-reviewers-and-ccs', 'javelin-behavior-differential-keyboard-navigation', 'javelin-behavior-aphront-drag-and-drop-textarea', 'javelin-behavior-phabricator-object-selector', diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index c48f7d6bed..bba2d53d45 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1867,7 +1867,6 @@ phutil_register_library_map(array( 'PhabricatorAsanaSubtaskHasObjectEdgeType' => 'applications/doorkeeper/edge/PhabricatorAsanaSubtaskHasObjectEdgeType.php', 'PhabricatorAsanaTaskHasObjectEdgeType' => 'applications/doorkeeper/edge/PhabricatorAsanaTaskHasObjectEdgeType.php', 'PhabricatorAuditActionConstants' => 'applications/audit/constants/PhabricatorAuditActionConstants.php', - 'PhabricatorAuditAddCommentController' => 'applications/audit/controller/PhabricatorAuditAddCommentController.php', 'PhabricatorAuditApplication' => 'applications/audit/application/PhabricatorAuditApplication.php', 'PhabricatorAuditCommentEditor' => 'applications/audit/editor/PhabricatorAuditCommentEditor.php', 'PhabricatorAuditCommitStatusConstants' => 'applications/audit/constants/PhabricatorAuditCommitStatusConstants.php', @@ -1879,7 +1878,6 @@ phutil_register_library_map(array( 'PhabricatorAuditMailReceiver' => 'applications/audit/mail/PhabricatorAuditMailReceiver.php', 'PhabricatorAuditManagementDeleteWorkflow' => 'applications/audit/management/PhabricatorAuditManagementDeleteWorkflow.php', 'PhabricatorAuditManagementWorkflow' => 'applications/audit/management/PhabricatorAuditManagementWorkflow.php', - 'PhabricatorAuditPreviewController' => 'applications/audit/controller/PhabricatorAuditPreviewController.php', 'PhabricatorAuditReplyHandler' => 'applications/audit/mail/PhabricatorAuditReplyHandler.php', 'PhabricatorAuditStatusConstants' => 'applications/audit/constants/PhabricatorAuditStatusConstants.php', 'PhabricatorAuditTransaction' => 'applications/audit/storage/PhabricatorAuditTransaction.php', @@ -6757,7 +6755,6 @@ phutil_register_library_map(array( 'PhabricatorAsanaSubtaskHasObjectEdgeType' => 'PhabricatorEdgeType', 'PhabricatorAsanaTaskHasObjectEdgeType' => 'PhabricatorEdgeType', 'PhabricatorAuditActionConstants' => 'Phobject', - 'PhabricatorAuditAddCommentController' => 'PhabricatorAuditController', 'PhabricatorAuditApplication' => 'PhabricatorApplication', 'PhabricatorAuditCommentEditor' => 'PhabricatorEditor', 'PhabricatorAuditCommitStatusConstants' => 'Phobject', @@ -6772,7 +6769,6 @@ phutil_register_library_map(array( 'PhabricatorAuditMailReceiver' => 'PhabricatorObjectMailReceiver', 'PhabricatorAuditManagementDeleteWorkflow' => 'PhabricatorAuditManagementWorkflow', 'PhabricatorAuditManagementWorkflow' => 'PhabricatorManagementWorkflow', - 'PhabricatorAuditPreviewController' => 'PhabricatorAuditController', 'PhabricatorAuditReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler', 'PhabricatorAuditStatusConstants' => 'Phobject', 'PhabricatorAuditTransaction' => 'PhabricatorModularTransaction', diff --git a/src/applications/audit/application/PhabricatorAuditApplication.php b/src/applications/audit/application/PhabricatorAuditApplication.php index 932f2ca651..5c7ae66f0d 100644 --- a/src/applications/audit/application/PhabricatorAuditApplication.php +++ b/src/applications/audit/application/PhabricatorAuditApplication.php @@ -35,8 +35,6 @@ final class PhabricatorAuditApplication extends PhabricatorApplication { return array( '/audit/' => array( '(?:query/(?P[^/]+)/)?' => 'PhabricatorAuditListController', - 'addcomment/' => 'PhabricatorAuditAddCommentController', - 'preview/(?P[1-9]\d*)/' => 'PhabricatorAuditPreviewController', ), ); } diff --git a/src/applications/audit/controller/PhabricatorAuditAddCommentController.php b/src/applications/audit/controller/PhabricatorAuditAddCommentController.php deleted file mode 100644 index 5e50b2ae9d..0000000000 --- a/src/applications/audit/controller/PhabricatorAuditAddCommentController.php +++ /dev/null @@ -1,90 +0,0 @@ -getViewer(); - - if (!$request->isFormPost()) { - return new Aphront403Response(); - } - - $commit_phid = $request->getStr('commit'); - $commit = id(new DiffusionCommitQuery()) - ->setViewer($viewer) - ->withPHIDs(array($commit_phid)) - ->needAuditRequests(true) - ->executeOne(); - if (!$commit) { - return new Aphront404Response(); - } - - $xactions = array(); - - // make sure we only add auditors or ccs if the action matches - $action = $request->getStr('action'); - switch ($action) { - case PhabricatorAuditActionConstants::ADD_AUDITORS: - $auditors = $request->getArr('auditors'); - $xactions[] = id(new PhabricatorAuditTransaction()) - ->setTransactionType(PhabricatorAuditActionConstants::ADD_AUDITORS) - ->setNewValue(array_fuse($auditors)); - break; - case PhabricatorAuditActionConstants::ADD_CCS: - $xactions[] = id(new PhabricatorAuditTransaction()) - ->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS) - ->setNewValue( - array( - '+' => $request->getArr('ccs'), - )); - break; - case PhabricatorAuditActionConstants::COMMENT: - // We'll deal with this below. - break; - default: - $xactions[] = id(new PhabricatorAuditTransaction()) - ->setTransactionType(PhabricatorAuditActionConstants::ACTION) - ->setNewValue($action); - break; - } - - $content = $request->getStr('content'); - if (strlen($content)) { - $xactions[] = id(new PhabricatorAuditTransaction()) - ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) - ->attachComment( - id(new PhabricatorAuditTransactionComment()) - ->setCommitPHID($commit->getPHID()) - ->setContent($content)); - } - - $inlines = PhabricatorAuditInlineComment::loadDraftComments( - $viewer, - $commit->getPHID()); - foreach ($inlines as $inline) { - $xactions[] = id(new PhabricatorAuditTransaction()) - ->setTransactionType(PhabricatorAuditActionConstants::INLINE) - ->attachComment($inline->getTransactionComment()); - } - - id(new PhabricatorAuditEditor()) - ->setActor($viewer) - ->setContentSourceFromRequest($request) - ->setContinueOnMissingFields(true) - ->applyTransactions($commit, $xactions); - - $draft = id(new PhabricatorDraft())->loadOneWhere( - 'authorPHID = %s AND draftKey = %s', - $viewer->getPHID(), - 'diffusion-audit-'.$commit->getID()); - if ($draft) { - $draft->delete(); - } - - $uri = $commit->getURI(); - - return id(new AphrontRedirectResponse())->setURI($uri); - } - -} diff --git a/src/applications/audit/controller/PhabricatorAuditPreviewController.php b/src/applications/audit/controller/PhabricatorAuditPreviewController.php deleted file mode 100644 index 2b0212370c..0000000000 --- a/src/applications/audit/controller/PhabricatorAuditPreviewController.php +++ /dev/null @@ -1,82 +0,0 @@ -getViewer(); - $id = $request->getURIData('id'); - - $commit = id(new PhabricatorRepositoryCommit())->load($id); - if (!$commit) { - return new Aphront404Response(); - } - - $xactions = array(); - - $action = $request->getStr('action'); - if ($action != PhabricatorAuditActionConstants::COMMENT) { - $action_xaction = id(new PhabricatorAuditTransaction()) - ->setAuthorPHID($viewer->getPHID()) - ->setObjectPHID($commit->getPHID()) - ->setTransactionType(PhabricatorAuditActionConstants::ACTION) - ->setNewValue($action); - - $auditors = $request->getStrList('auditors'); - if ($action == PhabricatorAuditActionConstants::ADD_AUDITORS && - $auditors) { - $action_xaction->setTransactionType($action); - $action_xaction->setNewValue(array_fuse($auditors)); - } - - $ccs = $request->getStrList('ccs'); - if ($action == PhabricatorAuditActionConstants::ADD_CCS && $ccs) { - $action_xaction->setTransactionType( - PhabricatorTransactions::TYPE_SUBSCRIBERS); - - // NOTE: This doesn't get processed before use, so just provide fake - // values. - $action_xaction->setOldValue(array()); - $action_xaction->setNewValue($ccs); - } - - $xactions[] = $action_xaction; - } - - $content = $request->getStr('content'); - if (strlen($content)) { - $xactions[] = id(new PhabricatorAuditTransaction()) - ->setAuthorPHID($viewer->getPHID()) - ->setObjectPHID($commit->getPHID()) - ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) - ->attachComment( - id(new PhabricatorAuditTransactionComment()) - ->setContent($content)); - } - - $phids = array(); - foreach ($xactions as $xaction) { - $phids[] = $xaction->getRequiredHandlePHIDs(); - } - $phids = array_mergev($phids); - $handles = $this->loadViewerHandles($phids); - foreach ($xactions as $xaction) { - $xaction->setHandles($handles); - } - - $view = id(new PhabricatorAuditTransactionView()) - ->setIsPreview(true) - ->setUser($viewer) - ->setObjectPHID($commit->getPHID()) - ->setTransactions($xactions); - - id(new PhabricatorDraft()) - ->setAuthorPHID($viewer->getPHID()) - ->setDraftKey('diffusion-audit-'.$id) - ->setDraft($content) - ->replaceOrDelete(); - - return id(new AphrontAjaxResponse())->setContent(hsprintf('%s', $view)); - } - -} diff --git a/src/applications/audit/editor/PhabricatorAuditEditor.php b/src/applications/audit/editor/PhabricatorAuditEditor.php index a6ef866c93..608939df5a 100644 --- a/src/applications/audit/editor/PhabricatorAuditEditor.php +++ b/src/applications/audit/editor/PhabricatorAuditEditor.php @@ -10,7 +10,7 @@ final class PhabricatorAuditEditor private $rawPatch; private $auditorPHIDs = array(); - private $didExpandInlineState; + private $didExpandInlineState = false; public function addAuditReason($phid, $reason) { if (!isset($this->auditReasonMap[$phid])) { @@ -67,6 +67,21 @@ final class PhabricatorAuditEditor return $types; } + protected function expandTransactions( + PhabricatorLiskDAO $object, + array $xactions) { + + foreach ($xactions as $xaction) { + switch ($xaction->getTransactionType()) { + case PhabricatorTransactions::TYPE_INLINESTATE: + $this->didExpandInlineState = true; + break; + } + } + + return parent::expandTransactions($object, $xactions); + } + protected function transactionHasEffect( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { diff --git a/src/applications/audit/storage/PhabricatorAuditInlineComment.php b/src/applications/audit/storage/PhabricatorAuditInlineComment.php index ecb627efec..b330368d1b 100644 --- a/src/applications/audit/storage/PhabricatorAuditInlineComment.php +++ b/src/applications/audit/storage/PhabricatorAuditInlineComment.php @@ -68,7 +68,8 @@ final class PhabricatorAuditInlineComment public static function loadDraftComments( PhabricatorUser $viewer, - $commit_phid) { + $commit_phid, + $raw = false) { $inlines = id(new DiffusionDiffInlineCommentQuery()) ->setViewer($viewer) @@ -80,6 +81,10 @@ final class PhabricatorAuditInlineComment ->needReplyToComments(true) ->execute(); + if ($raw) { + return $inlines; + } + return self::buildProxies($inlines); } diff --git a/src/applications/audit/view/PhabricatorAuditTransactionView.php b/src/applications/audit/view/PhabricatorAuditTransactionView.php index 305ea19c56..ac5653e278 100644 --- a/src/applications/audit/view/PhabricatorAuditTransactionView.php +++ b/src/applications/audit/view/PhabricatorAuditTransactionView.php @@ -3,7 +3,7 @@ final class PhabricatorAuditTransactionView extends PhabricatorApplicationTransactionView { - private $pathMap; + private $pathMap = array(); public function setPathMap(array $path_map) { $this->pathMap = $path_map; @@ -55,12 +55,17 @@ final class PhabricatorAuditTransactionView $type_inline = PhabricatorAuditActionConstants::INLINE; $group = $xaction->getTransactionGroup(); + if ($xaction->getTransactionType() == $type_inline) { array_unshift($group, $xaction); } else { $out[] = parent::renderTransactionContent($xaction); } + if ($this->getIsPreview()) { + return $out; + } + if (!$group) { return $out; } diff --git a/src/applications/differential/controller/DifferentialRevisionViewController.php b/src/applications/differential/controller/DifferentialRevisionViewController.php index 5c476ef985..f126345fb7 100644 --- a/src/applications/differential/controller/DifferentialRevisionViewController.php +++ b/src/applications/differential/controller/DifferentialRevisionViewController.php @@ -463,12 +463,7 @@ final class DifferentialRevisionViewController extends DifferentialController { } Javelin::initBehavior('differential-user-select'); - - Javelin::initBehavior( - 'differential-keyboard-navigation', - array( - 'haunt' => null, - )); + Javelin::initBehavior('differential-keyboard-navigation'); $view = id(new PHUITwoColumnView()) ->setHeader($header) diff --git a/src/applications/diffusion/controller/DiffusionCommitController.php b/src/applications/diffusion/controller/DiffusionCommitController.php index 6da285aedc..97cb219b51 100644 --- a/src/applications/diffusion/controller/DiffusionCommitController.php +++ b/src/applications/diffusion/controller/DiffusionCommitController.php @@ -369,7 +369,9 @@ final class DiffusionCommitController extends DiffusionController { } - $add_comment = $this->renderAddCommentPanel($commit, $audit_requests); + $add_comment = $this->renderAddCommentPanel( + $commit, + $timeline); $filetree_on = $viewer->compareUserSetting( PhabricatorShowFiletreeSetting::SETTINGKEY, @@ -717,150 +719,24 @@ final class DiffusionCommitController extends DiffusionController { private function renderAddCommentPanel( PhabricatorRepositoryCommit $commit, - array $audit_requests) { - assert_instances_of($audit_requests, 'PhabricatorRepositoryAuditRequest'); + $timeline) { $request = $this->getRequest(); $viewer = $request->getUser(); - if (!$viewer->isLoggedIn()) { - return id(new PhabricatorApplicationTransactionCommentView()) - ->setUser($viewer) - ->setRequestURI($request->getRequestURI()); - } - - $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business'); - - $pane_id = celerity_generate_unique_node_id(); - Javelin::initBehavior( - 'differential-keyboard-navigation', - array( - 'haunt' => $pane_id, - )); - - $draft = id(new PhabricatorDraft())->loadOneWhere( - 'authorPHID = %s AND draftKey = %s', - $viewer->getPHID(), - 'diffusion-audit-'.$commit->getID()); - if ($draft) { - $draft = $draft->getDraft(); - } else { - $draft = null; - } - - $actions = $this->getAuditActions($commit, $audit_requests); - - $mailable_source = new PhabricatorMetaMTAMailableDatasource(); - $auditor_source = new DiffusionAuditorDatasource(); - - $form = id(new AphrontFormView()) - ->setUser($viewer) - ->setAction('/audit/addcomment/') - ->addHiddenInput('commit', $commit->getPHID()) - ->appendChild( - id(new AphrontFormSelectControl()) - ->setLabel(pht('Action')) - ->setName('action') - ->setID('audit-action') - ->setOptions($actions)) - ->appendControl( - id(new AphrontFormTokenizerControl()) - ->setLabel(pht('Add Auditors')) - ->setName('auditors') - ->setControlID('add-auditors') - ->setControlStyle('display: none') - ->setID('add-auditors-tokenizer') - ->setDisableBehavior(true) - ->setDatasource($auditor_source)) - ->appendControl( - id(new AphrontFormTokenizerControl()) - ->setLabel(pht('Add CCs')) - ->setName('ccs') - ->setControlID('add-ccs') - ->setControlStyle('display: none') - ->setID('add-ccs-tokenizer') - ->setDisableBehavior(true) - ->setDatasource($mailable_source)) - ->appendChild( - id(new PhabricatorRemarkupControl()) - ->setLabel(pht('Comments')) - ->setName('content') - ->setValue($draft) - ->setID('audit-content') - ->setUser($viewer)) - ->appendChild( - id(new AphrontFormSubmitControl()) - ->setValue(pht('Submit'))); - - $header = new PHUIHeaderView(); - $header->setHeader( - $is_serious ? pht('Audit Commit') : pht('Creative Accounting')); - - Javelin::initBehavior( - 'differential-add-reviewers-and-ccs', - array( - 'dynamic' => array( - 'add-auditors-tokenizer' => array( - 'actions' => array('add_auditors' => 1), - 'src' => $auditor_source->getDatasourceURI(), - 'row' => 'add-auditors', - 'placeholder' => $auditor_source->getPlaceholderText(), - ), - 'add-ccs-tokenizer' => array( - 'actions' => array('add_ccs' => 1), - 'src' => $mailable_source->getDatasourceURI(), - 'row' => 'add-ccs', - 'placeholder' => $mailable_source->getPlaceholderText(), - ), - ), - 'select' => 'audit-action', - )); - - Javelin::initBehavior('differential-feedback-preview', array( - 'uri' => '/audit/preview/'.$commit->getID().'/', - 'preview' => 'audit-preview', - 'content' => 'audit-content', - 'action' => 'audit-action', - 'previewTokenizers' => array( - 'auditors' => 'add-auditors-tokenizer', - 'ccs' => 'add-ccs-tokenizer', - ), - 'inline' => 'inline-comment-preview', - 'inlineuri' => '/diffusion/inline/preview/'.$commit->getPHID().'/', - )); - - $loading = phutil_tag_div( - 'aphront-panel-preview-loading-text', - pht('Loading preview...')); - - $preview_panel = phutil_tag_div( - 'aphront-panel-preview aphront-panel-flush', - array( - phutil_tag('div', array('id' => 'audit-preview'), $loading), - phutil_tag('div', array('id' => 'inline-comment-preview')), - )); + Javelin::initBehavior('differential-keyboard-navigation'); // TODO: This is pretty awkward, unify the CSS between Diffusion and // Differential better. require_celerity_resource('differential-core-view-css'); - $anchor = id(new PhabricatorAnchorView()) - ->setAnchorName('comment') - ->setNavigationMarker(true) - ->render(); + $comment_view = id(new DiffusionCommitEditEngine()) + ->setViewer($viewer) + ->buildEditEngineCommentView($commit); - $comment_box = id(new PHUIObjectBoxView()) - ->setHeader($header) - ->appendChild($form); + $comment_view->setTransactionTimeline($timeline); - return phutil_tag( - 'div', - array( - 'id' => $pane_id, - ), - phutil_tag_div( - 'differential-add-comment-panel', - array($anchor, $comment_box, $preview_panel))); + return $comment_view; } /** diff --git a/src/applications/diffusion/editor/DiffusionCommitEditEngine.php b/src/applications/diffusion/editor/DiffusionCommitEditEngine.php index 3f5f5f3cfe..28614579a0 100644 --- a/src/applications/diffusion/editor/DiffusionCommitEditEngine.php +++ b/src/applications/diffusion/editor/DiffusionCommitEditEngine.php @@ -48,6 +48,21 @@ final class DiffusionCommitEditEngine ->needAuditRequests(true); } + protected function getEditorURI() { + return $this->getApplication()->getApplicationURI('commit/edit/'); + } + + protected function newCommentActionGroups() { + return array( + id(new PhabricatorEditEngineCommentActionGroup()) + ->setKey(self::ACTIONGROUP_AUDIT) + ->setLabel(pht('Audit Actions')), + id(new PhabricatorEditEngineCommentActionGroup()) + ->setKey(self::ACTIONGROUP_COMMIT) + ->setLabel(pht('Commit Actions')), + ); + } + protected function getObjectCreateTitleText($object) { return pht('Create Commit'); } @@ -143,4 +158,76 @@ final class DiffusionCommitEditEngine return $fields; } + protected function newAutomaticCommentTransactions($object) { + $viewer = $this->getViewer(); + $xactions = array(); + + $inlines = PhabricatorAuditInlineComment::loadDraftComments( + $viewer, + $object->getPHID(), + $raw = true); + $inlines = msort($inlines, 'getID'); + + foreach ($inlines as $inline) { + $xactions[] = $object->getApplicationTransactionTemplate() + ->setTransactionType(PhabricatorAuditActionConstants::INLINE) + ->attachComment($inline); + } + + $viewer_phid = $viewer->getPHID(); + $viewer_is_author = ($object->getAuthorPHID() == $viewer_phid); + if ($viewer_is_author) { + $state_map = PhabricatorTransactions::getInlineStateMap(); + + $inlines = id(new DiffusionDiffInlineCommentQuery()) + ->setViewer($viewer) + ->withCommitPHIDs(array($object->getPHID())) + ->withFixedStates(array_keys($state_map)) + ->execute(); + if ($inlines) { + $old_value = mpull($inlines, 'getFixedState', 'getPHID'); + $new_value = array(); + foreach ($old_value as $key => $state) { + $new_value[$key] = $state_map[$state]; + } + + $xactions[] = $object->getApplicationTransactionTemplate() + ->setTransactionType(PhabricatorTransactions::TYPE_INLINESTATE) + ->setIgnoreOnNoEffect(true) + ->setOldValue($old_value) + ->setNewValue($new_value); + } + } + + return $xactions; + } + + protected function newCommentPreviewContent($object, array $xactions) { + $viewer = $this->getViewer(); + $type_inline = PhabricatorAuditActionConstants::INLINE; + + $inlines = array(); + foreach ($xactions as $xaction) { + if ($xaction->getTransactionType() === $type_inline) { + $inlines[] = $xaction->getComment(); + } + } + + $content = array(); + + if ($inlines) { + $inline_preview = id(new PHUIDiffInlineCommentPreviewListView()) + ->setViewer($viewer) + ->setInlineComments($inlines); + + $content[] = phutil_tag( + 'div', + array( + 'id' => 'inline-comment-preview', + ), + $inline_preview); + } + + return $content; + } } diff --git a/src/infrastructure/diff/view/PHUIDiffInlineCommentPreviewListView.php b/src/infrastructure/diff/view/PHUIDiffInlineCommentPreviewListView.php index 31e6b2b087..4bede1603b 100644 --- a/src/infrastructure/diff/view/PHUIDiffInlineCommentPreviewListView.php +++ b/src/infrastructure/diff/view/PHUIDiffInlineCommentPreviewListView.php @@ -38,8 +38,15 @@ final class PHUIDiffInlineCommentPreviewListView $inlines = $this->getInlineComments(); foreach ($inlines as $key => $inline) { - $inlines[$key] = DifferentialInlineComment::newFromModernComment( - $inline); + // TODO: This is real, real gross. + + if ($inline instanceof DifferentialTransactionComment) { + $inlines[$key] = DifferentialInlineComment::newFromModernComment( + $inline); + } else { + $inlines[$key] = PhabricatorAuditInlineComment::newFromModernComment( + $inline); + } } $engine = new PhabricatorMarkupEngine(); diff --git a/webroot/rsrc/js/application/differential/behavior-add-reviewers-and-ccs.js b/webroot/rsrc/js/application/differential/behavior-add-reviewers-and-ccs.js deleted file mode 100644 index 8f6a031d50..0000000000 --- a/webroot/rsrc/js/application/differential/behavior-add-reviewers-and-ccs.js +++ /dev/null @@ -1,47 +0,0 @@ -/** - * @provides javelin-behavior-differential-add-reviewers-and-ccs - * @requires javelin-behavior - * javelin-dom - * phabricator-prefab - */ - -JX.behavior('differential-add-reviewers-and-ccs', function(config) { - - var dynamic = {}; - for (var k in config.dynamic) { - var props = config.dynamic[k]; - props.id = k; - - var tokenizer = JX.Prefab.buildTokenizer(props).tokenizer; - tokenizer.start(); - - dynamic[k] = { - row : JX.$(props.row), - tokenizer : tokenizer, - actions : props.actions, - labels: props.labels - }; - } - - JX.DOM.listen( - JX.$(config.select), - 'change', - null, - function() { - var v = JX.$(config.select).value; - for (var k in dynamic) { - if (dynamic[k].actions[v]) { - JX.DOM.show(dynamic[k].row); - if (dynamic[k].labels) { - var label_node = JX.DOM.find(dynamic[k].row, 'label'); - if (label_node) { - JX.DOM.setContent(label_node, dynamic[k].labels[v]); - } - } - dynamic[k].tokenizer.refresh(); - } else { - JX.DOM.hide(dynamic[k].row); - } - } - }); -}); diff --git a/webroot/rsrc/js/application/differential/behavior-keyboard-nav.js b/webroot/rsrc/js/application/differential/behavior-keyboard-nav.js index da97d908b2..f48df4d3dc 100644 --- a/webroot/rsrc/js/application/differential/behavior-keyboard-nav.js +++ b/webroot/rsrc/js/application/differential/behavior-keyboard-nav.js @@ -173,16 +173,6 @@ JX.behavior('differential-keyboard-navigation', function(config) { refreshFocus(); }); - var haunt_mode = 0; - function haunt() { - haunt_mode = (haunt_mode + 1) % 3; - - var el = JX.$(config.haunt); - for (var ii = 1; ii <= 2; ii++) { - JX.DOM.alterClass(el, 'differential-haunt-mode-'+ii, (haunt_mode == ii)); - } - } - new JX.KeyboardShortcut('j', 'Jump to next change.') .setHandler(function(manager) { jump(manager, 1); @@ -271,10 +261,4 @@ JX.behavior('differential-keyboard-navigation', function(config) { }) .register(); - if (config.haunt) { - new JX.KeyboardShortcut('z', 'Cycle comment panel haunting modes.') - .setHandler(haunt) - .register(); - } - }); From b471f6c07aefaaa0029e7b94b03fb51c54da0074 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 11 Jan 2017 14:44:33 -0800 Subject: [PATCH 34/51] Order inline comments in Diffusion consistently with Differential Summary: Fixes T8739. Currently, Diffusion inline comments in the timeline are sorted arbitrarily, mostly by creation order. Instead, sort them by line number, like Differential. Test Plan: Made comments in "C", "B", "A" order, saw them in line order after submit: {F2343032} Reviewers: chad Reviewed By: chad Maniphest Tasks: T8739 Differential Revision: https://secure.phabricator.com/D17184 --- .../view/PhabricatorAuditTransactionView.php | 82 +++++++++++-------- 1 file changed, 46 insertions(+), 36 deletions(-) diff --git a/src/applications/audit/view/PhabricatorAuditTransactionView.php b/src/applications/audit/view/PhabricatorAuditTransactionView.php index ac5653e278..897440d9d7 100644 --- a/src/applications/audit/view/PhabricatorAuditTransactionView.php +++ b/src/applications/audit/view/PhabricatorAuditTransactionView.php @@ -81,48 +81,58 @@ final class PhabricatorAuditTransactionView } } - if ($inlines) { - - // TODO: This should do something similar to sortAndGroupInlines() to get - // a stable ordering. - - $inlines_by_path = array(); - foreach ($inlines as $key => $inline) { - $comment = $inline->getComment(); - if (!$comment) { - // TODO: Migrate these away? They probably do not exist on normal - // non-development installs. - unset($inlines[$key]); - continue; - } - $path_id = $comment->getPathID(); - $inlines_by_path[$path_id][] = $inline; + $structs = array(); + foreach ($inlines as $key => $inline) { + $comment = $inline->getComment(); + if (!$comment) { + // TODO: Migrate these away? They probably do not exist on normal + // non-development installs. + unset($inlines[$key]); + continue; } - $inline_view = new PhabricatorInlineSummaryView(); - foreach ($inlines_by_path as $path_id => $group) { - $path = idx($this->pathMap, $path_id); - if ($path === null) { - continue; - } - - $items = array(); - foreach ($group as $inline) { - $comment = $inline->getComment(); - $item = array( - 'id' => $comment->getID(), - 'line' => $comment->getLineNumber(), - 'length' => $comment->getLineLength(), - 'content' => parent::renderTransactionContent($inline), - ); - $items[] = $item; - } - $inline_view->addCommentGroup($path, $items); + $path_id = $comment->getPathID(); + $path = idx($this->pathMap, $path_id); + if ($path === null) { + continue; } - $out[] = $inline_view; + $structs[] = array( + 'inline' => $inline, + 'path' => $path, + 'sort' => (string)id(new PhutilSortVector()) + ->addString($path) + ->addInt($comment->getLineNumber()) + ->addInt($comment->getLineLength()) + ->addInt($inline->getID()), + ); } + if (!$structs) { + return $out; + } + + $structs = isort($structs, 'sort'); + $structs = igroup($structs, 'path'); + + $inline_view = new PhabricatorInlineSummaryView(); + foreach ($structs as $path => $group) { + $inlines = ipull($group, 'inline'); + $items = array(); + foreach ($inlines as $inline) { + $comment = $inline->getComment(); + $items[] = array( + 'id' => $comment->getID(), + 'line' => $comment->getLineNumber(), + 'length' => $comment->getLineLength(), + 'content' => parent::renderTransactionContent($inline), + ); + } + $inline_view->addCommentGroup($path, $items); + } + + $out[] = $inline_view; + return $out; } From c05cb1ba6ddd695312a2affd609b5401dfde0d66 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 11 Jan 2017 14:51:52 -0800 Subject: [PATCH 35/51] Make "Audit Requested" put commits into the "Needs Audit" state Summary: Fixes T7504. I think that task legitimately describes a bug and that the current behavior is counterintuitive. Test Plan: Manually added an auditor to a commit with none; saw it become "Audit Required" as an overall state. Reviewers: chad Reviewed By: chad Maniphest Tasks: T7504 Differential Revision: https://secure.phabricator.com/D17185 --- .../repository/storage/PhabricatorRepositoryCommit.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/applications/repository/storage/PhabricatorRepositoryCommit.php b/src/applications/repository/storage/PhabricatorRepositoryCommit.php index 704686cfb1..fd3e5fe97a 100644 --- a/src/applications/repository/storage/PhabricatorRepositoryCommit.php +++ b/src/applications/repository/storage/PhabricatorRepositoryCommit.php @@ -256,6 +256,7 @@ final class PhabricatorRepositoryCommit foreach ($requests as $request) { switch ($request->getAuditStatus()) { case PhabricatorAuditStatusConstants::AUDIT_REQUIRED: + case PhabricatorAuditStatusConstants::AUDIT_REQUESTED: $any_need = true; break; case PhabricatorAuditStatusConstants::ACCEPTED: From 11861265fe94fa97e4d0563c5bdb7b8cac27282d Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 11 Jan 2017 15:09:57 -0800 Subject: [PATCH 36/51] Merge "Audit" more completely into "Diffusion" Summary: Fixes T6630. Long ago, "Audit", "Diffusion" and "Repositories" were three totally separate applications. This separation isn't useful and the three rapidly became intertwined. Ideally, they would all be one application. This doesn't take us quite that far, but Audit no longer has any controllers and has little actual behavior. The "Audit" screen has always just been a SearchEngine view of commits with some filters on it, and this formalizes that and puts a link to it in Diffusion. (This view has other uses, too.) Test Plan: - Accessed audit from home page. - Accessed audit/commits from Diffusion. - Could no longer uninstall Audit on its own. - Grepped for `/audit/` and `AuditApplication`. Reviewers: chad Reviewed By: chad Maniphest Tasks: T6630 Differential Revision: https://secure.phabricator.com/D17186 --- src/__phutil_library_map__.php | 4 +-- .../PhabricatorAuditApplication.php | 29 +++++++------------ .../audit/conduit/AuditConduitAPIMethod.php | 3 +- .../PhabricatorAuditListController.php | 19 ------------ .../audit/editor/PhabricatorAuditEditor.php | 2 +- .../mail/PhabricatorAuditMailReceiver.php | 2 +- .../query/PhabricatorCommitSearchEngine.php | 2 +- .../PhabricatorDiffusionApplication.php | 6 ++++ .../DiffusionCommitListController.php | 26 +++++++++++++++++ .../DiffusionRepositoryListController.php | 11 +++++++ .../PhabricatorOwnersDetailController.php | 2 +- .../PhabricatorPeopleProfileMenuEngine.php | 2 +- .../engine/PhabricatorJumpNavHandler.php | 2 +- src/docs/book/phabricator.book | 4 --- src/docs/user/userguide/audit.diviner | 4 +-- 15 files changed, 65 insertions(+), 53 deletions(-) delete mode 100644 src/applications/audit/controller/PhabricatorAuditListController.php create mode 100644 src/applications/diffusion/controller/DiffusionCommitListController.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index bba2d53d45..a644231520 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -642,6 +642,7 @@ phutil_register_library_map(array( 'DiffusionCommitHintQuery' => 'applications/diffusion/query/DiffusionCommitHintQuery.php', 'DiffusionCommitHookEngine' => 'applications/diffusion/engine/DiffusionCommitHookEngine.php', 'DiffusionCommitHookRejectException' => 'applications/diffusion/exception/DiffusionCommitHookRejectException.php', + 'DiffusionCommitListController' => 'applications/diffusion/controller/DiffusionCommitListController.php', 'DiffusionCommitMergeHeraldField' => 'applications/diffusion/herald/DiffusionCommitMergeHeraldField.php', 'DiffusionCommitMessageHeraldField' => 'applications/diffusion/herald/DiffusionCommitMessageHeraldField.php', 'DiffusionCommitPackageAuditHeraldField' => 'applications/diffusion/herald/DiffusionCommitPackageAuditHeraldField.php', @@ -1873,7 +1874,6 @@ phutil_register_library_map(array( 'PhabricatorAuditController' => 'applications/audit/controller/PhabricatorAuditController.php', 'PhabricatorAuditEditor' => 'applications/audit/editor/PhabricatorAuditEditor.php', 'PhabricatorAuditInlineComment' => 'applications/audit/storage/PhabricatorAuditInlineComment.php', - 'PhabricatorAuditListController' => 'applications/audit/controller/PhabricatorAuditListController.php', 'PhabricatorAuditListView' => 'applications/audit/view/PhabricatorAuditListView.php', 'PhabricatorAuditMailReceiver' => 'applications/audit/mail/PhabricatorAuditMailReceiver.php', 'PhabricatorAuditManagementDeleteWorkflow' => 'applications/audit/management/PhabricatorAuditManagementDeleteWorkflow.php', @@ -5346,6 +5346,7 @@ phutil_register_library_map(array( 'DiffusionCommitHintQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'DiffusionCommitHookEngine' => 'Phobject', 'DiffusionCommitHookRejectException' => 'Exception', + 'DiffusionCommitListController' => 'DiffusionController', 'DiffusionCommitMergeHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitMessageHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitPackageAuditHeraldField' => 'DiffusionCommitHeraldField', @@ -6764,7 +6765,6 @@ phutil_register_library_map(array( 'Phobject', 'PhabricatorInlineCommentInterface', ), - 'PhabricatorAuditListController' => 'PhabricatorAuditController', 'PhabricatorAuditListView' => 'AphrontView', 'PhabricatorAuditMailReceiver' => 'PhabricatorObjectMailReceiver', 'PhabricatorAuditManagementDeleteWorkflow' => 'PhabricatorAuditManagementWorkflow', diff --git a/src/applications/audit/application/PhabricatorAuditApplication.php b/src/applications/audit/application/PhabricatorAuditApplication.php index 5c7ae66f0d..9eb9cd0e09 100644 --- a/src/applications/audit/application/PhabricatorAuditApplication.php +++ b/src/applications/audit/application/PhabricatorAuditApplication.php @@ -3,7 +3,7 @@ final class PhabricatorAuditApplication extends PhabricatorApplication { public function getBaseURI() { - return '/audit/'; + return '/diffusion/commit/'; } public function getIcon() { @@ -18,25 +18,16 @@ final class PhabricatorAuditApplication extends PhabricatorApplication { return pht('Browse and Audit Commits'); } + public function canUninstall() { + // Audit was once a separate application, but has largely merged with + // Diffusion. + return false; + } + public function isPinnedByDefault(PhabricatorUser $viewer) { - return true; - } - - public function getHelpDocumentationArticles(PhabricatorUser $viewer) { - return array( - array( - 'name' => pht('Audit User Guide'), - 'href' => PhabricatorEnv::getDoclink('Audit User Guide'), - ), - ); - } - - public function getRoutes() { - return array( - '/audit/' => array( - '(?:query/(?P[^/]+)/)?' => 'PhabricatorAuditListController', - ), - ); + return parent::isClassInstalledForViewer( + 'PhabricatorDiffusionApplication', + $viewer); } public function getApplicationOrder() { diff --git a/src/applications/audit/conduit/AuditConduitAPIMethod.php b/src/applications/audit/conduit/AuditConduitAPIMethod.php index 3a11b27698..a5a7957b1f 100644 --- a/src/applications/audit/conduit/AuditConduitAPIMethod.php +++ b/src/applications/audit/conduit/AuditConduitAPIMethod.php @@ -3,7 +3,8 @@ abstract class AuditConduitAPIMethod extends ConduitAPIMethod { final public function getApplication() { - return PhabricatorApplication::getByClass('PhabricatorAuditApplication'); + return PhabricatorApplication::getByClass( + 'PhabricatorDiffusionApplication'); } } diff --git a/src/applications/audit/controller/PhabricatorAuditListController.php b/src/applications/audit/controller/PhabricatorAuditListController.php deleted file mode 100644 index 4d2d7bf37e..0000000000 --- a/src/applications/audit/controller/PhabricatorAuditListController.php +++ /dev/null @@ -1,19 +0,0 @@ -setQueryKey($request->getURIData('queryKey')) - ->setSearchEngine(new PhabricatorCommitSearchEngine()) - ->setNavigation($this->buildSideNavView()); - - return $this->delegateToController($controller); - } - -} diff --git a/src/applications/audit/editor/PhabricatorAuditEditor.php b/src/applications/audit/editor/PhabricatorAuditEditor.php index 608939df5a..4b2d9bec29 100644 --- a/src/applications/audit/editor/PhabricatorAuditEditor.php +++ b/src/applications/audit/editor/PhabricatorAuditEditor.php @@ -42,7 +42,7 @@ final class PhabricatorAuditEditor } public function getEditorApplicationClass() { - return 'PhabricatorAuditApplication'; + return 'PhabricatorDiffusionApplication'; } public function getEditorObjectsDescription() { diff --git a/src/applications/audit/mail/PhabricatorAuditMailReceiver.php b/src/applications/audit/mail/PhabricatorAuditMailReceiver.php index 36e68c76a9..9dc70e9d8a 100644 --- a/src/applications/audit/mail/PhabricatorAuditMailReceiver.php +++ b/src/applications/audit/mail/PhabricatorAuditMailReceiver.php @@ -4,7 +4,7 @@ final class PhabricatorAuditMailReceiver extends PhabricatorObjectMailReceiver { public function isEnabled() { return PhabricatorApplication::isClassInstalled( - 'PhabricatorAuditApplication'); + 'PhabricatorDiffusionApplication'); } protected function getObjectPattern() { diff --git a/src/applications/audit/query/PhabricatorCommitSearchEngine.php b/src/applications/audit/query/PhabricatorCommitSearchEngine.php index b0c5ec1d2c..40bab34264 100644 --- a/src/applications/audit/query/PhabricatorCommitSearchEngine.php +++ b/src/applications/audit/query/PhabricatorCommitSearchEngine.php @@ -73,7 +73,7 @@ final class PhabricatorCommitSearchEngine } protected function getURI($path) { - return '/audit/'.$path; + return '/diffusion/commit/'.$path; } protected function getBuiltinQueryNames() { diff --git a/src/applications/diffusion/application/PhabricatorDiffusionApplication.php b/src/applications/diffusion/application/PhabricatorDiffusionApplication.php index 0e2b669eed..8fef05bd88 100644 --- a/src/applications/diffusion/application/PhabricatorDiffusionApplication.php +++ b/src/applications/diffusion/application/PhabricatorDiffusionApplication.php @@ -28,6 +28,10 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication { 'name' => pht('Diffusion User Guide'), 'href' => PhabricatorEnv::getDoclink('Diffusion User Guide'), ), + array( + 'name' => pht('Audit User Guide'), + 'href' => PhabricatorEnv::getDoclink('Audit User Guide'), + ), ); } @@ -131,6 +135,8 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication { 'lint/' => 'DiffusionLintController', 'commit/' => array( + $this->getQueryRoutePattern() => + 'DiffusionCommitListController', $this->getEditRoutePattern('edit/') => 'DiffusionCommitEditController', ), diff --git a/src/applications/diffusion/controller/DiffusionCommitListController.php b/src/applications/diffusion/controller/DiffusionCommitListController.php new file mode 100644 index 0000000000..b8c008c4d6 --- /dev/null +++ b/src/applications/diffusion/controller/DiffusionCommitListController.php @@ -0,0 +1,26 @@ +setController($this) + ->buildResponse(); + } + + protected function buildApplicationCrumbs() { + $crumbs = parent::buildApplicationCrumbs(); + + $crumbs->addTextCrumb( + pht('Commits'), + $this->getApplicationURI('commit/')); + + return $crumbs; + } + +} diff --git a/src/applications/diffusion/controller/DiffusionRepositoryListController.php b/src/applications/diffusion/controller/DiffusionRepositoryListController.php index eebe0bf91f..d886209c89 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryListController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryListController.php @@ -7,8 +7,19 @@ final class DiffusionRepositoryListController extends DiffusionController { } public function handleRequest(AphrontRequest $request) { + $items = array(); + + $items[] = id(new PHUIListItemView()) + ->setType(PHUIListItemView::TYPE_LABEL) + ->setName(pht('Commits')); + + $items[] = id(new PHUIListItemView()) + ->setName('Browse Commits') + ->setHref($this->getApplicationURI('commit/')); + return id(new PhabricatorRepositorySearchEngine()) ->setController($this) + ->setNavigationItems($items) ->buildResponse(); } diff --git a/src/applications/owners/controller/PhabricatorOwnersDetailController.php b/src/applications/owners/controller/PhabricatorOwnersDetailController.php index c91de51cc6..d7d249d8a5 100644 --- a/src/applications/owners/controller/PhabricatorOwnersDetailController.php +++ b/src/applications/owners/controller/PhabricatorOwnersDetailController.php @@ -65,7 +65,7 @@ final class PhabricatorOwnersDetailController $commit_views = array(); - $commit_uri = id(new PhutilURI('/audit/')) + $commit_uri = id(new PhutilURI('/diffusion/commit/')) ->setQueryParams( array( 'auditorPHIDs' => $package->getPHID(), diff --git a/src/applications/people/engine/PhabricatorPeopleProfileMenuEngine.php b/src/applications/people/engine/PhabricatorPeopleProfileMenuEngine.php index aab3896721..9965147a12 100644 --- a/src/applications/people/engine/PhabricatorPeopleProfileMenuEngine.php +++ b/src/applications/people/engine/PhabricatorPeopleProfileMenuEngine.php @@ -63,7 +63,7 @@ final class PhabricatorPeopleProfileMenuEngine $viewer); if ($have_diffusion) { $uri = urisprintf( - '/audit/?authors=%s#R', + '/diffusion/commit/?authors=%s#R', $object->getPHID()); $items[] = $this->newItem() diff --git a/src/applications/search/engine/PhabricatorJumpNavHandler.php b/src/applications/search/engine/PhabricatorJumpNavHandler.php index 6c30af98dd..6f6ea3d0a9 100644 --- a/src/applications/search/engine/PhabricatorJumpNavHandler.php +++ b/src/applications/search/engine/PhabricatorJumpNavHandler.php @@ -6,7 +6,7 @@ final class PhabricatorJumpNavHandler extends Phobject { $jump = trim($jump); $patterns = array( - '/^a$/i' => 'uri:/audit/', + '/^a$/i' => 'uri:/diffusion/commit/', '/^f$/i' => 'uri:/feed/', '/^d$/i' => 'uri:/differential/', '/^r$/i' => 'uri:/diffusion/', diff --git a/src/docs/book/phabricator.book b/src/docs/book/phabricator.book index 2b362853b4..2beef23023 100644 --- a/src/docs/book/phabricator.book +++ b/src/docs/book/phabricator.book @@ -37,10 +37,6 @@ "name": "Arcanist Integration", "include": "(^src/applications/arcanist/)" }, - "audit": { - "name": "Audit", - "include": "(^src/applications/audit/)" - }, "auth": { "name": "Auth", "include": "(^src/applications/auth/)" diff --git a/src/docs/user/userguide/audit.diviner b/src/docs/user/userguide/audit.diviner index 9c2f752b08..2ebc26a687 100644 --- a/src/docs/user/userguide/audit.diviner +++ b/src/docs/user/userguide/audit.diviner @@ -23,8 +23,8 @@ track of two things: - **Audit Requests** which ask a user (or some other entity) to audit a commit. These can be triggered in a number of ways (see below). -In the Audit tool's home screen (at `/audit/`) and on the homepage you can see -commits and requests that require your action: +In the Audit tool's home screen and on the homepage you can see commits and +requests that require your action: - **Required Audits** are open audit requests that require you, a project you are a member of, or a package you own to audit a commit. An audit From b941331bdfe611794cd7a4163f63b63df36d2c88 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 11 Jan 2017 15:25:41 -0800 Subject: [PATCH 37/51] Prevent users from resigning from audits they've already resigned from Summary: Ref T10978. Since "Resigned" is a status in Audit, you could repeatedly resign. This is confusing; prevent it. Test Plan: Tried to resign twice; was only allowed to resign once. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10978 Differential Revision: https://secure.phabricator.com/D17187 --- .../xaction/DiffusionCommitAuditTransaction.php | 17 +++++++++++++++++ .../DiffusionCommitResignTransaction.php | 6 +++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/applications/diffusion/xaction/DiffusionCommitAuditTransaction.php b/src/applications/diffusion/xaction/DiffusionCommitAuditTransaction.php index 64d32d1700..86656d85a6 100644 --- a/src/applications/diffusion/xaction/DiffusionCommitAuditTransaction.php +++ b/src/applications/diffusion/xaction/DiffusionCommitAuditTransaction.php @@ -13,6 +13,23 @@ abstract class DiffusionCommitAuditTransaction return ($this->getViewerAuditStatus($commit, $viewer) !== null); } + protected function isViewerAnyActiveAuditor( + PhabricatorRepositoryCommit $commit, + PhabricatorUser $viewer) { + + // This omits various inactive states like "Resigned" and "Not Required". + + return $this->isViewerAuditStatusAmong( + $commit, + $viewer, + array( + PhabricatorAuditStatusConstants::AUDIT_REQUIRED, + PhabricatorAuditStatusConstants::CONCERNED, + PhabricatorAuditStatusConstants::ACCEPTED, + PhabricatorAuditStatusConstants::AUDIT_REQUESTED, + )); + } + protected function isViewerAcceptingAuditor( PhabricatorRepositoryCommit $commit, PhabricatorUser $viewer) { diff --git a/src/applications/diffusion/xaction/DiffusionCommitResignTransaction.php b/src/applications/diffusion/xaction/DiffusionCommitResignTransaction.php index 1a553e3471..4a68252414 100644 --- a/src/applications/diffusion/xaction/DiffusionCommitResignTransaction.php +++ b/src/applications/diffusion/xaction/DiffusionCommitResignTransaction.php @@ -28,7 +28,7 @@ final class DiffusionCommitResignTransaction public function generateOldValue($object) { $actor = $this->getActor(); - return !$this->isViewerAnyAuditor($object, $actor); + return !$this->isViewerAnyActiveAuditor($object, $actor); } public function applyExternalEffects($object, $value) { @@ -38,11 +38,11 @@ final class DiffusionCommitResignTransaction } protected function validateAction($object, PhabricatorUser $viewer) { - if (!$this->isViewerAnyAuditor($object, $viewer)) { + if (!$this->isViewerAnyActiveAuditor($object, $viewer)) { throw new Exception( pht( 'You can not resign from this commit because you are not an '. - 'auditor.')); + 'active auditor.')); } } From 69d6374646eeb1981745e6805635c08b88c23d3f Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 12 Jan 2017 07:23:54 -0800 Subject: [PATCH 38/51] Make new EditEngine Audit transactions apply old mail tags Summary: Ref T10978. Until T10448 makes mail tags modular, keep the old tags working. Test Plan: Made some commit edits, ran `bin/phd debug task` to process mail for them. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10978 Differential Revision: https://secure.phabricator.com/D17190 --- .../audit/storage/PhabricatorAuditTransaction.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/applications/audit/storage/PhabricatorAuditTransaction.php b/src/applications/audit/storage/PhabricatorAuditTransaction.php index 1ee1833009..ff3751b912 100644 --- a/src/applications/audit/storage/PhabricatorAuditTransaction.php +++ b/src/applications/audit/storage/PhabricatorAuditTransaction.php @@ -438,6 +438,18 @@ final class PhabricatorAuditTransaction public function getMailTags() { $tags = array(); switch ($this->getTransactionType()) { + case DiffusionCommitAcceptTransaction::TRANSACTIONTYPE: + $tags[] = self::MAILTAG_ACTION_ACCEPT; + break; + case DiffusionCommitConcernTransaction::TRANSACTIONTYPE: + $tags[] = self::MAILTAG_ACTION_CONCERN; + break; + case DiffusionCommitResignTransaction::TRANSACTIONTYPE: + $tags[] = self::MAILTAG_ACTION_RESIGN; + break; + case DiffusionCommitAuditorsTransaction::TRANSACTIONTYPE: + $tags[] = self::MAILTAG_ADD_AUDITORS; + break; case PhabricatorAuditActionConstants::ACTION: switch ($this->getNewValue()) { case PhabricatorAuditActionConstants::CONCERN: From 7d3d0224071a35edbb6cafbb9ab1e373e96435f4 Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 12 Jan 2017 07:40:48 -0800 Subject: [PATCH 39/51] Restore "[Action]" mail subject lines to Differential/Diffusion Summary: Ref T11114. Ref T10978. These hadn't made it over to EditEngine yet. Test Plan: - Took various actions on revisions and commits. - Used `bin/mail show-outbound --id ...` to examine the "Vary Subject", saw it properly generate "[Accepted]", "[Resigned]", etc. Reviewers: chad Reviewed By: chad Maniphest Tasks: T11114, T10978 Differential Revision: https://secure.phabricator.com/D17191 --- .../DifferentialRevisionAbandonTransaction.php | 4 ++++ .../DifferentialRevisionAcceptTransaction.php | 4 ++++ .../DifferentialRevisionActionTransaction.php | 4 ++++ .../DifferentialRevisionCloseTransaction.php | 4 ++++ ...fferentialRevisionCommandeerTransaction.php | 4 ++++ ...ferentialRevisionPlanChangesTransaction.php | 4 ++++ .../DifferentialRevisionReclaimTransaction.php | 4 ++++ .../DifferentialRevisionRejectTransaction.php | 4 ++++ .../DifferentialRevisionReopenTransaction.php | 4 ++++ ...rentialRevisionRequestReviewTransaction.php | 4 ++++ .../DifferentialRevisionResignTransaction.php | 4 ++++ .../DiffusionCommitAcceptTransaction.php | 4 ++++ .../DiffusionCommitConcernTransaction.php | 4 ++++ .../DiffusionCommitResignTransaction.php | 4 ++++ .../storage/PhabricatorModularTransaction.php | 18 ++++++++++++++++++ .../PhabricatorModularTransactionType.php | 8 ++++++++ 16 files changed, 82 insertions(+) diff --git a/src/applications/differential/xaction/DifferentialRevisionAbandonTransaction.php b/src/applications/differential/xaction/DifferentialRevisionAbandonTransaction.php index 627e1c9d37..2f221bd8c6 100644 --- a/src/applications/differential/xaction/DifferentialRevisionAbandonTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionAbandonTransaction.php @@ -26,6 +26,10 @@ final class DifferentialRevisionAbandonTransaction return 500; } + public function getActionName() { + return pht('Abandoned'); + } + public function getCommandKeyword() { return 'abandon'; } diff --git a/src/applications/differential/xaction/DifferentialRevisionAcceptTransaction.php b/src/applications/differential/xaction/DifferentialRevisionAcceptTransaction.php index dcb19c4037..df4e3b4d33 100644 --- a/src/applications/differential/xaction/DifferentialRevisionAcceptTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionAcceptTransaction.php @@ -26,6 +26,10 @@ final class DifferentialRevisionAcceptTransaction return 500; } + public function getActionName() { + return pht('Accepted'); + } + public function getCommandKeyword() { $accept_key = 'differential.enable-email-accept'; $allow_email_accept = PhabricatorEnv::getEnvConfig($accept_key); diff --git a/src/applications/differential/xaction/DifferentialRevisionActionTransaction.php b/src/applications/differential/xaction/DifferentialRevisionActionTransaction.php index dd80706b44..5507d1309d 100644 --- a/src/applications/differential/xaction/DifferentialRevisionActionTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionActionTransaction.php @@ -35,6 +35,10 @@ abstract class DifferentialRevisionActionTransaction return 1000; } + public function getActionStrength() { + return 3; + } + public function getRevisionActionOrderVector() { return id(new PhutilSortVector()) ->addInt($this->getRevisionActionOrder()); diff --git a/src/applications/differential/xaction/DifferentialRevisionCloseTransaction.php b/src/applications/differential/xaction/DifferentialRevisionCloseTransaction.php index c2ba6c1bb8..30bfd5044f 100644 --- a/src/applications/differential/xaction/DifferentialRevisionCloseTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionCloseTransaction.php @@ -26,6 +26,10 @@ final class DifferentialRevisionCloseTransaction return 300; } + public function getActionName() { + return pht('Closed'); + } + public function generateOldValue($object) { return $object->isClosed(); } diff --git a/src/applications/differential/xaction/DifferentialRevisionCommandeerTransaction.php b/src/applications/differential/xaction/DifferentialRevisionCommandeerTransaction.php index 57ecf5a039..4e4cd9600f 100644 --- a/src/applications/differential/xaction/DifferentialRevisionCommandeerTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionCommandeerTransaction.php @@ -26,6 +26,10 @@ final class DifferentialRevisionCommandeerTransaction return 700; } + public function getActionName() { + return pht('Commandeered'); + } + public function getCommandKeyword() { return 'commandeer'; } diff --git a/src/applications/differential/xaction/DifferentialRevisionPlanChangesTransaction.php b/src/applications/differential/xaction/DifferentialRevisionPlanChangesTransaction.php index 35c40273f6..e7cdaa2455 100644 --- a/src/applications/differential/xaction/DifferentialRevisionPlanChangesTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionPlanChangesTransaction.php @@ -27,6 +27,10 @@ final class DifferentialRevisionPlanChangesTransaction return 200; } + public function getActionName() { + return pht('Planned Changes'); + } + public function getCommandKeyword() { return 'planchanges'; } diff --git a/src/applications/differential/xaction/DifferentialRevisionReclaimTransaction.php b/src/applications/differential/xaction/DifferentialRevisionReclaimTransaction.php index 96a1f4415c..1f2f6a8d6c 100644 --- a/src/applications/differential/xaction/DifferentialRevisionReclaimTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionReclaimTransaction.php @@ -26,6 +26,10 @@ final class DifferentialRevisionReclaimTransaction return 600; } + public function getActionName() { + return pht('Reclaimed'); + } + public function getCommandKeyword() { return 'reclaim'; } diff --git a/src/applications/differential/xaction/DifferentialRevisionRejectTransaction.php b/src/applications/differential/xaction/DifferentialRevisionRejectTransaction.php index 808412ff78..96812de06b 100644 --- a/src/applications/differential/xaction/DifferentialRevisionRejectTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionRejectTransaction.php @@ -26,6 +26,10 @@ final class DifferentialRevisionRejectTransaction return 600; } + public function getActionName() { + return pht('Requested Changes'); + } + public function getCommandKeyword() { return 'request'; } diff --git a/src/applications/differential/xaction/DifferentialRevisionReopenTransaction.php b/src/applications/differential/xaction/DifferentialRevisionReopenTransaction.php index 5024ab99fa..84015b9286 100644 --- a/src/applications/differential/xaction/DifferentialRevisionReopenTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionReopenTransaction.php @@ -26,6 +26,10 @@ final class DifferentialRevisionReopenTransaction return 400; } + public function getActionName() { + return pht('Reopened'); + } + public function generateOldValue($object) { return !$object->isClosed(); } diff --git a/src/applications/differential/xaction/DifferentialRevisionRequestReviewTransaction.php b/src/applications/differential/xaction/DifferentialRevisionRequestReviewTransaction.php index cd1c14c66e..32fcb3271e 100644 --- a/src/applications/differential/xaction/DifferentialRevisionRequestReviewTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionRequestReviewTransaction.php @@ -22,6 +22,10 @@ final class DifferentialRevisionRequestReviewTransaction return 200; } + public function getActionName() { + return pht('Requested Review'); + } + public function generateOldValue($object) { $status_review = ArcanistDifferentialRevisionStatus::NEEDS_REVIEW; return ($object->getStatus() == $status_review); diff --git a/src/applications/differential/xaction/DifferentialRevisionResignTransaction.php b/src/applications/differential/xaction/DifferentialRevisionResignTransaction.php index 0d3045247d..5b75d7753f 100644 --- a/src/applications/differential/xaction/DifferentialRevisionResignTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionResignTransaction.php @@ -30,6 +30,10 @@ final class DifferentialRevisionResignTransaction return 'resign'; } + public function getActionName() { + return pht('Resigned'); + } + public function getCommandAliases() { return array(); } diff --git a/src/applications/diffusion/xaction/DiffusionCommitAcceptTransaction.php b/src/applications/diffusion/xaction/DiffusionCommitAcceptTransaction.php index e04310a53e..76c2de609e 100644 --- a/src/applications/diffusion/xaction/DiffusionCommitAcceptTransaction.php +++ b/src/applications/diffusion/xaction/DiffusionCommitAcceptTransaction.php @@ -26,6 +26,10 @@ final class DiffusionCommitAcceptTransaction return 500; } + public function getActionName() { + return pht('Accepted'); + } + public function generateOldValue($object) { $actor = $this->getActor(); return $this->isViewerAcceptingAuditor($object, $actor); diff --git a/src/applications/diffusion/xaction/DiffusionCommitConcernTransaction.php b/src/applications/diffusion/xaction/DiffusionCommitConcernTransaction.php index 01d4db01ad..6bbc27818f 100644 --- a/src/applications/diffusion/xaction/DiffusionCommitConcernTransaction.php +++ b/src/applications/diffusion/xaction/DiffusionCommitConcernTransaction.php @@ -26,6 +26,10 @@ final class DiffusionCommitConcernTransaction return 600; } + public function getActionName() { + return pht('Raised Concern'); + } + public function generateOldValue($object) { $actor = $this->getActor(); return $this->isViewerRejectingAuditor($object, $actor); diff --git a/src/applications/diffusion/xaction/DiffusionCommitResignTransaction.php b/src/applications/diffusion/xaction/DiffusionCommitResignTransaction.php index 4a68252414..103d0fabfe 100644 --- a/src/applications/diffusion/xaction/DiffusionCommitResignTransaction.php +++ b/src/applications/diffusion/xaction/DiffusionCommitResignTransaction.php @@ -26,6 +26,10 @@ final class DiffusionCommitResignTransaction return 700; } + public function getActionName() { + return pht('Resigned'); + } + public function generateOldValue($object) { $actor = $this->getActor(); return !$this->isViewerAnyActiveAuditor($object, $actor); diff --git a/src/applications/transactions/storage/PhabricatorModularTransaction.php b/src/applications/transactions/storage/PhabricatorModularTransaction.php index 035a2585fd..396478c98b 100644 --- a/src/applications/transactions/storage/PhabricatorModularTransaction.php +++ b/src/applications/transactions/storage/PhabricatorModularTransaction.php @@ -110,6 +110,24 @@ abstract class PhabricatorModularTransaction return parent::getTitle(); } + /* final */ public function getActionName() { + $action = $this->getTransactionImplementation()->getActionName(); + if ($action !== null) { + return $action; + } + + return parent::getActionName(); + } + + /* final */ public function getActionStrength() { + $strength = $this->getTransactionImplementation()->getActionStrength(); + if ($strength !== null) { + return $strength; + } + + return parent::getActionStrength(); + } + public function getTitleForMail() { $old_target = $this->getRenderingTarget(); $new_target = self::TARGET_TEXT; diff --git a/src/applications/transactions/storage/PhabricatorModularTransactionType.php b/src/applications/transactions/storage/PhabricatorModularTransactionType.php index 2606264778..f57d91b08f 100644 --- a/src/applications/transactions/storage/PhabricatorModularTransactionType.php +++ b/src/applications/transactions/storage/PhabricatorModularTransactionType.php @@ -59,6 +59,14 @@ abstract class PhabricatorModularTransactionType return null; } + public function getActionName() { + return null; + } + + public function getActionStrength() { + return null; + } + public function getColor() { return null; } From a635da68d41ecd5b07011f30a558164ab0786204 Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 12 Jan 2017 08:49:36 -0800 Subject: [PATCH 40/51] Provide bucketing for commits in Audit Summary: Fixes T9430. Fixes T9362. Fixes T9544. This changes the default view of Audit to work like Differential, where commits you need to audit or respond to are shown in buckets. This is a bit messy and probably needs some followups. This stuff has changed from a compatibility viewpoint: - The query works differently now (but in a better, modern way), so existing saved queries will need to be updated. - I've removed the counters from the home page instead of updating them, since they're going to get wiped out by ProfileMenu soon anyway. - When bucketed queries return too many results (more than 1,000) we now show a warning about it. This isn't greaaaat but it seems good enough for now. Test Plan: {F2351123} Reviewers: chad Reviewed By: chad Maniphest Tasks: T9430, T9362, T9544 Differential Revision: https://secure.phabricator.com/D17192 --- src/__phutil_library_map__.php | 4 + .../PhabricatorAuditApplication.php | 53 ------ .../conduit/AuditQueryConduitAPIMethod.php | 50 ++++-- .../PhabricatorAuditCommitStatusConstants.php | 1 + .../PhabricatorAuditStatusConstants.php | 7 + ...abricatorAuditManagementDeleteWorkflow.php | 5 +- .../query/PhabricatorCommitSearchEngine.php | 150 ++++++++--------- .../audit/view/PhabricatorAuditListView.php | 64 +++----- .../diffusion/query/DiffusionCommitQuery.php | 130 +++++---------- ...fusionCommitRequiredActionResultBucket.php | 151 ++++++++++++++++++ .../query/DiffusionCommitResultBucket.php | 33 ++++ .../PhabricatorOwnersDetailController.php | 19 +-- .../policy/filter/PhabricatorPolicyFilter.php | 22 ++- ...PhabricatorApplicationSearchController.php | 25 +++ .../PhabricatorApplicationSearchEngine.php | 2 +- 15 files changed, 422 insertions(+), 294 deletions(-) create mode 100644 src/applications/diffusion/query/DiffusionCommitRequiredActionResultBucket.php create mode 100644 src/applications/diffusion/query/DiffusionCommitResultBucket.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index a644231520..e704ece353 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -657,7 +657,9 @@ phutil_register_library_map(array( 'DiffusionCommitRemarkupRuleTestCase' => 'applications/diffusion/remarkup/__tests__/DiffusionCommitRemarkupRuleTestCase.php', 'DiffusionCommitRepositoryHeraldField' => 'applications/diffusion/herald/DiffusionCommitRepositoryHeraldField.php', 'DiffusionCommitRepositoryProjectsHeraldField' => 'applications/diffusion/herald/DiffusionCommitRepositoryProjectsHeraldField.php', + 'DiffusionCommitRequiredActionResultBucket' => 'applications/diffusion/query/DiffusionCommitRequiredActionResultBucket.php', 'DiffusionCommitResignTransaction' => 'applications/diffusion/xaction/DiffusionCommitResignTransaction.php', + 'DiffusionCommitResultBucket' => 'applications/diffusion/query/DiffusionCommitResultBucket.php', 'DiffusionCommitRevertedByCommitEdgeType' => 'applications/diffusion/edge/DiffusionCommitRevertedByCommitEdgeType.php', 'DiffusionCommitRevertsCommitEdgeType' => 'applications/diffusion/edge/DiffusionCommitRevertsCommitEdgeType.php', 'DiffusionCommitReviewerHeraldField' => 'applications/diffusion/herald/DiffusionCommitReviewerHeraldField.php', @@ -5361,7 +5363,9 @@ phutil_register_library_map(array( 'DiffusionCommitRemarkupRuleTestCase' => 'PhabricatorTestCase', 'DiffusionCommitRepositoryHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitRepositoryProjectsHeraldField' => 'DiffusionCommitHeraldField', + 'DiffusionCommitRequiredActionResultBucket' => 'DiffusionCommitResultBucket', 'DiffusionCommitResignTransaction' => 'DiffusionCommitAuditTransaction', + 'DiffusionCommitResultBucket' => 'PhabricatorSearchResultBucket', 'DiffusionCommitRevertedByCommitEdgeType' => 'PhabricatorEdgeType', 'DiffusionCommitRevertsCommitEdgeType' => 'PhabricatorEdgeType', 'DiffusionCommitReviewerHeraldField' => 'DiffusionCommitHeraldField', diff --git a/src/applications/audit/application/PhabricatorAuditApplication.php b/src/applications/audit/application/PhabricatorAuditApplication.php index 9eb9cd0e09..7d8ad94aa7 100644 --- a/src/applications/audit/application/PhabricatorAuditApplication.php +++ b/src/applications/audit/application/PhabricatorAuditApplication.php @@ -34,57 +34,4 @@ final class PhabricatorAuditApplication extends PhabricatorApplication { return 0.130; } - public function loadStatus(PhabricatorUser $user) { - $status = array(); - $limit = self::MAX_STATUS_ITEMS; - - $phids = PhabricatorAuditCommentEditor::loadAuditPHIDsForUser($user); - - $query = id(new DiffusionCommitQuery()) - ->setViewer($user) - ->withAuthorPHIDs(array($user->getPHID())) - ->withAuditStatus(DiffusionCommitQuery::AUDIT_STATUS_CONCERN) - ->setLimit($limit); - $commits = $query->execute(); - - $count = count($commits); - if ($count >= $limit) { - $count_str = pht('%s+ Problem Commits', new PhutilNumber($limit - 1)); - } else { - $count_str = pht('%s Problem Commit(s)', new PhutilNumber($count)); - } - - $type = PhabricatorApplicationStatusView::TYPE_NEEDS_ATTENTION; - $status[] = id(new PhabricatorApplicationStatusView()) - ->setType($type) - ->setText($count_str) - ->setCount($count); - - $query = id(new DiffusionCommitQuery()) - ->setViewer($user) - ->withNeedsAuditByPHIDs($phids) - ->withAuditStatus(DiffusionCommitQuery::AUDIT_STATUS_OPEN) - ->setLimit($limit); - $commits = $query->execute(); - - $count = count($commits); - if ($count >= $limit) { - $count_str = pht( - '%s+ Commits Awaiting Audit', - new PhutilNumber($limit - 1)); - } else { - $count_str = pht( - '%s Commit(s) Awaiting Audit', - new PhutilNumber($count)); - } - - $type = PhabricatorApplicationStatusView::TYPE_WARNING; - $status[] = id(new PhabricatorApplicationStatusView()) - ->setType($type) - ->setText($count_str) - ->setCount($count); - - return $status; - } - } diff --git a/src/applications/audit/conduit/AuditQueryConduitAPIMethod.php b/src/applications/audit/conduit/AuditQueryConduitAPIMethod.php index 97dbec3d68..0f2fe473f5 100644 --- a/src/applications/audit/conduit/AuditQueryConduitAPIMethod.php +++ b/src/applications/audit/conduit/AuditQueryConduitAPIMethod.php @@ -2,6 +2,12 @@ final class AuditQueryConduitAPIMethod extends AuditConduitAPIMethod { + const AUDIT_LEGACYSTATUS_ANY = 'audit-status-any'; + const AUDIT_LEGACYSTATUS_OPEN = 'audit-status-open'; + const AUDIT_LEGACYSTATUS_CONCERN = 'audit-status-concern'; + const AUDIT_LEGACYSTATUS_ACCEPTED = 'audit-status-accepted'; + const AUDIT_LEGACYSTATUS_PARTIAL = 'audit-status-partial'; + public function getAPIMethodName() { return 'audit.query'; } @@ -10,13 +16,23 @@ final class AuditQueryConduitAPIMethod extends AuditConduitAPIMethod { return pht('Query audit requests.'); } + public function getMethodStatus() { + return self::METHOD_STATUS_FROZEN; + } + + public function getMethodStatusDescription() { + return pht( + 'This method is frozen and will eventually be deprecated. New code '. + 'should use "diffusion.commit.search" instead.'); + } + protected function defineParamTypes() { $statuses = array( - DiffusionCommitQuery::AUDIT_STATUS_ANY, - DiffusionCommitQuery::AUDIT_STATUS_OPEN, - DiffusionCommitQuery::AUDIT_STATUS_CONCERN, - DiffusionCommitQuery::AUDIT_STATUS_ACCEPTED, - DiffusionCommitQuery::AUDIT_STATUS_PARTIAL, + self::AUDIT_LEGACYSTATUS_ANY, + self::AUDIT_LEGACYSTATUS_OPEN, + self::AUDIT_LEGACYSTATUS_CONCERN, + self::AUDIT_LEGACYSTATUS_ACCEPTED, + self::AUDIT_LEGACYSTATUS_PARTIAL, ); $status_const = $this->formatStringConstants($statuses); @@ -50,10 +66,26 @@ final class AuditQueryConduitAPIMethod extends AuditConduitAPIMethod { $query->withPHIDs($commit_phids); } - $status = $request->getValue( - 'status', - DiffusionCommitQuery::AUDIT_STATUS_ANY); - $query->withAuditStatus($status); + $status_map = array( + self::AUDIT_LEGACYSTATUS_OPEN => array( + PhabricatorAuditCommitStatusConstants::NEEDS_AUDIT, + PhabricatorAuditCommitStatusConstants::CONCERN_RAISED, + ), + self::AUDIT_LEGACYSTATUS_CONCERN => array( + PhabricatorAuditCommitStatusConstants::CONCERN_RAISED, + ), + self::AUDIT_LEGACYSTATUS_ACCEPTED => array( + PhabricatorAuditCommitStatusConstants::CONCERN_ACCEPTED, + ), + self::AUDIT_LEGACYSTATUS_PARTIAL => array( + PhabricatorAuditCommitStatusConstants::PARTIALLY_AUDITED, + ), + ); + + $status = $request->getValue('status'); + if (isset($status_map[$status])) { + $query->withStatuses($status_map[$status]); + } // NOTE: These affect the number of commits identified, which is sort of // reasonable but means the method may return an arbitrary number of diff --git a/src/applications/audit/constants/PhabricatorAuditCommitStatusConstants.php b/src/applications/audit/constants/PhabricatorAuditCommitStatusConstants.php index ca959d4aef..7c015efc43 100644 --- a/src/applications/audit/constants/PhabricatorAuditCommitStatusConstants.php +++ b/src/applications/audit/constants/PhabricatorAuditCommitStatusConstants.php @@ -28,6 +28,7 @@ final class PhabricatorAuditCommitStatusConstants extends Phobject { return array( self::CONCERN_RAISED, self::NEEDS_AUDIT, + self::PARTIALLY_AUDITED, ); } diff --git a/src/applications/audit/constants/PhabricatorAuditStatusConstants.php b/src/applications/audit/constants/PhabricatorAuditStatusConstants.php index 5920b7f357..9685e152e9 100644 --- a/src/applications/audit/constants/PhabricatorAuditStatusConstants.php +++ b/src/applications/audit/constants/PhabricatorAuditStatusConstants.php @@ -28,6 +28,13 @@ final class PhabricatorAuditStatusConstants extends Phobject { return $map; } + public static function getActionRequiredStatusConstants() { + return array( + self::AUDIT_REQUIRED, + self::AUDIT_REQUESTED, + ); + } + public static function getStatusName($code) { return idx(self::getStatusNameMap(), $code, pht('Unknown')); } diff --git a/src/applications/audit/management/PhabricatorAuditManagementDeleteWorkflow.php b/src/applications/audit/management/PhabricatorAuditManagementDeleteWorkflow.php index 95fbdb430f..23583422fb 100644 --- a/src/applications/audit/management/PhabricatorAuditManagementDeleteWorkflow.php +++ b/src/applications/audit/management/PhabricatorAuditManagementDeleteWorkflow.php @@ -67,9 +67,6 @@ final class PhabricatorAuditManagementDeleteWorkflow $ids = $this->parseList($args->getArg('ids')); $status = $args->getArg('status'); - if (!$status) { - $status = DiffusionCommitQuery::AUDIT_STATUS_OPEN; - } $min_date = $this->loadDate($args->getArg('min-commit-date')); $max_date = $this->loadDate($args->getArg('max-commit-date')); @@ -85,7 +82,7 @@ final class PhabricatorAuditManagementDeleteWorkflow ->needAuditRequests(true); if ($status) { - $query->withAuditStatus($status); + $query->withStatuses(array($status)); } $id_map = array(); diff --git a/src/applications/audit/query/PhabricatorCommitSearchEngine.php b/src/applications/audit/query/PhabricatorCommitSearchEngine.php index 40bab34264..c6c37208f8 100644 --- a/src/applications/audit/query/PhabricatorCommitSearchEngine.php +++ b/src/applications/audit/query/PhabricatorCommitSearchEngine.php @@ -17,23 +17,27 @@ final class PhabricatorCommitSearchEngine ->needCommitData(true); } + protected function newResultBuckets() { + return DiffusionCommitResultBucket::getAllResultBuckets(); + } + protected function buildQueryFromParameters(array $map) { $query = $this->newQuery(); - if ($map['needsAuditByPHIDs']) { - $query->withNeedsAuditByPHIDs($map['needsAuditByPHIDs']); + if ($map['responsiblePHIDs']) { + $query->withResponsiblePHIDs($map['responsiblePHIDs']); } if ($map['auditorPHIDs']) { $query->withAuditorPHIDs($map['auditorPHIDs']); } - if ($map['commitAuthorPHIDs']) { - $query->withAuthorPHIDs($map['commitAuthorPHIDs']); + if ($map['authorPHIDs']) { + $query->withAuthorPHIDs($map['authorPHIDs']); } - if ($map['auditStatus']) { - $query->withAuditStatus($map['auditStatus']); + if ($map['statuses']) { + $query->withStatuses($map['statuses']); } if ($map['repositoryPHIDs']) { @@ -46,28 +50,32 @@ final class PhabricatorCommitSearchEngine protected function buildCustomSearchFields() { return array( id(new PhabricatorSearchDatasourceField()) - ->setLabel(pht('Needs Audit By')) - ->setKey('needsAuditByPHIDs') - ->setAliases(array('needs', 'need')) - ->setDatasource(new DiffusionAuditorFunctionDatasource()), + ->setLabel(pht('Responsible Users')) + ->setKey('responsiblePHIDs') + ->setConduitKey('responsible') + ->setAliases(array('responsible', 'responsibles', 'responsiblePHID')) + ->setDatasource(new DifferentialResponsibleDatasource()), + id(new PhabricatorUsersSearchField()) + ->setLabel(pht('Authors')) + ->setKey('authorPHIDs') + ->setConduitKey('authors') + ->setAliases(array('author', 'authors', 'authorPHID')), id(new PhabricatorSearchDatasourceField()) ->setLabel(pht('Auditors')) ->setKey('auditorPHIDs') - ->setAliases(array('auditor', 'auditors')) + ->setConduitKey('auditors') + ->setAliases(array('auditor', 'auditors', 'auditorPHID')) ->setDatasource(new DiffusionAuditorFunctionDatasource()), - id(new PhabricatorUsersSearchField()) - ->setLabel(pht('Authors')) - ->setKey('commitAuthorPHIDs') - ->setAliases(array('author', 'authors')), - id(new PhabricatorSearchSelectField()) + id(new PhabricatorSearchCheckboxesField()) ->setLabel(pht('Audit Status')) - ->setKey('auditStatus') + ->setKey('statuses') ->setAliases(array('status')) - ->setOptions($this->getAuditStatusOptions()), + ->setOptions(PhabricatorAuditCommitStatusConstants::getStatusNameMap()), id(new PhabricatorSearchDatasourceField()) ->setLabel(pht('Repositories')) ->setKey('repositoryPHIDs') - ->setAliases(array('repository', 'repositories')) + ->setConduitKey('repositories') + ->setAliases(array('repository', 'repositories', 'repositoryPHID')) ->setDatasource(new DiffusionRepositoryDatasource()), ); } @@ -80,14 +88,9 @@ final class PhabricatorCommitSearchEngine $names = array(); if ($this->requireViewer()->isLoggedIn()) { - $names['need'] = pht('Needs Audit'); - $names['problem'] = pht('Problem Commits'); - } - - $names['open'] = pht('Open Audits'); - - if ($this->requireViewer()->isLoggedIn()) { - $names['authored'] = pht('Authored Commits'); + $names['active'] = pht('Active Audits'); + $names['authored'] = pht('Authored'); + $names['audited'] = pht('Audited'); } $names['all'] = pht('All Commits'); @@ -101,76 +104,75 @@ final class PhabricatorCommitSearchEngine $viewer = $this->requireViewer(); $viewer_phid = $viewer->getPHID(); - $status_open = DiffusionCommitQuery::AUDIT_STATUS_OPEN; - switch ($query_key) { case 'all': return $query; - case 'open': - $query->setParameter('auditStatus', $status_open); - return $query; - case 'need': - $needs_tokens = array( - $viewer_phid, - 'projects('.$viewer_phid.')', - 'packages('.$viewer_phid.')', - ); + case 'active': + $bucket_key = DiffusionCommitRequiredActionResultBucket::BUCKETKEY; - $query->setParameter('needsAuditByPHIDs', $needs_tokens); - $query->setParameter('auditStatus', $status_open); + $open = PhabricatorAuditCommitStatusConstants::getOpenStatusConstants(); + + $query + ->setParameter('responsiblePHIDs', array($viewer_phid)) + ->setParameter('statuses', $open) + ->setParameter('bucket', $bucket_key); return $query; case 'authored': - $query->setParameter('commitAuthorPHIDs', array($viewer->getPHID())); + $query + ->setParameter('authorPHIDs', array($viewer_phid)); return $query; - case 'problem': - $query->setParameter('commitAuthorPHIDs', array($viewer->getPHID())); - $query->setParameter( - 'auditStatus', - DiffusionCommitQuery::AUDIT_STATUS_CONCERN); + case 'audited': + $query + ->setParameter('auditorPHIDs', array($viewer_phid)); return $query; } return parent::buildSavedQueryFromBuiltin($query_key); } - private function getAuditStatusOptions() { - return array( - DiffusionCommitQuery::AUDIT_STATUS_ANY => pht('Any'), - DiffusionCommitQuery::AUDIT_STATUS_OPEN => pht('Open'), - DiffusionCommitQuery::AUDIT_STATUS_CONCERN => pht('Concern Raised'), - DiffusionCommitQuery::AUDIT_STATUS_ACCEPTED => pht('Accepted'), - DiffusionCommitQuery::AUDIT_STATUS_PARTIAL => pht('Partially Audited'), - ); - } - protected function renderResultList( array $commits, PhabricatorSavedQuery $query, array $handles) { - assert_instances_of($commits, 'PhabricatorRepositoryCommit'); - $viewer = $this->requireViewer(); - $nodata = pht('No matching audits.'); - $view = id(new PhabricatorAuditListView()) - ->setUser($viewer) - ->setCommits($commits) - ->setAuthorityPHIDs( - PhabricatorAuditCommentEditor::loadAuditPHIDsForUser($viewer)) - ->setNoDataString($nodata); - $phids = $view->getRequiredHandlePHIDs(); - if ($phids) { - $handles = id(new PhabricatorHandleQuery()) - ->setViewer($viewer) - ->withPHIDs($phids) - ->execute(); + $bucket = $this->getResultBucket($query); + + $authority_phids = PhabricatorAuditCommentEditor::loadAuditPHIDsForUser( + $viewer); + + $template = id(new PhabricatorAuditListView()) + ->setViewer($viewer) + ->setAuthorityPHIDs($authority_phids); + + $views = array(); + if ($bucket) { + $bucket->setViewer($viewer); + + try { + $groups = $bucket->newResultGroups($query, $commits); + + foreach ($groups as $group) { + $views[] = id(clone $template) + ->setHeader($group->getName()) + ->setNoDataString($group->getNoDataString()) + ->setCommits($group->getObjects()); + } + } catch (Exception $ex) { + $this->addError($ex->getMessage()); + } } else { - $handles = array(); + $views[] = id(clone $template) + ->setCommits($commits) + ->setNoDataString(pht('No matching commits.')); } - $view->setHandles($handles); - $list = $view->buildList(); + if (count($views) == 1) { + $list = head($views)->buildList(); + } else { + $list = $views; + } $result = new PhabricatorApplicationSearchResultView(); $result->setContent($list); diff --git a/src/applications/audit/view/PhabricatorAuditListView.php b/src/applications/audit/view/PhabricatorAuditListView.php index 593172006c..c359833729 100644 --- a/src/applications/audit/view/PhabricatorAuditListView.php +++ b/src/applications/audit/view/PhabricatorAuditListView.php @@ -3,18 +3,11 @@ final class PhabricatorAuditListView extends AphrontView { private $commits; - private $handles; private $authorityPHIDs = array(); + private $header; private $noDataString; - private $highlightedAudits; - public function setHandles(array $handles) { - assert_instances_of($handles, 'PhabricatorObjectHandle'); - $this->handles = $handles; - return $this; - } - public function setAuthorityPHIDs(array $phids) { $this->authorityPHIDs = $phids; return $this; @@ -29,6 +22,15 @@ final class PhabricatorAuditListView extends AphrontView { return $this->noDataString; } + public function setHeader($header) { + $this->header = $header; + return $this; + } + + public function getHeader() { + return $this->header; + } + /** * These commits should have both commit data and audit requests attached. */ @@ -42,28 +44,6 @@ final class PhabricatorAuditListView extends AphrontView { return $this->commits; } - public function getRequiredHandlePHIDs() { - $phids = array(); - $commits = $this->getCommits(); - foreach ($commits as $commit) { - $phids[$commit->getPHID()] = true; - $phids[$commit->getAuthorPHID()] = true; - $audits = $commit->getAudits(); - foreach ($audits as $audit) { - $phids[$audit->getAuditorPHID()] = true; - } - } - return array_keys($phids); - } - - private function getHandle($phid) { - $handle = idx($this->handles, $phid); - if (!$handle) { - throw new Exception(pht("No handle for '%s'!", $phid)); - } - return $handle; - } - private function getCommitDescription($phid) { if ($this->commits === null) { return pht('(Unknown Commit)'); @@ -96,34 +76,28 @@ final class PhabricatorAuditListView extends AphrontView { } public function buildList() { - $user = $this->getUser(); - if (!$user) { - throw new Exception( - pht( - 'You must %s before %s!', - 'setUser()', - __FUNCTION__.'()')); - } + $viewer = $this->getViewer(); $rowc = array(); + $handles = $viewer->loadHandles(mpull($this->commits, 'getPHID')); + $list = new PHUIObjectItemListView(); foreach ($this->commits as $commit) { $commit_phid = $commit->getPHID(); - $commit_handle = $this->getHandle($commit_phid); + $commit_handle = $handles[$commit_phid]; $committed = null; $commit_name = $commit_handle->getName(); $commit_link = $commit_handle->getURI(); $commit_desc = $this->getCommitDescription($commit_phid); - $committed = phabricator_datetime($commit->getEpoch(), $user); + $committed = phabricator_datetime($commit->getEpoch(), $viewer); $audits = mpull($commit->getAudits(), null, 'getAuditorPHID'); $auditors = array(); $reasons = array(); foreach ($audits as $audit) { $auditor_phid = $audit->getAuditorPHID(); - $auditors[$auditor_phid] = - $this->getHandle($auditor_phid)->renderLink(); + $auditors[$auditor_phid] = $viewer->renderHandle($auditor_phid); } $auditors = phutil_implode_html(', ', $auditors); @@ -151,7 +125,7 @@ final class PhabricatorAuditListView extends AphrontView { } $author_phid = $commit->getAuthorPHID(); if ($author_phid) { - $author_name = $this->getHandle($author_phid)->renderLink(); + $author_name = $viewer->renderHandle($author_phid); } else { $author_name = $commit->getCommitData()->getAuthorName(); } @@ -180,6 +154,10 @@ final class PhabricatorAuditListView extends AphrontView { $list->setNoDataString($this->noDataString); } + if ($this->header) { + $list->setHeader($this->header); + } + return $list; } diff --git a/src/applications/diffusion/query/DiffusionCommitQuery.php b/src/applications/diffusion/query/DiffusionCommitQuery.php index 4aeca4c38e..a24f8038e0 100644 --- a/src/applications/diffusion/query/DiffusionCommitQuery.php +++ b/src/applications/diffusion/query/DiffusionCommitQuery.php @@ -11,22 +11,16 @@ final class DiffusionCommitQuery private $repositoryIDs; private $repositoryPHIDs; private $identifierMap; + private $responsiblePHIDs; + private $statuses; private $needAuditRequests; private $auditIDs; private $auditorPHIDs; - private $needsAuditByPHIDs; - private $auditStatus; private $epochMin; private $epochMax; private $importing; - const AUDIT_STATUS_ANY = 'audit-status-any'; - const AUDIT_STATUS_OPEN = 'audit-status-open'; - const AUDIT_STATUS_CONCERN = 'audit-status-concern'; - const AUDIT_STATUS_ACCEPTED = 'audit-status-accepted'; - const AUDIT_STATUS_PARTIAL = 'audit-status-partial'; - private $needCommitData; public function withIDs(array $ids) { @@ -119,14 +113,22 @@ final class DiffusionCommitQuery return $this; } - public function withNeedsAuditByPHIDs(array $needs_phids) { - $this->needsAuditByPHIDs = $needs_phids; + public function withResponsiblePHIDs(array $responsible_phids) { + $this->responsiblePHIDs = $responsible_phids; + return $this; + } + + public function withStatuses(array $statuses) { + $this->statuses = $statuses; return $this; } public function withAuditStatus($status) { - $this->auditStatus = $status; - return $this; + // TODO: Replace callers with `withStatuses()`. + return $this->withStatuses( + array( + $status, + )); } public function withEpochRange($min, $max) { @@ -251,10 +253,7 @@ final class DiffusionCommitQuery } } - // TODO: This should just be `needAuditRequests`, not `shouldJoinAudits()`, - // but leave that for a future diff. - - if ($this->needAuditRequests || $this->shouldJoinAudits()) { + if ($this->needAuditRequests) { $requests = id(new PhabricatorRepositoryAuditRequest())->loadAllWhere( 'commitPHID IN (%Ls)', mpull($commits, 'getPHID')); @@ -459,67 +458,30 @@ final class DiffusionCommitQuery if ($this->auditIDs !== null) { $where[] = qsprintf( $conn, - 'audit.id IN (%Ld)', + 'auditor.id IN (%Ld)', $this->auditIDs); } if ($this->auditorPHIDs !== null) { $where[] = qsprintf( $conn, - 'audit.auditorPHID IN (%Ls)', + 'auditor.auditorPHID IN (%Ls)', $this->auditorPHIDs); } - if ($this->needsAuditByPHIDs !== null) { + if ($this->responsiblePHIDs !== null) { $where[] = qsprintf( $conn, - 'needs.auditorPHID IN (%Ls)', - $this->needsAuditByPHIDs); + '(audit.auditorPHID IN (%Ls) OR commit.authorPHID IN (%Ls))', + $this->responsiblePHIDs, + $this->responsiblePHIDs); } - $status = $this->auditStatus; - if ($status !== null) { - switch ($status) { - case self::AUDIT_STATUS_PARTIAL: - $where[] = qsprintf( - $conn, - 'commit.auditStatus = %d', - PhabricatorAuditCommitStatusConstants::PARTIALLY_AUDITED); - break; - case self::AUDIT_STATUS_ACCEPTED: - $where[] = qsprintf( - $conn, - 'commit.auditStatus = %d', - PhabricatorAuditCommitStatusConstants::FULLY_AUDITED); - break; - case self::AUDIT_STATUS_CONCERN: - $where[] = qsprintf( - $conn, - 'status.auditStatus = %s', - PhabricatorAuditStatusConstants::CONCERNED); - break; - case self::AUDIT_STATUS_OPEN: - $where[] = qsprintf( - $conn, - 'status.auditStatus in (%Ls)', - PhabricatorAuditStatusConstants::getOpenStatusConstants()); - break; - case self::AUDIT_STATUS_ANY: - break; - default: - $valid = array( - self::AUDIT_STATUS_ANY, - self::AUDIT_STATUS_OPEN, - self::AUDIT_STATUS_CONCERN, - self::AUDIT_STATUS_ACCEPTED, - self::AUDIT_STATUS_PARTIAL, - ); - throw new Exception( - pht( - "Unknown audit status '%s'! Valid statuses are: %s.", - $status, - implode(', ', $valid))); - } + if ($this->statuses !== null) { + $where[] = qsprintf( + $conn, + 'commit.auditStatus IN (%Ls)', + $this->statuses); } return $where; @@ -535,61 +497,41 @@ final class DiffusionCommitQuery } } - private function shouldJoinStatus() { - return $this->auditStatus; + private function shouldJoinAuditor() { + return ($this->auditIDs || $this->auditorPHIDs); } - private function shouldJoinAudits() { - return $this->auditIDs || $this->auditorPHIDs; - } - - private function shouldJoinNeeds() { - return $this->needsAuditByPHIDs; + private function shouldJoinAudit() { + return (bool)$this->responsiblePHIDs; } protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $join = parent::buildJoinClauseParts($conn); $audit_request = new PhabricatorRepositoryAuditRequest(); - if ($this->shouldJoinStatus()) { + if ($this->shouldJoinAuditor()) { $join[] = qsprintf( $conn, - 'LEFT JOIN %T status ON commit.phid = status.commitPHID', + 'JOIN %T auditor ON commit.phid = auditor.commitPHID', $audit_request->getTableName()); } - if ($this->shouldJoinAudits()) { + if ($this->shouldJoinAudit()) { $join[] = qsprintf( $conn, - 'JOIN %T audit ON commit.phid = audit.commitPHID', + 'LEFT JOIN %T audit ON commit.phid = audit.commitPHID', $audit_request->getTableName()); } - if ($this->shouldJoinNeeds()) { - $join[] = qsprintf( - $conn, - 'JOIN %T needs ON commit.phid = needs.commitPHID - AND needs.auditStatus IN (%Ls)', - $audit_request->getTableName(), - array( - PhabricatorAuditStatusConstants::AUDIT_REQUESTED, - PhabricatorAuditStatusConstants::AUDIT_REQUIRED, - )); - } - return $join; } protected function shouldGroupQueryResultRows() { - if ($this->shouldJoinStatus()) { + if ($this->shouldJoinAuditor()) { return true; } - if ($this->shouldJoinAudits()) { - return true; - } - - if ($this->shouldJoinNeeds()) { + if ($this->shouldJoinAudit()) { return true; } diff --git a/src/applications/diffusion/query/DiffusionCommitRequiredActionResultBucket.php b/src/applications/diffusion/query/DiffusionCommitRequiredActionResultBucket.php new file mode 100644 index 0000000000..ad80cddf39 --- /dev/null +++ b/src/applications/diffusion/query/DiffusionCommitRequiredActionResultBucket.php @@ -0,0 +1,151 @@ +objects = $objects; + + $phids = $query->getEvaluatedParameter('responsiblePHIDs', array()); + if (!$phids) { + throw new Exception( + pht( + 'You can not bucket results by required action without '. + 'specifying "Responsible Users".')); + } + $phids = array_fuse($phids); + + $groups = array(); + + $groups[] = $this->newGroup() + ->setName(pht('Needs Attention')) + ->setNoDataString(pht('None of your commits have active concerns.')) + ->setObjects($this->filterConcernRaised($phids)); + + $groups[] = $this->newGroup() + ->setName(pht('Ready to Audit')) + ->setNoDataString(pht('No commits are waiting for you to audit them.')) + ->setObjects($this->filterShouldAudit($phids)); + + $groups[] = $this->newGroup() + ->setName(pht('Waiting on Authors')) + ->setNoDataString(pht('None of your audits are waiting on authors.')) + ->setObjects($this->filterWaitingOnAuthors($phids)); + + $groups[] = $this->newGroup() + ->setName(pht('Waiting on Auditors')) + ->setNoDataString(pht('None of your commits are waiting on audit.')) + ->setObjects($this->filterWaitingOnAuditors($phids)); + + // Because you can apply these buckets to queries which include revisions + // that have been closed, add an "Other" bucket if we still have stuff + // that didn't get filtered into any of the previous buckets. + if ($this->objects) { + $groups[] = $this->newGroup() + ->setName(pht('Other Commits')) + ->setObjects($this->objects); + } + + return $groups; + } + + private function filterConcernRaised(array $phids) { + $results = array(); + $objects = $this->objects; + + $status_concern = PhabricatorAuditCommitStatusConstants::CONCERN_RAISED; + + foreach ($objects as $key => $object) { + if (empty($phids[$object->getAuthorPHID()])) { + continue; + } + + if ($object->getAuditStatus() != $status_concern) { + continue; + } + + $results[$key] = $object; + unset($this->objects[$key]); + } + + return $results; + } + + private function filterShouldAudit(array $phids) { + $results = array(); + $objects = $this->objects; + + $should_audit = array( + PhabricatorAuditStatusConstants::AUDIT_REQUIRED, + PhabricatorAuditStatusConstants::AUDIT_REQUESTED, + ); + $should_audit = array_fuse($should_audit); + + foreach ($objects as $key => $object) { + if (!$this->hasAuditorsWithStatus($object, $phids, $should_audit)) { + continue; + } + + $results[$key] = $object; + unset($this->objects[$key]); + } + + return $results; + } + + private function filterWaitingOnAuthors(array $phids) { + $results = array(); + $objects = $this->objects; + + $status_concern = PhabricatorAuditCommitStatusConstants::CONCERN_RAISED; + + foreach ($objects as $key => $object) { + if ($object->getAuditStatus() != $status_concern) { + continue; + } + + if (isset($phids[$object->getAuthorPHID()])) { + continue; + } + + $results[$key] = $object; + unset($this->objects[$key]); + } + + return $results; + } + + private function filterWaitingOnAuditors(array $phids) { + $results = array(); + $objects = $this->objects; + + $status_waiting = array( + PhabricatorAuditCommitStatusConstants::NEEDS_AUDIT, + PhabricatorAuditCommitStatusConstants::PARTIALLY_AUDITED, + ); + $status_waiting = array_fuse($status_waiting); + + foreach ($objects as $key => $object) { + if (empty($status_waiting[$object->getAuditStatus()])) { + continue; + } + + $results[$key] = $object; + unset($this->objects[$key]); + } + + return $results; + } + +} diff --git a/src/applications/diffusion/query/DiffusionCommitResultBucket.php b/src/applications/diffusion/query/DiffusionCommitResultBucket.php new file mode 100644 index 0000000000..28d80aa56c --- /dev/null +++ b/src/applications/diffusion/query/DiffusionCommitResultBucket.php @@ -0,0 +1,33 @@ +setAncestorClass(__CLASS__) + ->setUniqueMethod('getResultBucketKey') + ->execute(); + } + + protected function hasAuditorsWithStatus( + PhabricatorRepositoryCommit $commit, + array $phids, + array $statuses) { + + foreach ($commit->getAudits() as $audit) { + if (!isset($phids[$audit->getAuditorPHID()])) { + continue; + } + + if (!isset($statuses[$audit->getAuditStatus()])) { + continue; + } + + return true; + } + + return false; + } + +} diff --git a/src/applications/owners/controller/PhabricatorOwnersDetailController.php b/src/applications/owners/controller/PhabricatorOwnersDetailController.php index d7d249d8a5..b641290f56 100644 --- a/src/applications/owners/controller/PhabricatorOwnersDetailController.php +++ b/src/applications/owners/controller/PhabricatorOwnersDetailController.php @@ -71,13 +71,17 @@ final class PhabricatorOwnersDetailController 'auditorPHIDs' => $package->getPHID(), )); - $status_concern = DiffusionCommitQuery::AUDIT_STATUS_CONCERN; + $status_concern = PhabricatorAuditCommitStatusConstants::CONCERN_RAISED; $attention_commits = id(new DiffusionCommitQuery()) ->setViewer($request->getUser()) ->withAuditorPHIDs(array($package->getPHID())) - ->withAuditStatus($status_concern) + ->withStatuses( + array( + $status_concern, + )) ->needCommitData(true) + ->needAuditRequests(true) ->setLimit(10) ->execute(); $view = id(new PhabricatorAuditListView()) @@ -100,7 +104,8 @@ final class PhabricatorOwnersDetailController ->setViewer($request->getUser()) ->withAuditorPHIDs(array($package->getPHID())) ->needCommitData(true) - ->setLimit(100) + ->needAuditRequests(true) + ->setLimit(25) ->execute(); $view = id(new PhabricatorAuditListView()) @@ -119,13 +124,6 @@ final class PhabricatorOwnersDetailController ->setText(pht('View All')), ); - $phids = array(); - foreach ($commit_views as $commit_view) { - $phids[] = $commit_view['view']->getRequiredHandlePHIDs(); - } - $phids = array_mergev($phids); - $handles = $this->loadViewerHandles($phids); - $commit_panels = array(); foreach ($commit_views as $commit_view) { $commit_panel = id(new PHUIObjectBoxView()) @@ -136,7 +134,6 @@ final class PhabricatorOwnersDetailController if (isset($commit_view['button'])) { $commit_header->addActionLink($commit_view['button']); } - $commit_view['view']->setHandles($handles); $commit_panel->setHeader($commit_header); $commit_panel->appendChild($commit_view['view']); diff --git a/src/applications/policy/filter/PhabricatorPolicyFilter.php b/src/applications/policy/filter/PhabricatorPolicyFilter.php index e0c223cd2c..44931085fc 100644 --- a/src/applications/policy/filter/PhabricatorPolicyFilter.php +++ b/src/applications/policy/filter/PhabricatorPolicyFilter.php @@ -917,15 +917,27 @@ final class PhabricatorPolicyFilter extends Phobject { } private function getApplicationForPHID($phid) { - $phid_type = phid_get_type($phid); + static $class_map = array(); - $type_objects = PhabricatorPHIDType::getTypes(array($phid_type)); - $type_object = idx($type_objects, $phid_type); - if (!$type_object) { + $phid_type = phid_get_type($phid); + if (!isset($class_map[$phid_type])) { + $type_objects = PhabricatorPHIDType::getTypes(array($phid_type)); + $type_object = idx($type_objects, $phid_type); + if (!$type_object) { + $class = false; + } else { + $class = $type_object->getPHIDTypeApplicationClass(); + } + + $class_map[$phid_type] = $class; + } + + $class = $class_map[$phid_type]; + if ($class === false) { return null; } - return $type_object->getPHIDTypeApplicationClass(); + return $class; } } diff --git a/src/applications/search/controller/PhabricatorApplicationSearchController.php b/src/applications/search/controller/PhabricatorApplicationSearchController.php index 8937c124ea..cdd6b9335a 100644 --- a/src/applications/search/controller/PhabricatorApplicationSearchController.php +++ b/src/applications/search/controller/PhabricatorApplicationSearchController.php @@ -239,6 +239,10 @@ final class PhabricatorApplicationSearchController $nux_view = null; } + $is_overflowing = + $pager->willShowPagingControls() && + $engine->getResultBucket($saved_query); + $force_overheated = $request->getBool('overheated'); $is_overheated = $query->getIsOverheated() || $force_overheated; @@ -265,6 +269,11 @@ final class PhabricatorApplicationSearchController if ($list->getInfoView()) { $box->setInfoView($list->getInfoView()); } + + if ($is_overflowing) { + $box->appendChild($this->newOverflowingView()); + } + if ($list->getContent()) { $box->appendChild($list->getContent()); } @@ -545,6 +554,22 @@ final class PhabricatorApplicationSearchController ->setDropdownMenu($action_list); } + private function newOverflowingView() { + $message = pht( + 'The query matched more than one page of results. Results are '. + 'paginated before bucketing, so later pages may contain additional '. + 'results in any bucket.'); + + return id(new PHUIInfoView()) + ->setSeverity(PHUIInfoView::SEVERITY_WARNING) + ->setFlush(true) + ->setTitle(pht('Buckets Overflowing')) + ->setErrors( + array( + $message, + )); + } + private function newOverheatedView(array $results) { if ($results) { $message = pht( diff --git a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php index 26a9e0f8e4..d5ca8b7a5e 100644 --- a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php +++ b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php @@ -908,7 +908,7 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject { return array(); } - protected function getResultBucket(PhabricatorSavedQuery $saved) { + public function getResultBucket(PhabricatorSavedQuery $saved) { $key = $saved->getParameter('bucket'); if ($key == self::BUCKET_NONE) { return null; From 45c740ac9856bea4c132ddd933798724a6e34b5e Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 12 Jan 2017 12:25:36 -0800 Subject: [PATCH 41/51] Render revision and audit state icons in Maniphest Summary: Fixes T7076. This could probably use some tweaking but should get the basics in place. This shows overall object state (e.g., "Needs Review"), not individual viewer state (e.g., "you need to review this"). After the bucketing changes it seems like we're mostly in a reasonable place on showing global state instead of viewer state. This makes the overall change much easier than it might otherwise have been. Test Plan: {F2351867} Reviewers: chad Reviewed By: chad Maniphest Tasks: T7076 Differential Revision: https://secure.phabricator.com/D17193 --- .../phid/DifferentialRevisionPHIDType.php | 22 ++++++-- .../ManiphestTaskDetailController.php | 17 +++--- .../phid/PhabricatorObjectHandle.php | 52 +++++++++++++++++++ .../phid/view/PHUIHandleListView.php | 17 ++++++ src/applications/phid/view/PHUIHandleView.php | 15 ++++++ .../PhabricatorRepositoryCommitPHIDType.php | 10 ++++ 6 files changed, 123 insertions(+), 10 deletions(-) diff --git a/src/applications/differential/phid/DifferentialRevisionPHIDType.php b/src/applications/differential/phid/DifferentialRevisionPHIDType.php index b22c8b05cb..5a6cf701ae 100644 --- a/src/applications/differential/phid/DifferentialRevisionPHIDType.php +++ b/src/applications/differential/phid/DifferentialRevisionPHIDType.php @@ -33,16 +33,30 @@ final class DifferentialRevisionPHIDType extends PhabricatorPHIDType { $revision = $objects[$phid]; $title = $revision->getTitle(); - $id = $revision->getID(); $status = $revision->getStatus(); + $monogram = $revision->getMonogram(); + $uri = $revision->getURI(); - $handle->setName("D{$id}"); - $handle->setURI("/D{$id}"); - $handle->setFullName("D{$id}: {$title}"); + $handle + ->setName($monogram) + ->setURI($uri) + ->setFullName("{$monogram}: {$title}"); if ($revision->isClosed()) { $handle->setStatus(PhabricatorObjectHandle::STATUS_CLOSED); } + + $status = $revision->getStatus(); + + $icon = DifferentialRevisionStatus::getRevisionStatusIcon($status); + $color = DifferentialRevisionStatus::getRevisionStatusColor($status); + $name = ArcanistDifferentialRevisionStatus::getNameForRevisionStatus( + $status); + + $handle + ->setStateIcon($icon) + ->setStateColor($color) + ->setStateName($name); } } diff --git a/src/applications/maniphest/controller/ManiphestTaskDetailController.php b/src/applications/maniphest/controller/ManiphestTaskDetailController.php index f1157bcb9f..ca0fe5466e 100644 --- a/src/applications/maniphest/controller/ManiphestTaskDetailController.php +++ b/src/applications/maniphest/controller/ManiphestTaskDetailController.php @@ -397,7 +397,8 @@ final class ManiphestTaskDetailController extends ManiphestController { foreach ($commit_phids as $phid) { $revisions_commits[$phid] = $handles->renderHandle($phid) - ->setShowHovercard(true); + ->setShowHovercard(true) + ->setShowStateIcon(true); $revision_phid = key($drev_edges[$phid][$commit_drev]); $revision_handle = $handles->getHandleIfExists($revision_phid); if ($revision_handle) { @@ -412,12 +413,16 @@ final class ManiphestTaskDetailController extends ManiphestController { } foreach ($edge_types as $edge_type => $edge_name) { - if ($edges[$edge_type]) { - $edge_handles = $viewer->loadHandles(array_keys($edges[$edge_type])); - $view->addProperty( - $edge_name, - $edge_handles->renderList()); + if (!$edges[$edge_type]) { + continue; } + + $edge_handles = $viewer->loadHandles(array_keys($edges[$edge_type])); + + $edge_list = $edge_handles->renderList() + ->setShowStateIcons(true); + + $view->addProperty($edge_name, $edge_list); } if ($revisions_commits) { diff --git a/src/applications/phid/PhabricatorObjectHandle.php b/src/applications/phid/PhabricatorObjectHandle.php index 943464ed67..e6230c4ae6 100644 --- a/src/applications/phid/PhabricatorObjectHandle.php +++ b/src/applications/phid/PhabricatorObjectHandle.php @@ -31,6 +31,10 @@ final class PhabricatorObjectHandle private $tokenIcon; private $commandLineObjectName; + private $stateIcon; + private $stateColor; + private $stateName; + public function setIcon($icon) { $this->icon = $icon; return $this; @@ -284,6 +288,54 @@ final class PhabricatorObjectHandle return $this->complete; } + public function setStateIcon($state_icon) { + $this->stateIcon = $state_icon; + return $this; + } + + public function getStateIcon() { + return $this->stateIcon; + } + + public function setStateColor($state_color) { + $this->stateColor = $state_color; + return $this; + } + + public function getStateColor() { + return $this->stateColor; + } + + public function setStateName($state_name) { + $this->stateName = $state_name; + return $this; + } + + public function getStateName() { + return $this->stateName; + } + + public function renderStateIcon() { + $icon = $this->getStateIcon(); + if ($icon === null) { + $icon = 'fa-question-circle-o'; + } + + $color = $this->getStateColor(); + + $name = $this->getStateName(); + if ($name === null) { + $name = pht('Unknown'); + } + + return id(new PHUIIconView()) + ->setIcon($icon, $color) + ->addSigil('has-tooltip') + ->setMetadata( + array( + 'tip' => $name, + )); + } public function renderLink($name = null) { return $this->renderLinkWithAttributes($name, array()); diff --git a/src/applications/phid/view/PHUIHandleListView.php b/src/applications/phid/view/PHUIHandleListView.php index ed57da611b..d15b3d15c6 100644 --- a/src/applications/phid/view/PHUIHandleListView.php +++ b/src/applications/phid/view/PHUIHandleListView.php @@ -13,6 +13,7 @@ final class PHUIHandleListView private $handleList; private $asInline; private $asText; + private $showStateIcons; public function setHandleList(PhabricatorHandleList $list) { $this->handleList = $list; @@ -37,6 +38,15 @@ final class PHUIHandleListView return $this->asText; } + public function setShowStateIcons($show_state_icons) { + $this->showStateIcons = $show_state_icons; + return $this; + } + + public function getShowStateIcons() { + return $this->showStateIcons; + } + protected function getTagName() { if ($this->getAsText()) { return null; @@ -49,12 +59,19 @@ final class PHUIHandleListView protected function getTagContent() { $list = $this->handleList; + + $show_state_icons = $this->getShowStateIcons(); + $items = array(); foreach ($list as $handle) { $view = $list->renderHandle($handle->getPHID()) ->setShowHovercard(true) ->setAsText($this->getAsText()); + if ($show_state_icons) { + $view->setShowStateIcon(true); + } + $items[] = $view; } diff --git a/src/applications/phid/view/PHUIHandleView.php b/src/applications/phid/view/PHUIHandleView.php index 6d968bd7ee..96ae7de025 100644 --- a/src/applications/phid/view/PHUIHandleView.php +++ b/src/applications/phid/view/PHUIHandleView.php @@ -17,6 +17,7 @@ final class PHUIHandleView private $asText; private $useShortName; private $showHovercard; + private $showStateIcon; public function setHandleList(PhabricatorHandleList $list) { $this->handleList = $list; @@ -48,6 +49,15 @@ final class PHUIHandleView return $this; } + public function setShowStateIcon($show_state_icon) { + $this->showStateIcon = $show_state_icon; + return $this; + } + + public function getShowStateIcon() { + return $this->showStateIcon; + } + public function render() { $handle = $this->handleList[$this->handlePHID]; @@ -77,6 +87,11 @@ final class PHUIHandleView $link = $handle->renderLink($name); } + if ($this->showStateIcon) { + $icon = $handle->renderStateIcon(); + $link = array($icon, ' ', $link); + } + return $link; } diff --git a/src/applications/repository/phid/PhabricatorRepositoryCommitPHIDType.php b/src/applications/repository/phid/PhabricatorRepositoryCommitPHIDType.php index df84f2dcfd..b5abd40032 100644 --- a/src/applications/repository/phid/PhabricatorRepositoryCommitPHIDType.php +++ b/src/applications/repository/phid/PhabricatorRepositoryCommitPHIDType.php @@ -81,6 +81,16 @@ final class PhabricatorRepositoryCommitPHIDType extends PhabricatorPHIDType { $handle->setFullName($full_name); $handle->setURI($commit->getURI()); $handle->setTimestamp($commit->getEpoch()); + + $status = $commit->getAuditStatus(); + $icon = PhabricatorAuditCommitStatusConstants::getStatusIcon($status); + $color = PhabricatorAuditCommitStatusConstants::getStatusColor($status); + $name = PhabricatorAuditCommitStatusConstants::getStatusName($status); + + $handle + ->setStateIcon($icon) + ->setStateColor($color) + ->setStateName($name); } } From 19525ed81afad93d2f1a6b3f74542ee0adef59ee Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 12 Jan 2017 12:47:42 -0800 Subject: [PATCH 42/51] Add diffusion.commit.search Conduit API method Summary: Ref T10978. This is bare bones, but the SearchEngine is at least mostly in reasonable shape now, so get it in place and freeze the old stuff. I previously froze `audit.query`, which did much the same thing. Test Plan: Issued some queries with the API, technically got results back. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10978 Differential Revision: https://secure.phabricator.com/D17194 --- src/__phutil_library_map__.php | 3 +++ .../DiffusionCommitSearchConduitAPIMethod.php | 18 +++++++++++++ .../DiffusionQueryCommitsConduitAPIMethod.php | 10 ++++++++ .../storage/PhabricatorRepositoryCommit.php | 25 ++++++++++++++++++- 4 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 src/applications/diffusion/conduit/DiffusionCommitSearchConduitAPIMethod.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index e704ece353..e0bcc82c8d 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -667,6 +667,7 @@ phutil_register_library_map(array( 'DiffusionCommitRevisionHeraldField' => 'applications/diffusion/herald/DiffusionCommitRevisionHeraldField.php', 'DiffusionCommitRevisionReviewersHeraldField' => 'applications/diffusion/herald/DiffusionCommitRevisionReviewersHeraldField.php', 'DiffusionCommitRevisionSubscribersHeraldField' => 'applications/diffusion/herald/DiffusionCommitRevisionSubscribersHeraldField.php', + 'DiffusionCommitSearchConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionCommitSearchConduitAPIMethod.php', 'DiffusionCommitTagsController' => 'applications/diffusion/controller/DiffusionCommitTagsController.php', 'DiffusionCommitTransactionType' => 'applications/diffusion/xaction/DiffusionCommitTransactionType.php', 'DiffusionCompareController' => 'applications/diffusion/controller/DiffusionCompareController.php', @@ -5373,6 +5374,7 @@ phutil_register_library_map(array( 'DiffusionCommitRevisionHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitRevisionReviewersHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitRevisionSubscribersHeraldField' => 'DiffusionCommitHeraldField', + 'DiffusionCommitSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod', 'DiffusionCommitTagsController' => 'DiffusionController', 'DiffusionCommitTransactionType' => 'PhabricatorModularTransactionType', 'DiffusionCompareController' => 'DiffusionController', @@ -8783,6 +8785,7 @@ phutil_register_library_map(array( 'PhabricatorCustomFieldInterface', 'PhabricatorApplicationTransactionInterface', 'PhabricatorFulltextInterface', + 'PhabricatorConduitResultInterface', ), 'PhabricatorRepositoryCommitChangeParserWorker' => 'PhabricatorRepositoryCommitParserWorker', 'PhabricatorRepositoryCommitData' => 'PhabricatorRepositoryDAO', diff --git a/src/applications/diffusion/conduit/DiffusionCommitSearchConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionCommitSearchConduitAPIMethod.php new file mode 100644 index 0000000000..667e1d51b1 --- /dev/null +++ b/src/applications/diffusion/conduit/DiffusionCommitSearchConduitAPIMethod.php @@ -0,0 +1,18 @@ +'; } diff --git a/src/applications/repository/storage/PhabricatorRepositoryCommit.php b/src/applications/repository/storage/PhabricatorRepositoryCommit.php index fd3e5fe97a..67f187d9c2 100644 --- a/src/applications/repository/storage/PhabricatorRepositoryCommit.php +++ b/src/applications/repository/storage/PhabricatorRepositoryCommit.php @@ -13,7 +13,8 @@ final class PhabricatorRepositoryCommit HarbormasterCircleCIBuildableInterface, PhabricatorCustomFieldInterface, PhabricatorApplicationTransactionInterface, - PhabricatorFulltextInterface { + PhabricatorFulltextInterface, + PhabricatorConduitResultInterface { protected $repositoryID; protected $phid; @@ -580,4 +581,26 @@ final class PhabricatorRepositoryCommit return new DiffusionCommitFulltextEngine(); } + +/* -( PhabricatorConduitResultInterface )---------------------------------- */ + + public function getFieldSpecificationsForConduit() { + return array( + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('identifier') + ->setType('string') + ->setDescription(pht('The commit identifier.')), + ); + } + + public function getFieldValuesForConduit() { + return array( + 'identifier' => $this->getCommitIdentifier(), + ); + } + + public function getConduitSearchAttachments() { + return array(); + } + } From e66a03eaa3e4c62f22e8e6a62fd98f134ea85466 Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 12 Jan 2017 12:56:10 -0800 Subject: [PATCH 43/51] In Audit list and Owners list, show overall commit audit status instead of semi-viewer status Summary: Fixes T9482. Historically, Audit was somewhat confused about whether queries and views should act on the viewer's status or the object's status. This realigns Audit to work like Differential: we show overall status for the commit, just like we show overall status for revisions. This better aligns with expectation and isn't weird/confusing, and bucketing should handle all the "what do //I// need to do" stuff now (or, at least, seems to have in Differential). This is also how every other type of object works in every other application, AFAIK (all of them show object status, not viewer's-relationship-to-the-object status). Test Plan: - Viewed commit lists in Owners and Audit. - Saw commit overall statuses, not my personal status. Reviewers: chad Reviewed By: chad Maniphest Tasks: T9482 Differential Revision: https://secure.phabricator.com/D17195 --- .../query/PhabricatorCommitSearchEngine.php | 6 +-- .../audit/view/PhabricatorAuditListView.php | 40 +++++-------------- 2 files changed, 11 insertions(+), 35 deletions(-) diff --git a/src/applications/audit/query/PhabricatorCommitSearchEngine.php b/src/applications/audit/query/PhabricatorCommitSearchEngine.php index c6c37208f8..fa4fa3c963 100644 --- a/src/applications/audit/query/PhabricatorCommitSearchEngine.php +++ b/src/applications/audit/query/PhabricatorCommitSearchEngine.php @@ -139,12 +139,8 @@ final class PhabricatorCommitSearchEngine $bucket = $this->getResultBucket($query); - $authority_phids = PhabricatorAuditCommentEditor::loadAuditPHIDsForUser( - $viewer); - $template = id(new PhabricatorAuditListView()) - ->setViewer($viewer) - ->setAuthorityPHIDs($authority_phids); + ->setViewer($viewer); $views = array(); if ($bucket) { diff --git a/src/applications/audit/view/PhabricatorAuditListView.php b/src/applications/audit/view/PhabricatorAuditListView.php index c359833729..f348acc759 100644 --- a/src/applications/audit/view/PhabricatorAuditListView.php +++ b/src/applications/audit/view/PhabricatorAuditListView.php @@ -3,16 +3,10 @@ final class PhabricatorAuditListView extends AphrontView { private $commits; - private $authorityPHIDs = array(); private $header; private $noDataString; private $highlightedAudits; - public function setAuthorityPHIDs(array $phids) { - $this->authorityPHIDs = $phids; - return $this; - } - public function setNoDataString($no_data_string) { $this->noDataString = $no_data_string; return $this; @@ -101,28 +95,15 @@ final class PhabricatorAuditListView extends AphrontView { } $auditors = phutil_implode_html(', ', $auditors); - $authority_audits = array_select_keys($audits, $this->authorityPHIDs); - if ($authority_audits) { - $audit = reset($authority_audits); - } else { - $audit = reset($audits); - } - if ($audit) { - $reasons = $audit->getAuditReasons(); - $reasons = phutil_implode_html(', ', $reasons); - $status_code = $audit->getAuditStatus(); - $status_text = - PhabricatorAuditStatusConstants::getStatusName($status_code); - $status_color = - PhabricatorAuditStatusConstants::getStatusColor($status_code); - $status_icon = - PhabricatorAuditStatusConstants::getStatusIcon($status_code); - } else { - $reasons = null; - $status_text = null; - $status_color = null; - $status_icon = null; - } + $status = $commit->getAuditStatus(); + + $status_text = + PhabricatorAuditCommitStatusConstants::getStatusName($status); + $status_color = + PhabricatorAuditCommitStatusConstants::getStatusColor($status); + $status_icon = + PhabricatorAuditCommitStatusConstants::getStatusIcon($status); + $author_phid = $commit->getAuthorPHID(); if ($author_phid) { $author_name = $viewer->renderHandle($author_phid); @@ -143,8 +124,7 @@ final class PhabricatorAuditListView extends AphrontView { } if ($status_color) { - $item->setStatusIcon( - $status_icon.' '.$status_color, $status_text); + $item->setStatusIcon($status_icon.' '.$status_color, $status_text); } $list->addItem($item); From 4a34f26a44ae6438bb3b68eb6b9a17c0fb0d161b Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 12 Jan 2017 15:10:58 -0800 Subject: [PATCH 44/51] Don't warn about "always_populate_raw_post_data" on PHP7 Summary: Ref T9640. This option was removed in PHP7, so there's no reason to warn about it. Test Plan: No longer saw a setup warning on PHP7. Reviewers: chad Reviewed By: chad Maniphest Tasks: T9640 Differential Revision: https://secure.phabricator.com/D17196 --- .../config/check/PhabricatorPHPConfigSetupCheck.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/applications/config/check/PhabricatorPHPConfigSetupCheck.php b/src/applications/config/check/PhabricatorPHPConfigSetupCheck.php index f286b46f9c..1dd3add94d 100644 --- a/src/applications/config/check/PhabricatorPHPConfigSetupCheck.php +++ b/src/applications/config/check/PhabricatorPHPConfigSetupCheck.php @@ -40,7 +40,13 @@ final class PhabricatorPHPConfigSetupCheck extends PhabricatorSetupCheck { ->setMessage($message); } - $raw_post_data = (int)ini_get('always_populate_raw_post_data'); + if (version_compare(phpversion(), '7', '>=')) { + // This option was removed in PHP7. + $raw_post_data = -1; + } else { + $raw_post_data = (int)ini_get('always_populate_raw_post_data'); + } + if ($raw_post_data != -1) { $summary = pht( 'PHP setting "%s" should be set to "-1" to avoid deprecation '. From a2cd3d9a8913d5709e2bc999efb75b63d7c19696 Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 12 Jan 2017 15:11:05 -0800 Subject: [PATCH 45/51] Change PHP 7 setup warning to complain about 7.0 only, not 7.1+ Summary: Ref T9640. On 7.0 we had signal handling issues so we can never support it, but async signals should resolve them on 7.1 or newer. Test Plan: On PHP 7.1, got through the setup warning. Reviewers: chad Reviewed By: chad Maniphest Tasks: T9640 Differential Revision: https://secure.phabricator.com/D17197 --- .../config/check/PhabricatorPHPPreflightSetupCheck.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/applications/config/check/PhabricatorPHPPreflightSetupCheck.php b/src/applications/config/check/PhabricatorPHPPreflightSetupCheck.php index 82a13438d8..7c9653f4ff 100644 --- a/src/applications/config/check/PhabricatorPHPPreflightSetupCheck.php +++ b/src/applications/config/check/PhabricatorPHPPreflightSetupCheck.php @@ -11,15 +11,16 @@ final class PhabricatorPHPPreflightSetupCheck extends PhabricatorSetupCheck { } protected function executeChecks() { - if (version_compare(phpversion(), 7, '>=')) { + if (version_compare(phpversion(), 7, '>=') && + version_compare(phpversion(), 7.1, '<')) { $message = pht( - 'This version of Phabricator does not support PHP 7. You '. - 'are running PHP %s.', + 'This version of Phabricator does not support PHP 7.0. You '. + 'are running PHP %s. Upgrade to PHP 7.1 or newer.', phpversion()); $this->newIssue('php.version7') ->setIsFatal(true) - ->setName(pht('PHP 7 Not Supported')) + ->setName(pht('PHP 7.0 Not Supported')) ->setMessage($message) ->addLink( 'https://phurl.io/u/php7', From 7ccc4cea43e328bddf56504bc954c6e014ae1ef0 Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 12 Jan 2017 15:38:28 -0800 Subject: [PATCH 46/51] With APCu 5+, use `apcu_*` function to examine cache state Summary: Ref T9640. APCu 5.0+ (for PHP7) uses `apcu_*` functions instead of `apc_` functions. Test for function existence and call the appropriate functions. Test Plan: {F2352695} Reviewers: chad Reviewed By: chad Maniphest Tasks: T9640 Differential Revision: https://secure.phabricator.com/D17198 --- .../cache/spec/PhabricatorDataCacheSpec.php | 52 ++++++++++++------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/src/applications/cache/spec/PhabricatorDataCacheSpec.php b/src/applications/cache/spec/PhabricatorDataCacheSpec.php index eae5d75790..482af00d00 100644 --- a/src/applications/cache/spec/PhabricatorDataCacheSpec.php +++ b/src/applications/cache/spec/PhabricatorDataCacheSpec.php @@ -79,28 +79,40 @@ final class PhabricatorDataCacheSpec extends PhabricatorCacheSpec { } private function initAPCCommonSpec() { - $mem = apc_sma_info(); - $this->setTotalMemory($mem['num_seg'] * $mem['seg_size']); - - $info = apc_cache_info('user'); - $this->setUsedMemory($info['mem_size']); - $this->setEntryCount(count($info['cache_list'])); - - $cache = $info['cache_list']; $state = array(); - foreach ($cache as $item) { - $info = idx($item, 'info', ''); - $key = self::getKeyPattern($info); - if (empty($state[$key])) { - $state[$key] = array( - 'max' => 0, - 'total' => 0, - 'count' => 0, - ); + + if (function_exists('apcu_sma_info')) { + $mem = apcu_sma_info(); + $info = apcu_cache_info(); + } else if (function_exists('apc_sma_info')) { + $mem = apc_sma_info(); + $info = apc_cache_info('user'); + } else { + $mem = null; + } + + if ($mem) { + $this->setTotalMemory($mem['num_seg'] * $mem['seg_size']); + + $this->setUsedMemory($info['mem_size']); + $this->setEntryCount(count($info['cache_list'])); + + $cache = $info['cache_list']; + $state = array(); + foreach ($cache as $item) { + $info = idx($item, 'info', ''); + $key = self::getKeyPattern($info); + if (empty($state[$key])) { + $state[$key] = array( + 'max' => 0, + 'total' => 0, + 'count' => 0, + ); + } + $state[$key]['max'] = max($state[$key]['max'], $item['mem_size']); + $state[$key]['total'] += $item['mem_size']; + $state[$key]['count']++; } - $state[$key]['max'] = max($state[$key]['max'], $item['mem_size']); - $state[$key]['total'] += $item['mem_size']; - $state[$key]['count']++; } $this->setCacheSummary($state); From e4f49f08065748d3ee62465513e4265edb72fae8 Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 12 Jan 2017 15:50:04 -0800 Subject: [PATCH 47/51] When available, use async_signals in Phabricator Summary: Ref T9640. See D17200 for the analogous change in libphutil. Test Plan: See D17200. Reviewers: chad Reviewed By: chad Maniphest Tasks: T9640 Differential Revision: https://secure.phabricator.com/D17201 --- scripts/daemon/launch_daemon.php | 6 +++++- scripts/ssh/ssh-connect.php | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/scripts/daemon/launch_daemon.php b/scripts/daemon/launch_daemon.php index 7c75ab671e..44e70f710d 100755 --- a/scripts/daemon/launch_daemon.php +++ b/scripts/daemon/launch_daemon.php @@ -5,7 +5,11 @@ // script, except it loads the Phabricator environment and adds some Phabricator // specific flags. -declare(ticks = 1); +if (function_exists('pcntl_async_signals')) { + pcntl_async_signals(true); +} else { + declare(ticks = 1); +} $root = dirname(dirname(dirname(__FILE__))); require_once $root.'/scripts/__init_script__.php'; diff --git a/scripts/ssh/ssh-connect.php b/scripts/ssh/ssh-connect.php index a9bc921e69..9757f3cbbe 100755 --- a/scripts/ssh/ssh-connect.php +++ b/scripts/ssh/ssh-connect.php @@ -7,7 +7,11 @@ // In some cases, Subversion sends us SIGTERM. If we don't catch the signal and // react to it, we won't run object destructors by default and thus won't clean // up temporary files. Declare ticks so we can install a signal handler. -declare(ticks=1); +if (function_exists('pcntl_async_signals')) { + pcntl_async_signals(true); +} else { + declare(ticks = 1); +} $root = dirname(dirname(dirname(__FILE__))); require_once $root.'/scripts/__init_script__.php'; From 103d8c45f3b801f2ef60cccba39a65d939ac0c2e Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 12 Jan 2017 15:54:09 -0800 Subject: [PATCH 48/51] Remove "PHP 7 is not supported" from install documentation Summary: Ref T9640. This is no longer true. I'm assuming PHP7 vs 7.1 issues won't be common (everyone on the cutting edge is probably on 7.1, and Ubuntu 16 is on 7.1) but we could make this more granular in the future. Test Plan: Careful reading. Reviewers: chad Reviewed By: chad Maniphest Tasks: T9640 Differential Revision: https://secure.phabricator.com/D17202 --- src/docs/user/installation_guide.diviner | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/docs/user/installation_guide.diviner b/src/docs/user/installation_guide.diviner index 749ca9a63d..8feb8ae1d0 100644 --- a/src/docs/user/installation_guide.diviner +++ b/src/docs/user/installation_guide.diviner @@ -70,8 +70,7 @@ Beyond an operating system, you will need **a webserver**. You will also need: - **MySQL**: You need MySQL. We strongly recommend MySQL 5.5 or newer. - - **PHP**: You need PHP 5.2 or newer, but note that PHP 7 is - **not supported**. + - **PHP**: You need PHP 5.2 or newer. You'll probably also need a **domain name**. In particular, you should read this note: From 4d0a03e3d0b2845369e005f8c5e9fa21bc03adbe Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 12 Jan 2017 16:31:33 -0800 Subject: [PATCH 49/51] Improve commit audit status icons Summary: Ref T9482. These may need a little more work (feel free to shoot me a counter-diff) but try to: - Never use only color to distinguish between states (for colorblind, etc users). - Give the "nothing needs to be done" state a more obvious "okay" icon (instead of a question mark). Test Plan: Looked at some linked commits in Maniphest, the icons made a bit more sense? Reviewers: chad Reviewed By: chad Maniphest Tasks: T9482 Differential Revision: https://secure.phabricator.com/D17203 --- .../PhabricatorAuditCommitStatusConstants.php | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/applications/audit/constants/PhabricatorAuditCommitStatusConstants.php b/src/applications/audit/constants/PhabricatorAuditCommitStatusConstants.php index 7c015efc43..bced1b2dc6 100644 --- a/src/applications/audit/constants/PhabricatorAuditCommitStatusConstants.php +++ b/src/applications/audit/constants/PhabricatorAuditCommitStatusConstants.php @@ -10,7 +10,7 @@ final class PhabricatorAuditCommitStatusConstants extends Phobject { public static function getStatusNameMap() { $map = array( - self::NONE => pht('None'), + self::NONE => pht('No Audits'), self::NEEDS_AUDIT => pht('Audit Required'), self::CONCERN_RAISED => pht('Concern Raised'), self::PARTIALLY_AUDITED => pht('Partially Audited'), @@ -46,6 +46,9 @@ final class PhabricatorAuditCommitStatusConstants extends Phobject { case self::FULLY_AUDITED: $color = 'green'; break; + case self::NONE: + $color = 'bluegrey'; + break; default: $color = null; break; @@ -56,13 +59,18 @@ final class PhabricatorAuditCommitStatusConstants extends Phobject { public static function getStatusIcon($code) { switch ($code) { case self::CONCERN_RAISED: - $icon = 'fa-exclamation-circle'; + $icon = 'fa-times-circle'; break; case self::NEEDS_AUDIT: - case self::PARTIALLY_AUDITED: $icon = 'fa-exclamation-circle'; break; + case self::PARTIALLY_AUDITED: + $icon = 'fa-check-circle-o'; + break; case self::FULLY_AUDITED: + $icon = 'fa-check-circle'; + break; + case self::NONE: $icon = 'fa-check'; break; default: From e684794bf33d910d007aee508a555c764ed0df51 Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 13 Jan 2017 06:18:37 -0800 Subject: [PATCH 50/51] Get "Create Revision" out of Quick Create menu for now Summary: Ref T12098. We have two methods (`supportsEditEngineConfiguration()` and `isEngineConfigurable()`) which sort of do the same thing and probably should be merged. For now, just swap which one we override to get "Create Revision" out of the Quick Create menu. Test Plan: No more "Create Revision" in Quick Create menu. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12098 Differential Revision: https://secure.phabricator.com/D17204 --- .../differential/editor/DifferentialRevisionEditEngine.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/differential/editor/DifferentialRevisionEditEngine.php b/src/applications/differential/editor/DifferentialRevisionEditEngine.php index d33be9035b..5c2fef275d 100644 --- a/src/applications/differential/editor/DifferentialRevisionEditEngine.php +++ b/src/applications/differential/editor/DifferentialRevisionEditEngine.php @@ -29,7 +29,7 @@ final class DifferentialRevisionEditEngine return 'PhabricatorDifferentialApplication'; } - protected function supportsEditEngineConfiguration() { + public function isEngineConfigurable() { return false; } From 7276af6a81f49bbdc14ace064aab50afbeb79cfc Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 13 Jan 2017 06:36:25 -0800 Subject: [PATCH 51/51] Make yellow "draft" bubbles more generic Summary: Fixes T12095. Ref T6660. The old code for this was specific to Differential, using the `DifferentialDraft` table. Instead, make the `EditEngine` / `VersionedDraft` code create and remove a `` edge when a particular author creates drafts. Some applications have drafts beyond `VersionedDrafts`, notably inline comments. Before writing "yes, draft" or "no, no draft", ask the object if it has any custom draft stuff we need to know about. This should fix all the yellow bubble bugs I created in T11114 and allow us to bring the feature to Audit fairly easily. Test Plan: Created and deleted comments and inlines, reloading the list view after each change. Couldn't find a way to break the list view anymore. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12095, T6660 Differential Revision: https://secure.phabricator.com/D17205 --- src/__phutil_library_map__.php | 10 ++ ...ifferentialInlineCommentEditController.php | 29 +++--- .../DifferentialRevisionDraftEngine.php | 17 ++++ .../query/DifferentialRevisionQuery.php | 45 ++++++--- .../storage/DifferentialDraft.php | 42 -------- .../storage/DifferentialRevision.php | 19 +++- .../view/DifferentialRevisionListView.php | 2 +- .../draft/PhabricatorBuiltinDraftEngine.php | 4 + .../draft/PhabricatorDraftEngine.php | 98 +++++++++++++++++++ .../draft/PhabricatorDraftInterface.php | 7 ++ .../PhabricatorObjectHasDraftEdgeType.php | 8 ++ .../editengine/PhabricatorEditEngine.php | 30 ++++++ 12 files changed, 234 insertions(+), 77 deletions(-) create mode 100644 src/applications/differential/engine/DifferentialRevisionDraftEngine.php create mode 100644 src/applications/transactions/draft/PhabricatorBuiltinDraftEngine.php create mode 100644 src/applications/transactions/draft/PhabricatorDraftEngine.php create mode 100644 src/applications/transactions/draft/PhabricatorDraftInterface.php create mode 100644 src/applications/transactions/edges/PhabricatorObjectHasDraftEdgeType.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index e0bcc82c8d..617365f333 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -519,6 +519,7 @@ phutil_register_library_map(array( 'DifferentialRevisionControlSystem' => 'applications/differential/constants/DifferentialRevisionControlSystem.php', 'DifferentialRevisionDependedOnByRevisionEdgeType' => 'applications/differential/edge/DifferentialRevisionDependedOnByRevisionEdgeType.php', 'DifferentialRevisionDependsOnRevisionEdgeType' => 'applications/differential/edge/DifferentialRevisionDependsOnRevisionEdgeType.php', + 'DifferentialRevisionDraftEngine' => 'applications/differential/engine/DifferentialRevisionDraftEngine.php', 'DifferentialRevisionEditConduitAPIMethod' => 'applications/differential/conduit/DifferentialRevisionEditConduitAPIMethod.php', 'DifferentialRevisionEditController' => 'applications/differential/controller/DifferentialRevisionEditController.php', 'DifferentialRevisionEditEngine' => 'applications/differential/editor/DifferentialRevisionEditEngine.php', @@ -2055,6 +2056,7 @@ phutil_register_library_map(array( 'PhabricatorBotUser' => 'infrastructure/daemon/bot/target/PhabricatorBotUser.php', 'PhabricatorBotWhatsNewHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotWhatsNewHandler.php', 'PhabricatorBritishEnglishTranslation' => 'infrastructure/internationalization/translation/PhabricatorBritishEnglishTranslation.php', + 'PhabricatorBuiltinDraftEngine' => 'applications/transactions/draft/PhabricatorBuiltinDraftEngine.php', 'PhabricatorBuiltinPatchList' => 'infrastructure/storage/patch/PhabricatorBuiltinPatchList.php', 'PhabricatorBulkContentSource' => 'infrastructure/daemon/contentsource/PhabricatorBulkContentSource.php', 'PhabricatorBusyUIExample' => 'applications/uiexample/examples/PhabricatorBusyUIExample.php', @@ -2550,6 +2552,8 @@ phutil_register_library_map(array( 'PhabricatorDoorkeeperApplication' => 'applications/doorkeeper/application/PhabricatorDoorkeeperApplication.php', 'PhabricatorDraft' => 'applications/draft/storage/PhabricatorDraft.php', 'PhabricatorDraftDAO' => 'applications/draft/storage/PhabricatorDraftDAO.php', + 'PhabricatorDraftEngine' => 'applications/transactions/draft/PhabricatorDraftEngine.php', + 'PhabricatorDraftInterface' => 'applications/transactions/draft/PhabricatorDraftInterface.php', 'PhabricatorDrydockApplication' => 'applications/drydock/application/PhabricatorDrydockApplication.php', 'PhabricatorEdgeConfig' => 'infrastructure/edges/constants/PhabricatorEdgeConfig.php', 'PhabricatorEdgeConstants' => 'infrastructure/edges/constants/PhabricatorEdgeConstants.php', @@ -3095,6 +3099,7 @@ phutil_register_library_map(array( 'PhabricatorObjectHasAsanaSubtaskEdgeType' => 'applications/doorkeeper/edge/PhabricatorObjectHasAsanaSubtaskEdgeType.php', 'PhabricatorObjectHasAsanaTaskEdgeType' => 'applications/doorkeeper/edge/PhabricatorObjectHasAsanaTaskEdgeType.php', 'PhabricatorObjectHasContributorEdgeType' => 'applications/transactions/edges/PhabricatorObjectHasContributorEdgeType.php', + 'PhabricatorObjectHasDraftEdgeType' => 'applications/transactions/edges/PhabricatorObjectHasDraftEdgeType.php', 'PhabricatorObjectHasFileEdgeType' => 'applications/transactions/edges/PhabricatorObjectHasFileEdgeType.php', 'PhabricatorObjectHasJiraIssueEdgeType' => 'applications/doorkeeper/edge/PhabricatorObjectHasJiraIssueEdgeType.php', 'PhabricatorObjectHasSubscriberEdgeType' => 'applications/transactions/edges/PhabricatorObjectHasSubscriberEdgeType.php', @@ -5210,6 +5215,7 @@ phutil_register_library_map(array( 'PhabricatorProjectInterface', 'PhabricatorFulltextInterface', 'PhabricatorConduitResultInterface', + 'PhabricatorDraftInterface', ), 'DifferentialRevisionAbandonTransaction' => 'DifferentialRevisionActionTransaction', 'DifferentialRevisionAcceptTransaction' => 'DifferentialRevisionReviewTransaction', @@ -5226,6 +5232,7 @@ phutil_register_library_map(array( 'DifferentialRevisionControlSystem' => 'Phobject', 'DifferentialRevisionDependedOnByRevisionEdgeType' => 'PhabricatorEdgeType', 'DifferentialRevisionDependsOnRevisionEdgeType' => 'PhabricatorEdgeType', + 'DifferentialRevisionDraftEngine' => 'PhabricatorDraftEngine', 'DifferentialRevisionEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod', 'DifferentialRevisionEditController' => 'DifferentialController', 'DifferentialRevisionEditEngine' => 'PhabricatorEditEngine', @@ -6981,6 +6988,7 @@ phutil_register_library_map(array( 'PhabricatorBotUser' => 'PhabricatorBotTarget', 'PhabricatorBotWhatsNewHandler' => 'PhabricatorBotHandler', 'PhabricatorBritishEnglishTranslation' => 'PhutilTranslation', + 'PhabricatorBuiltinDraftEngine' => 'PhabricatorDraftEngine', 'PhabricatorBuiltinPatchList' => 'PhabricatorSQLPatchList', 'PhabricatorBulkContentSource' => 'PhabricatorContentSource', 'PhabricatorBusyUIExample' => 'PhabricatorUIExample', @@ -7559,6 +7567,7 @@ phutil_register_library_map(array( 'PhabricatorDoorkeeperApplication' => 'PhabricatorApplication', 'PhabricatorDraft' => 'PhabricatorDraftDAO', 'PhabricatorDraftDAO' => 'PhabricatorLiskDAO', + 'PhabricatorDraftEngine' => 'Phobject', 'PhabricatorDrydockApplication' => 'PhabricatorApplication', 'PhabricatorEdgeConfig' => 'PhabricatorEdgeConstants', 'PhabricatorEdgeConstants' => 'Phobject', @@ -8172,6 +8181,7 @@ phutil_register_library_map(array( 'PhabricatorObjectHasAsanaSubtaskEdgeType' => 'PhabricatorEdgeType', 'PhabricatorObjectHasAsanaTaskEdgeType' => 'PhabricatorEdgeType', 'PhabricatorObjectHasContributorEdgeType' => 'PhabricatorEdgeType', + 'PhabricatorObjectHasDraftEdgeType' => 'PhabricatorEdgeType', 'PhabricatorObjectHasFileEdgeType' => 'PhabricatorEdgeType', 'PhabricatorObjectHasJiraIssueEdgeType' => 'PhabricatorEdgeType', 'PhabricatorObjectHasSubscriberEdgeType' => 'PhabricatorEdgeType', diff --git a/src/applications/differential/controller/DifferentialInlineCommentEditController.php b/src/applications/differential/controller/DifferentialInlineCommentEditController.php index 42b96920eb..926158cb41 100644 --- a/src/applications/differential/controller/DifferentialInlineCommentEditController.php +++ b/src/applications/differential/controller/DifferentialInlineCommentEditController.php @@ -152,36 +152,23 @@ final class DifferentialInlineCommentEditController protected function deleteComment(PhabricatorInlineCommentInterface $inline) { $inline->openTransaction(); - $inline->setIsDeleted(1)->save(); - DifferentialDraft::deleteHasDraft( - $inline->getAuthorPHID(), - $inline->getRevisionPHID(), - $inline->getPHID()); - + $this->syncDraft(); $inline->saveTransaction(); } protected function undeleteComment( PhabricatorInlineCommentInterface $inline) { $inline->openTransaction(); - $inline->setIsDeleted(0)->save(); - DifferentialDraft::markHasDraft( - $inline->getAuthorPHID(), - $inline->getRevisionPHID(), - $inline->getPHID()); - + $this->syncDraft(); $inline->saveTransaction(); } protected function saveComment(PhabricatorInlineCommentInterface $inline) { $inline->openTransaction(); $inline->save(); - DifferentialDraft::markHasDraft( - $inline->getAuthorPHID(), - $inline->getRevisionPHID(), - $inline->getPHID()); + $this->syncDraft(); $inline->saveTransaction(); } @@ -224,4 +211,14 @@ final class DifferentialInlineCommentEditController $ids); } + private function syncDraft() { + $viewer = $this->getViewer(); + $revision = $this->loadRevision(); + + $revision->newDraftEngine() + ->setObject($revision) + ->setViewer($viewer) + ->synchronize(); + } + } diff --git a/src/applications/differential/engine/DifferentialRevisionDraftEngine.php b/src/applications/differential/engine/DifferentialRevisionDraftEngine.php new file mode 100644 index 0000000000..94669b0182 --- /dev/null +++ b/src/applications/differential/engine/DifferentialRevisionDraftEngine.php @@ -0,0 +1,17 @@ +getViewer(); + $revision = $this->getObject(); + + $inlines = DifferentialTransactionQuery::loadUnsubmittedInlineComments( + $viewer, + $revision); + + return (bool)$inlines; + } + +} diff --git a/src/applications/differential/query/DifferentialRevisionQuery.php b/src/applications/differential/query/DifferentialRevisionQuery.php index 6dd690d179..a4e446ad0d 100644 --- a/src/applications/differential/query/DifferentialRevisionQuery.php +++ b/src/applications/differential/query/DifferentialRevisionQuery.php @@ -473,15 +473,33 @@ final class DifferentialRevisionQuery } if ($this->needDrafts) { - $drafts = id(new DifferentialDraft())->loadAllWhere( - 'authorPHID = %s AND objectPHID IN (%Ls)', - $viewer->getPHID(), - mpull($revisions, 'getPHID')); - $drafts = mgroup($drafts, 'getObjectPHID'); - foreach ($revisions as $revision) { - $revision->attachDrafts( - $viewer, - idx($drafts, $revision->getPHID(), array())); + $viewer_phid = $viewer->getPHID(); + $draft_type = PhabricatorObjectHasDraftEdgeType::EDGECONST; + + if (!$viewer_phid) { + // Viewers without a valid PHID can never have drafts. + foreach ($revisions as $revision) { + $revision->attachHasDraft($viewer, false); + } + } else { + $edge_query = id(new PhabricatorEdgeQuery()) + ->withSourcePHIDs(mpull($revisions, 'getPHID')) + ->withEdgeTypes( + array( + $draft_type, + )) + ->withDestinationPHIDs(array($viewer_phid)); + + $edge_query->execute(); + + foreach ($revisions as $revision) { + $has_draft = (bool)$edge_query->getDestinationPHIDs( + array( + $revision->getPHID(), + )); + + $revision->attachHasDraft($viewer, $has_draft); + } } } @@ -621,12 +639,13 @@ final class DifferentialRevisionQuery } if ($this->draftAuthors) { - $differential_draft = new DifferentialDraft(); $joins[] = qsprintf( $conn_r, - 'JOIN %T has_draft ON has_draft.objectPHID = r.phid '. - 'AND has_draft.authorPHID IN (%Ls)', - $differential_draft->getTableName(), + 'JOIN %T has_draft ON has_draft.srcPHID = r.phid + AND has_draft.type = %s + AND has_draft.dstPHID IN (%Ls)', + PhabricatorEdgeConfig::TABLE_NAME_EDGE, + PhabricatorObjectHasDraftEdgeType::EDGECONST, $this->draftAuthors); } diff --git a/src/applications/differential/storage/DifferentialDraft.php b/src/applications/differential/storage/DifferentialDraft.php index 371698937c..7dbe2f68bd 100644 --- a/src/applications/differential/storage/DifferentialDraft.php +++ b/src/applications/differential/storage/DifferentialDraft.php @@ -20,46 +20,4 @@ final class DifferentialDraft extends DifferentialDAO { ) + parent::getConfiguration(); } - public static function markHasDraft( - $author_phid, - $object_phid, - $draft_key) { - try { - id(new DifferentialDraft()) - ->setObjectPHID($object_phid) - ->setAuthorPHID($author_phid) - ->setDraftKey($draft_key) - ->save(); - } catch (AphrontDuplicateKeyQueryException $ex) { - // no worries - } - } - - public static function deleteHasDraft( - $author_phid, - $object_phid, - $draft_key) { - $draft = id(new DifferentialDraft())->loadOneWhere( - 'objectPHID = %s AND authorPHID = %s AND draftKey = %s', - $object_phid, - $author_phid, - $draft_key); - if ($draft) { - $draft->delete(); - } - } - - public static function deleteAllDrafts( - $author_phid, - $object_phid) { - - $drafts = id(new DifferentialDraft())->loadAllWhere( - 'objectPHID = %s AND authorPHID = %s', - $object_phid, - $author_phid); - foreach ($drafts as $draft) { - $draft->delete(); - } - } - } diff --git a/src/applications/differential/storage/DifferentialRevision.php b/src/applications/differential/storage/DifferentialRevision.php index 1e90b12dc4..df43043d6d 100644 --- a/src/applications/differential/storage/DifferentialRevision.php +++ b/src/applications/differential/storage/DifferentialRevision.php @@ -15,7 +15,8 @@ final class DifferentialRevision extends DifferentialDAO PhabricatorDestructibleInterface, PhabricatorProjectInterface, PhabricatorFulltextInterface, - PhabricatorConduitResultInterface { + PhabricatorConduitResultInterface, + PhabricatorDraftInterface { protected $title = ''; protected $originalTitle; @@ -488,12 +489,12 @@ final class DifferentialRevision extends DifferentialDAO return $this; } - public function getDrafts(PhabricatorUser $viewer) { - return $this->assertAttachedKey($this->drafts, $viewer->getPHID()); + public function getHasDraft(PhabricatorUser $viewer) { + return $this->assertAttachedKey($this->drafts, $viewer->getCacheFragment()); } - public function attachDrafts(PhabricatorUser $viewer, array $drafts) { - $this->drafts[$viewer->getPHID()] = $drafts; + public function attachHasDraft(PhabricatorUser $viewer, $has_draft) { + $this->drafts[$viewer->getCacheFragment()] = $has_draft; return $this; } @@ -735,4 +736,12 @@ final class DifferentialRevision extends DifferentialDAO return array(); } + +/* -( PhabricatorDraftInterface )------------------------------------------ */ + + + public function newDraftEngine() { + return new DifferentialRevisionDraftEngine(); + } + } diff --git a/src/applications/differential/view/DifferentialRevisionListView.php b/src/applications/differential/view/DifferentialRevisionListView.php index 7ab0f2d183..811e74fd65 100644 --- a/src/applications/differential/view/DifferentialRevisionListView.php +++ b/src/applications/differential/view/DifferentialRevisionListView.php @@ -92,7 +92,7 @@ final class DifferentialRevisionListView extends AphrontView { ''); } - if ($revision->getDrafts($viewer)) { + if ($revision->getHasDraft($viewer)) { $icons['draft'] = true; } diff --git a/src/applications/transactions/draft/PhabricatorBuiltinDraftEngine.php b/src/applications/transactions/draft/PhabricatorBuiltinDraftEngine.php new file mode 100644 index 0000000000..590ebea90c --- /dev/null +++ b/src/applications/transactions/draft/PhabricatorBuiltinDraftEngine.php @@ -0,0 +1,4 @@ +viewer = $viewer; + return $this; + } + + final public function getViewer() { + return $this->viewer; + } + + final public function setObject($object) { + $this->object = $object; + return $this; + } + + final public function getObject() { + return $this->object; + } + + final public function setVersionedDraft( + PhabricatorVersionedDraft $draft = null) { + $this->hasVersionedDraft = true; + $this->versionedDraft = $draft; + return $this; + } + + final public function getVersionedDraft() { + if (!$this->hasVersionedDraft) { + $draft = PhabricatorVersionedDraft::loadDraft( + $this->getObject()->getPHID(), + $this->getViewer()->getPHID()); + $this->setVersionedDraft($draft); + } + + return $this->versionedDraft; + } + + protected function hasVersionedDraftContent() { + $draft = $this->getVersionedDraft(); + if (!$draft) { + return false; + } + + if ($draft->getProperty('comment')) { + return true; + } + + if ($draft->getProperty('actions')) { + return true; + } + + return false; + } + + protected function hasCustomDraftContent() { + return false; + } + + final protected function hasAnyDraftContent() { + if ($this->hasVersionedDraftContent()) { + return true; + } + + if ($this->hasCustomDraftContent()) { + return true; + } + + return false; + } + + final public function synchronize() { + $object_phid = $this->getObject()->getPHID(); + $viewer_phid = $this->getViewer()->getPHID(); + + $has_draft = $this->hasAnyDraftContent(); + + $draft_type = PhabricatorObjectHasDraftEdgeType::EDGECONST; + $editor = id(new PhabricatorEdgeEditor()); + + if ($has_draft) { + $editor->addEdge($object_phid, $draft_type, $viewer_phid); + } else { + $editor->removeEdge($object_phid, $draft_type, $viewer_phid); + } + + $editor->save(); + } + +} diff --git a/src/applications/transactions/draft/PhabricatorDraftInterface.php b/src/applications/transactions/draft/PhabricatorDraftInterface.php new file mode 100644 index 0000000000..69b2edf8a0 --- /dev/null +++ b/src/applications/transactions/draft/PhabricatorDraftInterface.php @@ -0,0 +1,7 @@ +getPHID(), $current_version); + $is_empty = (!strlen($comment_text) && !$actions); + $draft ->setProperty('comment', $comment_text) ->setProperty('actions', $actions) ->save(); + + $draft_engine = $this->newDraftEngine($object); + if ($draft_engine) { + $draft_engine + ->setVersionedDraft($draft) + ->synchronize(); + } } } @@ -1831,6 +1840,13 @@ abstract class PhabricatorEditEngine $object->getPHID(), $viewer->getPHID(), $this->loadDraftVersion($object)); + + $draft_engine = $this->newDraftEngine($object); + if ($draft_engine) { + $draft_engine + ->setVersionedDraft(null) + ->synchronize(); + } } if ($request->isAjax() && $is_preview) { @@ -1847,6 +1863,20 @@ abstract class PhabricatorEditEngine } } + protected function newDraftEngine($object) { + $viewer = $this->getViewer(); + + if ($object instanceof PhabricatorDraftInterface) { + $engine = $object->newDraftEngine(); + } else { + $engine = new PhabricatorBuiltinDraftEngine(); + } + + return $engine + ->setObject($object) + ->setViewer($viewer); + } + /* -( Conduit )------------------------------------------------------------ */