[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/applications/differential/storage/ -> DifferentialTransaction.php (source)

   1  <?php
   2  
   3  final class DifferentialTransaction extends PhabricatorApplicationTransaction {
   4  
   5    private $isCommandeerSideEffect;
   6  
   7  
   8    public function setIsCommandeerSideEffect($is_side_effect) {
   9      $this->isCommandeerSideEffect = $is_side_effect;
  10      return $this;
  11    }
  12  
  13    public function getIsCommandeerSideEffect() {
  14      return $this->isCommandeerSideEffect;
  15    }
  16  
  17    const TYPE_INLINE = 'differential:inline';
  18    const TYPE_UPDATE = 'differential:update';
  19    const TYPE_ACTION = 'differential:action';
  20    const TYPE_STATUS = 'differential:status';
  21  
  22    public function getApplicationName() {
  23      return 'differential';
  24    }
  25  
  26    public function getApplicationTransactionType() {
  27      return DifferentialRevisionPHIDType::TYPECONST;
  28    }
  29  
  30    public function getApplicationTransactionCommentObject() {
  31      return new DifferentialTransactionComment();
  32    }
  33  
  34    public function shouldHide() {
  35      $old = $this->getOldValue();
  36      $new = $this->getNewValue();
  37  
  38      switch ($this->getTransactionType()) {
  39        case self::TYPE_UPDATE:
  40          // Older versions of this transaction have an ID for the new value,
  41          // and/or do not record the old value. Only hide the transaction if
  42          // the new value is a PHID, indicating that this is a newer style
  43          // transaction.
  44          if ($old === null) {
  45            if (phid_get_type($new) == DifferentialDiffPHIDType::TYPECONST) {
  46              return true;
  47            }
  48          }
  49          break;
  50  
  51        case PhabricatorTransactions::TYPE_EDGE:
  52          $add = array_diff_key($new, $old);
  53          $rem = array_diff_key($old, $new);
  54  
  55          // Hide metadata-only edge transactions. These correspond to users
  56          // accepting or rejecting revisions, but the change is always explicit
  57          // because of the TYPE_ACTION transaction. Rendering these transactions
  58          // just creates clutter.
  59  
  60          if (!$add && !$rem) {
  61            return true;
  62          }
  63          break;
  64      }
  65  
  66      return parent::shouldHide();
  67    }
  68  
  69    public function shouldHideForMail(array $xactions) {
  70      switch ($this->getTransactionType()) {
  71        case self::TYPE_INLINE:
  72          // Hide inlines when rendering mail transactions if any other
  73          // transaction type exists.
  74          foreach ($xactions as $xaction) {
  75            if ($xaction->getTransactionType() != self::TYPE_INLINE) {
  76              return true;
  77            }
  78          }
  79  
  80          // If only inline transactions exist, we just render the first one.
  81          return ($this !== head($xactions));
  82      }
  83  
  84      return parent::shouldHideForMail($xactions);
  85    }
  86  
  87    public function getBodyForMail() {
  88      switch ($this->getTransactionType()) {
  89        case self::TYPE_INLINE:
  90          // Don't render inlines into the mail body; they render into a special
  91          // section immediately after the body instead.
  92          return null;
  93      }
  94  
  95      return parent::getBodyForMail();
  96    }
  97  
  98    public function getRequiredHandlePHIDs() {
  99      $phids = parent::getRequiredHandlePHIDs();
 100  
 101      $old = $this->getOldValue();
 102      $new = $this->getNewValue();
 103  
 104      switch ($this->getTransactionType()) {
 105        case self::TYPE_ACTION:
 106          if ($new == DifferentialAction::ACTION_CLOSE &&
 107              $this->getMetadataValue('isCommitClose')) {
 108            $phids[] = $this->getMetadataValue('commitPHID');
 109            if ($this->getMetadataValue('committerPHID')) {
 110              $phids[] = $this->getMetadataValue('committerPHID');
 111            }
 112            if ($this->getMetadataValue('authorPHID')) {
 113              $phids[] = $this->getMetadataValue('authorPHID');
 114            }
 115          }
 116          break;
 117        case self::TYPE_UPDATE:
 118          if ($new) {
 119            $phids[] = $new;
 120          }
 121          break;
 122      }
 123  
 124      return $phids;
 125    }
 126  
 127    public function getActionStrength() {
 128  
 129      switch ($this->getTransactionType()) {
 130        case self::TYPE_ACTION:
 131          return 3;
 132        case self::TYPE_UPDATE:
 133          return 2;
 134        case self::TYPE_INLINE:
 135          return 0.25;
 136      }
 137  
 138      return parent::getActionStrength();
 139    }
 140  
 141  
 142    public function getActionName() {
 143      switch ($this->getTransactionType()) {
 144        case self::TYPE_INLINE:
 145          return pht('Commented On');
 146        case self::TYPE_UPDATE:
 147          $old = $this->getOldValue();
 148          if ($old === null) {
 149            return pht('Request');
 150          } else {
 151            return pht('Updated');
 152          }
 153        case self::TYPE_ACTION:
 154          $map = array(
 155            DifferentialAction::ACTION_ACCEPT => pht('Accepted'),
 156            DifferentialAction::ACTION_REJECT => pht('Requested Changes To'),
 157            DifferentialAction::ACTION_RETHINK => pht('Planned Changes To'),
 158            DifferentialAction::ACTION_ABANDON => pht('Abandoned'),
 159            DifferentialAction::ACTION_CLOSE => pht('Closed'),
 160            DifferentialAction::ACTION_REQUEST => pht('Requested A Review Of'),
 161            DifferentialAction::ACTION_RESIGN => pht('Resigned From'),
 162            DifferentialAction::ACTION_ADDREVIEWERS => pht('Added Reviewers'),
 163            DifferentialAction::ACTION_CLAIM => pht('Commandeered'),
 164            DifferentialAction::ACTION_REOPEN => pht('Reopened'),
 165          );
 166          $name = idx($map, $this->getNewValue());
 167          if ($name !== null) {
 168            return $name;
 169          }
 170          break;
 171      }
 172  
 173      return parent::getActionName();
 174    }
 175  
 176    public function getMailTags() {
 177      $tags = array();
 178  
 179      switch ($this->getTransactionType()) {
 180        case PhabricatorTransactions::TYPE_SUBSCRIBERS;
 181          $tags[] = MetaMTANotificationType::TYPE_DIFFERENTIAL_CC;
 182          break;
 183        case self::TYPE_ACTION:
 184          switch ($this->getNewValue()) {
 185            case DifferentialAction::ACTION_CLOSE:
 186              $tags[] = MetaMTANotificationType::TYPE_DIFFERENTIAL_CLOSED;
 187              break;
 188          }
 189          break;
 190        case self::TYPE_UPDATE:
 191          $old = $this->getOldValue();
 192          if ($old === null) {
 193            $tags[] = MetaMTANotificationType::TYPE_DIFFERENTIAL_REVIEW_REQUEST;
 194          } else {
 195            $tags[] = MetaMTANotificationType::TYPE_DIFFERENTIAL_UPDATED;
 196          }
 197          break;
 198        case PhabricatorTransactions::TYPE_EDGE:
 199          switch ($this->getMetadataValue('edge:type')) {
 200            case PhabricatorEdgeConfig::TYPE_DREV_HAS_REVIEWER:
 201              $tags[] = MetaMTANotificationType::TYPE_DIFFERENTIAL_REVIEWERS;
 202              break;
 203          }
 204          break;
 205        case PhabricatorTransactions::TYPE_COMMENT:
 206        case self::TYPE_INLINE:
 207          $tags[] = MetaMTANotificationType::TYPE_DIFFERENTIAL_COMMENT;
 208          break;
 209      }
 210  
 211      if (!$tags) {
 212        $tags[] = MetaMTANotificationType::TYPE_DIFFERENTIAL_OTHER;
 213      }
 214  
 215      return $tags;
 216    }
 217  
 218    public function getTitle() {
 219      $author_phid = $this->getAuthorPHID();
 220      $author_handle = $this->renderHandleLink($author_phid);
 221  
 222      $old = $this->getOldValue();
 223      $new = $this->getNewValue();
 224  
 225      switch ($this->getTransactionType()) {
 226        case self::TYPE_INLINE:
 227          return pht(
 228            '%s added inline comments.',
 229            $author_handle);
 230        case self::TYPE_UPDATE:
 231          if ($this->getMetadataValue('isCommitUpdate')) {
 232            return pht(
 233              'This revision was automatically updated to reflect the '.
 234              'committed changes.');
 235          } else if ($new) {
 236            // TODO: Migrate to PHIDs and use handles here?
 237            if (phid_get_type($new) == DifferentialDiffPHIDType::TYPECONST) {
 238              return pht(
 239                '%s updated this revision to %s.',
 240                $author_handle,
 241                $this->renderHandleLink($new));
 242            } else {
 243              return pht(
 244                '%s updated this revision.',
 245                $author_handle);
 246            }
 247          } else {
 248            return pht(
 249              '%s updated this revision.',
 250              $author_handle);
 251          }
 252        case self::TYPE_ACTION:
 253          switch ($new) {
 254            case DifferentialAction::ACTION_CLOSE:
 255              if (!$this->getMetadataValue('isCommitClose')) {
 256                return DifferentialAction::getBasicStoryText(
 257                  $new,
 258                  $author_handle);
 259              }
 260              $commit_name = $this->renderHandleLink(
 261                $this->getMetadataValue('commitPHID'));
 262              $committer_phid = $this->getMetadataValue('committerPHID');
 263              $author_phid = $this->getMetadataValue('authorPHID');
 264              if ($this->getHandleIfExists($committer_phid)) {
 265                $committer_name = $this->renderHandleLink($committer_phid);
 266              } else {
 267                $committer_name = $this->getMetadataValue('committerName');
 268              }
 269              if ($this->getHandleIfExists($author_phid)) {
 270                $author_name = $this->renderHandleLink($author_phid);
 271              } else {
 272                $author_name = $this->getMetadataValue('authorName');
 273              }
 274  
 275              if ($committer_name && ($committer_name != $author_name)) {
 276                return pht(
 277                  'Closed by commit %s (authored by %s, committed by %s).',
 278                  $commit_name,
 279                  $author_name,
 280                  $committer_name);
 281              } else {
 282                return pht(
 283                  'Closed by commit %s (authored by %s).',
 284                  $commit_name,
 285                  $author_name);
 286              }
 287              break;
 288            default:
 289              return DifferentialAction::getBasicStoryText($new, $author_handle);
 290          }
 291          break;
 292        case self::TYPE_STATUS:
 293          switch ($this->getNewValue()) {
 294            case ArcanistDifferentialRevisionStatus::ACCEPTED:
 295              return pht(
 296                'This revision is now accepted and ready to land.');
 297            case ArcanistDifferentialRevisionStatus::NEEDS_REVISION:
 298              return pht(
 299                'This revision now requires changes to proceed.');
 300            case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW:
 301              return pht(
 302                'This revision now requires review to proceed.');
 303          }
 304      }
 305  
 306      return parent::getTitle();
 307    }
 308  
 309    public function renderExtraInformationLink() {
 310      if ($this->getMetadataValue('revisionMatchData')) {
 311        $details_href =
 312          '/differential/revision/closedetails/'.$this->getPHID().'/';
 313        $details_link = javelin_tag(
 314          'a',
 315          array(
 316            'href' => $details_href,
 317            'sigil' => 'workflow',
 318          ),
 319          pht('Explain Why'));
 320        return $details_link;
 321      }
 322      return parent::renderExtraInformationLink();
 323    }
 324  
 325    public function getTitleForFeed(PhabricatorFeedStory $story) {
 326      $author_phid = $this->getAuthorPHID();
 327      $object_phid = $this->getObjectPHID();
 328  
 329      $old = $this->getOldValue();
 330      $new = $this->getNewValue();
 331  
 332      $author_link = $this->renderHandleLink($author_phid);
 333      $object_link = $this->renderHandleLink($object_phid);
 334  
 335      switch ($this->getTransactionType()) {
 336        case self::TYPE_INLINE:
 337          return pht(
 338            '%s added inline comments to %s.',
 339            $author_link,
 340            $object_link);
 341        case self::TYPE_UPDATE:
 342          return pht(
 343            '%s updated the diff for %s.',
 344            $author_link,
 345            $object_link);
 346        case self::TYPE_ACTION:
 347          switch ($new) {
 348            case DifferentialAction::ACTION_ACCEPT:
 349              return pht(
 350                '%s accepted %s.',
 351                $author_link,
 352                $object_link);
 353            case DifferentialAction::ACTION_REJECT:
 354              return pht(
 355                '%s requested changes to %s.',
 356                $author_link,
 357                $object_link);
 358            case DifferentialAction::ACTION_RETHINK:
 359              return pht(
 360                '%s planned changes to %s.',
 361                $author_link,
 362                $object_link);
 363            case DifferentialAction::ACTION_ABANDON:
 364              return pht(
 365                '%s abandoned %s.',
 366                $author_link,
 367                $object_link);
 368            case DifferentialAction::ACTION_CLOSE:
 369              if (!$this->getMetadataValue('isCommitClose')) {
 370                return pht(
 371                  '%s closed %s.',
 372                  $author_link,
 373                  $object_link);
 374              } else {
 375                $commit_name = $this->renderHandleLink(
 376                  $this->getMetadataValue('commitPHID'));
 377                $committer_phid = $this->getMetadataValue('committerPHID');
 378                $author_phid = $this->getMetadataValue('authorPHID');
 379                if ($this->getHandleIfExists($committer_phid)) {
 380                  $committer_name = $this->renderHandleLink($committer_phid);
 381                } else {
 382                  $committer_name = $this->getMetadataValue('committerName');
 383                }
 384                if ($this->getHandleIfExists($author_phid)) {
 385                  $author_name = $this->renderHandleLink($author_phid);
 386                } else {
 387                  $author_name = $this->getMetadataValue('authorName');
 388                }
 389  
 390                if ($committer_name && ($committer_name != $author_name)) {
 391                  return pht(
 392                    '%s closed %s by commit %s (authored by %s).',
 393                    $author_link,
 394                    $object_link,
 395                    $commit_name,
 396                    $author_name);
 397                } else {
 398                  return pht(
 399                    '%s closed %s by commit %s.',
 400                    $author_link,
 401                    $object_link,
 402                    $commit_name);
 403                }
 404              }
 405              break;
 406  
 407            case DifferentialAction::ACTION_REQUEST:
 408              return pht(
 409                '%s requested review of %s.',
 410                $author_link,
 411                $object_link);
 412            case DifferentialAction::ACTION_RECLAIM:
 413              return pht(
 414                '%s reclaimed %s.',
 415                $author_link,
 416                $object_link);
 417            case DifferentialAction::ACTION_RESIGN:
 418              return pht(
 419                '%s resigned from %s.',
 420                $author_link,
 421                $object_link);
 422            case DifferentialAction::ACTION_CLAIM:
 423              return pht(
 424                '%s commandeered %s.',
 425                $author_link,
 426                $object_link);
 427            case DifferentialAction::ACTION_REOPEN:
 428              return pht(
 429                '%s reopened %s.',
 430                $author_link,
 431                $object_link);
 432          }
 433          break;
 434        case self::TYPE_STATUS:
 435          switch ($this->getNewValue()) {
 436            case ArcanistDifferentialRevisionStatus::ACCEPTED:
 437              return pht(
 438                '%s is now accepted and ready to land.',
 439                $object_link);
 440            case ArcanistDifferentialRevisionStatus::NEEDS_REVISION:
 441              return pht(
 442                '%s now requires changes to proceed.',
 443                $object_link);
 444            case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW:
 445              return pht(
 446                '%s now requires review to proceed.',
 447                $object_link);
 448          }
 449      }
 450  
 451      return parent::getTitleForFeed($story);
 452    }
 453  
 454    public function getIcon() {
 455      switch ($this->getTransactionType()) {
 456        case self::TYPE_INLINE:
 457          return 'fa-comment';
 458        case self::TYPE_UPDATE:
 459          return 'fa-refresh';
 460        case self::TYPE_STATUS:
 461          switch ($this->getNewValue()) {
 462            case ArcanistDifferentialRevisionStatus::ACCEPTED:
 463              return 'fa-check';
 464            case ArcanistDifferentialRevisionStatus::NEEDS_REVISION:
 465              return 'fa-times';
 466            case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW:
 467              return 'fa-undo';
 468          }
 469          break;
 470        case self::TYPE_ACTION:
 471          switch ($this->getNewValue()) {
 472            case DifferentialAction::ACTION_CLOSE:
 473              return 'fa-check';
 474            case DifferentialAction::ACTION_ACCEPT:
 475              return 'fa-check-circle-o';
 476            case DifferentialAction::ACTION_REJECT:
 477              return 'fa-times-circle-o';
 478            case DifferentialAction::ACTION_ABANDON:
 479              return 'fa-plane';
 480            case DifferentialAction::ACTION_RETHINK:
 481              return 'fa-headphones';
 482            case DifferentialAction::ACTION_REQUEST:
 483              return 'fa-refresh';
 484            case DifferentialAction::ACTION_RECLAIM:
 485            case DifferentialAction::ACTION_REOPEN:
 486              return 'fa-bullhorn';
 487            case DifferentialAction::ACTION_RESIGN:
 488              return 'fa-flag';
 489            case DifferentialAction::ACTION_CLAIM:
 490              return 'fa-flag';
 491          }
 492        case PhabricatorTransactions::TYPE_EDGE:
 493          switch ($this->getMetadataValue('edge:type')) {
 494            case PhabricatorEdgeConfig::TYPE_DREV_HAS_REVIEWER:
 495              return 'fa-user';
 496          }
 497      }
 498  
 499      return parent::getIcon();
 500    }
 501  
 502    public function shouldDisplayGroupWith(array $group) {
 503  
 504      // Never group status changes with other types of actions, they're indirect
 505      // and don't make sense when combined with direct actions.
 506  
 507      $type_status = self::TYPE_STATUS;
 508  
 509      if ($this->getTransactionType() == $type_status) {
 510        return false;
 511      }
 512  
 513      foreach ($group as $xaction) {
 514        if ($xaction->getTransactionType() == $type_status) {
 515          return false;
 516        }
 517      }
 518  
 519      return parent::shouldDisplayGroupWith($group);
 520    }
 521  
 522  
 523    public function getColor() {
 524      switch ($this->getTransactionType()) {
 525        case self::TYPE_UPDATE:
 526          return PhabricatorTransactions::COLOR_SKY;
 527        case self::TYPE_STATUS:
 528          switch ($this->getNewValue()) {
 529            case ArcanistDifferentialRevisionStatus::ACCEPTED:
 530              return PhabricatorTransactions::COLOR_GREEN;
 531            case ArcanistDifferentialRevisionStatus::NEEDS_REVISION:
 532              return PhabricatorTransactions::COLOR_RED;
 533            case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW:
 534              return PhabricatorTransactions::COLOR_ORANGE;
 535          }
 536          break;
 537        case self::TYPE_ACTION:
 538          switch ($this->getNewValue()) {
 539            case DifferentialAction::ACTION_CLOSE:
 540              return PhabricatorTransactions::COLOR_BLUE;
 541            case DifferentialAction::ACTION_ACCEPT:
 542              return PhabricatorTransactions::COLOR_GREEN;
 543            case DifferentialAction::ACTION_REJECT:
 544              return PhabricatorTransactions::COLOR_RED;
 545            case DifferentialAction::ACTION_ABANDON:
 546              return PhabricatorTransactions::COLOR_BLACK;
 547            case DifferentialAction::ACTION_RETHINK:
 548              return PhabricatorTransactions::COLOR_RED;
 549            case DifferentialAction::ACTION_REQUEST:
 550              return PhabricatorTransactions::COLOR_SKY;
 551            case DifferentialAction::ACTION_RECLAIM:
 552              return PhabricatorTransactions::COLOR_SKY;
 553            case DifferentialAction::ACTION_REOPEN:
 554              return PhabricatorTransactions::COLOR_SKY;
 555            case DifferentialAction::ACTION_RESIGN:
 556              return PhabricatorTransactions::COLOR_ORANGE;
 557            case DifferentialAction::ACTION_CLAIM:
 558              return PhabricatorTransactions::COLOR_YELLOW;
 559          }
 560      }
 561  
 562  
 563      return parent::getColor();
 564    }
 565  
 566    public function getNoEffectDescription() {
 567      switch ($this->getTransactionType()) {
 568        case PhabricatorTransactions::TYPE_EDGE:
 569          switch ($this->getMetadataValue('edge:type')) {
 570            case PhabricatorEdgeConfig::TYPE_DREV_HAS_REVIEWER:
 571              return pht(
 572                'The reviewers you are trying to add are already reviewing '.
 573                'this revision.');
 574          }
 575          break;
 576        case DifferentialTransaction::TYPE_ACTION:
 577          switch ($this->getNewValue()) {
 578            case DifferentialAction::ACTION_CLOSE:
 579              return pht('This revision is already closed.');
 580            case DifferentialAction::ACTION_ABANDON:
 581              return pht('This revision has already been abandoned.');
 582            case DifferentialAction::ACTION_RECLAIM:
 583              return pht(
 584                'You can not reclaim this revision because his revision is '.
 585                'not abandoned.');
 586            case DifferentialAction::ACTION_REOPEN:
 587              return pht(
 588                'You can not reopen this revision because this revision is '.
 589                'not closed.');
 590            case DifferentialAction::ACTION_RETHINK:
 591              return pht('This revision already requires changes.');
 592            case DifferentialAction::ACTION_REQUEST:
 593              return pht('Review is already requested for this revision.');
 594            case DifferentialAction::ACTION_RESIGN:
 595              return pht(
 596                'You can not resign from this revision because you are not '.
 597                'a reviewer.');
 598            case DifferentialAction::ACTION_CLAIM:
 599              return pht(
 600                'You can not commandeer this revision because you already own '.
 601                'it.');
 602            case DifferentialAction::ACTION_ACCEPT:
 603              return pht(
 604                'You have already accepted this revision.');
 605            case DifferentialAction::ACTION_REJECT:
 606              return pht(
 607                'You have already requested changes to this revision.');
 608          }
 609          break;
 610      }
 611  
 612      return parent::getNoEffectDescription();
 613    }
 614  
 615    public function renderAsTextForDoorkeeper(
 616      DoorkeeperFeedStoryPublisher $publisher,
 617      PhabricatorFeedStory $story,
 618      array $xactions) {
 619  
 620      $body = parent::renderAsTextForDoorkeeper($publisher, $story, $xactions);
 621  
 622      $inlines = array();
 623      foreach ($xactions as $xaction) {
 624        if ($xaction->getTransactionType() == self::TYPE_INLINE) {
 625          $inlines[] = $xaction;
 626        }
 627      }
 628  
 629      // TODO: This is a bit gross, but far less bad than it used to be. It
 630      // could be further cleaned up at some point.
 631  
 632      if ($inlines) {
 633        $engine = PhabricatorMarkupEngine::newMarkupEngine(array())
 634          ->setConfig('viewer', new PhabricatorUser())
 635          ->setMode(PhutilRemarkupEngine::MODE_TEXT);
 636  
 637        $body .= "\n\n";
 638        $body .= pht('Inline Comments');
 639        $body .= "\n";
 640  
 641        $changeset_ids = array();
 642        foreach ($inlines as $inline) {
 643          $changeset_ids[] = $inline->getComment()->getChangesetID();
 644        }
 645  
 646        $changesets = id(new DifferentialChangeset())->loadAllWhere(
 647          'id IN (%Ld)',
 648          $changeset_ids);
 649  
 650        foreach ($inlines as $inline) {
 651          $comment = $inline->getComment();
 652          $changeset = idx($changesets, $comment->getChangesetID());
 653          if (!$changeset) {
 654            continue;
 655          }
 656  
 657          $filename = $changeset->getDisplayFilename();
 658          $linenumber = $comment->getLineNumber();
 659          $inline_text = $engine->markupText($comment->getContent());
 660          $inline_text = rtrim($inline_text);
 661  
 662          $body .= "{$filename}:{$linenumber} {$inline_text}\n";
 663        }
 664      }
 665  
 666      return $body;
 667    }
 668  
 669  
 670  }


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1