@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Allow custom actions in Differential to explicitly override "accept" stickiness

Summary:
See PHI431. Ref T13102. An install is interested in a custom "non-sticky" accept action, roughly.

On the one hand, this is a pretty hacky patch. However, I suspect it inches us closer to T731, and I'm generally comfortable with exploring the realms of "Accept Next Update", "Unblock Without Accepting", etc., as long as most of it doesn't end up enabled by default in the upstream.

Test Plan:
- Accepted and updated revisions normally, saw accepts respect global stickiness.
- Modified the "Accept" action to explicitly be unsticky, saw nonsticky accept behavior after update.

Maniphest Tasks: T13102

Differential Revision: https://secure.phabricator.com/D19211

+78 -48
+2
resources/sql/autopatches/20180312.reviewers.01.options.sql
··· 1 + ALTER TABLE {$NAMESPACE}_differential.differential_reviewer 2 + ADD options LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT};
+2
resources/sql/autopatches/20180312.reviewers.02.optionsdefault.sql
··· 1 + UPDATE {$NAMESPACE}_differential.differential_reviewer 2 + SET options = '{}' WHERE options = '';
+17 -35
src/applications/differential/editor/DifferentialTransactionEditor.php
··· 154 154 155 155 $edge_ref_task = DifferentialRevisionHasTaskEdgeType::EDGECONST; 156 156 157 - $is_sticky_accept = PhabricatorEnv::getEnvConfig( 158 - 'differential.sticky-accept'); 159 - 160 - $downgrade_rejects = false; 161 - $downgrade_accepts = false; 157 + $want_downgrade = array(); 158 + $must_downgrade = array(); 162 159 if ($this->getIsCloseByCommit()) { 163 160 // Never downgrade reviewers when we're closing a revision after a 164 161 // commit. 165 162 } else { 166 163 switch ($xaction->getTransactionType()) { 167 164 case DifferentialRevisionUpdateTransaction::TRANSACTIONTYPE: 168 - $downgrade_rejects = true; 169 - if (!$is_sticky_accept) { 170 - // If "sticky accept" is disabled, also downgrade the accepts. 171 - $downgrade_accepts = true; 172 - } 165 + $want_downgrade[] = DifferentialReviewerStatus::STATUS_ACCEPTED; 166 + $want_downgrade[] = DifferentialReviewerStatus::STATUS_REJECTED; 173 167 break; 174 168 case DifferentialRevisionRequestReviewTransaction::TRANSACTIONTYPE: 175 - $downgrade_rejects = true; 176 - if ((!$is_sticky_accept) || 177 - (!$object->isChangePlanned())) { 178 - // If the old state isn't "changes planned", downgrade the accepts. 179 - // This exception allows an accepted revision to go through 180 - // "Plan Changes" -> "Request Review" and return to "accepted" if 181 - // the author didn't update the revision, essentially undoing the 182 - // "Plan Changes". 183 - $downgrade_accepts = true; 169 + if (!$object->isChangePlanned()) { 170 + // If the old state isn't "Changes Planned", downgrade the accepts 171 + // even if they're sticky. 172 + 173 + // We don't downgrade for "Changes Planned" to allow an author to 174 + // undo a "Plan Changes" by immediately following it up with a 175 + // "Request Review". 176 + $want_downgrade[] = DifferentialReviewerStatus::STATUS_ACCEPTED; 177 + $must_downgrade[] = DifferentialReviewerStatus::STATUS_ACCEPTED; 184 178 } 179 + $want_downgrade[] = DifferentialReviewerStatus::STATUS_REJECTED; 185 180 break; 186 181 } 187 182 } 188 183 189 - $new_accept = DifferentialReviewerStatus::STATUS_ACCEPTED; 190 - $new_reject = DifferentialReviewerStatus::STATUS_REJECTED; 191 - $old_accept = DifferentialReviewerStatus::STATUS_ACCEPTED_OLDER; 192 - $old_reject = DifferentialReviewerStatus::STATUS_REJECTED_OLDER; 193 - 194 - $downgrade = array(); 195 - if ($downgrade_accepts) { 196 - $downgrade[] = DifferentialReviewerStatus::STATUS_ACCEPTED; 197 - } 198 - 199 - if ($downgrade_rejects) { 200 - $downgrade[] = DifferentialReviewerStatus::STATUS_REJECTED; 201 - } 202 - 203 - if ($downgrade) { 184 + if ($want_downgrade) { 204 185 $void_type = DifferentialRevisionVoidTransaction::TRANSACTIONTYPE; 205 186 206 187 $results[] = id(new DifferentialTransaction()) 207 188 ->setTransactionType($void_type) 208 189 ->setIgnoreOnNoEffect(true) 209 - ->setNewValue($downgrade); 190 + ->setMetadataValue('void.force', $must_downgrade) 191 + ->setNewValue($want_downgrade); 210 192 } 211 193 212 194 $is_commandeer = false;
+13
src/applications/differential/storage/DifferentialReviewer.php
··· 10 10 protected $lastCommentDiffPHID; 11 11 protected $lastActorPHID; 12 12 protected $voidedPHID; 13 + protected $options = array(); 13 14 14 15 private $authority = array(); 15 16 private $changesets = self::ATTACHABLE; 16 17 17 18 protected function getConfiguration() { 18 19 return array( 20 + self::CONFIG_SERIALIZATION => array( 21 + 'options' => self::SERIALIZATION_JSON, 22 + ), 19 23 self::CONFIG_COLUMN_SCHEMA => array( 20 24 'reviewerStatus' => 'text64', 21 25 'lastActionDiffPHID' => 'phid?', ··· 62 66 63 67 public function getChangesets() { 64 68 return $this->assertAttached($this->changesets); 69 + } 70 + 71 + public function setOption($key, $value) { 72 + $this->options[$key] = $value; 73 + return $this; 74 + } 75 + 76 + public function getOption($key, $default = null) { 77 + return idx($this->options, $key, $default); 65 78 } 66 79 67 80 public function isResigned() {
+10 -1
src/applications/differential/xaction/DifferentialRevisionReviewTransaction.php
··· 164 164 DifferentialRevision $revision, 165 165 PhabricatorUser $viewer, 166 166 $value, 167 - $status) { 167 + $status, 168 + array $reviewer_options = array()) { 169 + 170 + PhutilTypeSpec::checkMap( 171 + $reviewer_options, 172 + array( 173 + 'sticky' => 'optional bool', 174 + )); 168 175 169 176 $map = array(); 170 177 ··· 252 259 // by the author using "Request Review" when a reviewer has already 253 260 // accepted. 254 261 $reviewer->setVoidedPHID(null); 262 + 263 + $reviewer->setOption('sticky', idx($reviewer_options, 'sticky')); 255 264 256 265 try { 257 266 $reviewer->save();
+34 -12
src/applications/differential/xaction/DifferentialRevisionVoidTransaction.php
··· 16 16 } 17 17 18 18 public function generateNewValue($object, $value) { 19 - $table = new DifferentialReviewer(); 20 - $table_name = $table->getTableName(); 21 - $conn = $table->establishConnection('w'); 22 - 23 - $rows = queryfx_all( 24 - $conn, 25 - 'SELECT reviewerPHID FROM %T 26 - WHERE revisionPHID = %s 27 - AND voidedPHID IS NULL 28 - AND reviewerStatus IN (%Ls)', 29 - $table_name, 19 + $reviewers = id(new DifferentialReviewer())->loadAllWhere( 20 + 'revisionPHID = %s 21 + AND voidedPHID IS NULL 22 + AND reviewerStatus IN (%Ls)', 30 23 $object->getPHID(), 31 24 $value); 32 25 33 - return ipull($rows, 'reviewerPHID'); 26 + $must_downgrade = $this->getMetadataValue('void.force', array()); 27 + $must_downgrade = array_fuse($must_downgrade); 28 + 29 + $default = PhabricatorEnv::getEnvConfig('differential.sticky-accept'); 30 + foreach ($reviewers as $key => $reviewer) { 31 + $status = $reviewer->getReviewerStatus(); 32 + 33 + // If this void is forced, always downgrade. For example, this happens 34 + // when an author chooses "Request Review": existing reviews are always 35 + // voided, even if they're sticky. 36 + if (isset($must_downgrade[$status])) { 37 + continue; 38 + } 39 + 40 + // Otherwise, if this is a sticky accept, don't void it. Accepts may be 41 + // explicitly sticky or unsticky, or they'll use the default value if 42 + // no value is specified. 43 + $is_sticky = $reviewer->getOption('sticky'); 44 + $is_sticky = coalesce($is_sticky, $default); 45 + 46 + if ($status === DifferentialReviewerStatus::STATUS_ACCEPTED) { 47 + if ($is_sticky) { 48 + unset($reviewers[$key]); 49 + continue; 50 + } 51 + } 52 + } 53 + 54 + 55 + return mpull($reviewers, 'getReviewerPHID'); 34 56 } 35 57 36 58 public function getTransactionHasEffect($object, $old, $new) {