<commit_before><?php

final class PhabricatorSearchAttachController
  extends PhabricatorSearchBaseController {

  private $phid;
  private $type;
  private $action;

  const ACTION_ATTACH       = 'attach';
  const ACTION_MERGE        = 'merge';
  const ACTION_DEPENDENCIES = 'dependencies';
  const ACTION_BLOCKS       = 'blocks';
  const ACTION_EDGE         = 'edge';

  public function willProcessRequest(array $data) {
    $this->phid = $data['phid'];
    $this->type = $data['type'];
    $this->action = idx($data, 'action', self::ACTION_ATTACH);
  }

  public function processRequest() {

    $request = $this->getRequest();
    $user = $request->getUser();

    $handle = id(new PhabricatorHandleQuery())
      ->setViewer($user)
      ->withPHIDs(array($this->phid))
      ->executeOne();

    $object_type = $handle->getType();
    $attach_type = $this->type;

    $object = id(new PhabricatorObjectQuery())
      ->setViewer($user)
      ->withPHIDs(array($this->phid))
      ->executeOne();

    if (!$object) {
      return new Aphront404Response();
    }

    $edge_type = null;
    switch ($this->action) {
      case self::ACTION_EDGE:
      case self::ACTION_DEPENDENCIES:
      case self::ACTION_BLOCKS:
      case self::ACTION_ATTACH:
        $edge_type = $this->getEdgeType($object_type, $attach_type);
        break;
    }

    if ($request->isFormPost()) {
      $phids = explode(';', $request->getStr('phids'));
      $phids = array_filter($phids);
      $phids = array_values($phids);

      if ($edge_type) {
        if (!$object instanceof PhabricatorApplicationTransactionInterface) {
          throw new Exception(
            pht(
              'Expected object ("%s") to implement interface "%s".',
              get_class($object),
              'PhabricatorApplicationTransactionInterface'));
        }

        $old_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
          $this->phid,
          $edge_type);
        $add_phids = $phids;
        $rem_phids = array_diff($old_phids, $add_phids);

        $txn_editor = $object->getApplicationTransactionEditor()
          ->setActor($user)
          ->setContentSourceFromRequest($request)
          ->setContinueOnMissingFields(true)
          ->setContinueOnNoEffect(true);

        $txn_template = $object->getApplicationTransactionTemplate()
          ->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
          ->setMetadataValue('edge:type', $edge_type)
          ->setNewValue(array(
            '+' => array_fuse($add_phids),
            '-' => array_fuse($rem_phids),
          ));
        $txn_editor->applyTransactions(
          $object->getApplicationTransactionObject(),
          array($txn_template));

        return id(new AphrontReloadResponse())->setURI($handle->getURI());
      } else {
        return $this->performMerge($object, $handle, $phids);
      }
    } else {
      if ($edge_type) {
        $phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
          $this->phid,
          $edge_type);
      } else {
        // This is a merge.
        $phids = array();
      }
    }
<commit_msg>Improve exception when creating a cyclic dependency in Maniphest<commit_after><?php

final class PhabricatorSearchAttachController
  extends PhabricatorSearchBaseController {

  private $phid;
  private $type;
  private $action;

  const ACTION_ATTACH       = 'attach';
  const ACTION_MERGE        = 'merge';
  const ACTION_DEPENDENCIES = 'dependencies';
  const ACTION_BLOCKS       = 'blocks';
  const ACTION_EDGE         = 'edge';

  public function willProcessRequest(array $data) {
    $this->phid = $data['phid'];
    $this->type = $data['type'];
    $this->action = idx($data, 'action', self::ACTION_ATTACH);
  }

  public function processRequest() {

    $request = $this->getRequest();
    $user = $request->getUser();

    $handle = id(new PhabricatorHandleQuery())
      ->setViewer($user)
      ->withPHIDs(array($this->phid))
      ->executeOne();

    $object_type = $handle->getType();
    $attach_type = $this->type;

    $object = id(new PhabricatorObjectQuery())
      ->setViewer($user)
      ->withPHIDs(array($this->phid))
      ->executeOne();

    if (!$object) {
      return new Aphront404Response();
    }

    $edge_type = null;
    switch ($this->action) {
      case self::ACTION_EDGE:
      case self::ACTION_DEPENDENCIES:
      case self::ACTION_BLOCKS:
      case self::ACTION_ATTACH:
        $edge_type = $this->getEdgeType($object_type, $attach_type);
        break;
    }

    if ($request->isFormPost()) {
      $phids = explode(';', $request->getStr('phids'));
      $phids = array_filter($phids);
      $phids = array_values($phids);

      if ($edge_type) {
        if (!$object instanceof PhabricatorApplicationTransactionInterface) {
          throw new Exception(
            pht(
              'Expected object ("%s") to implement interface "%s".',
              get_class($object),
              'PhabricatorApplicationTransactionInterface'));
        }

        $old_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
          $this->phid,
          $edge_type);
        $add_phids = $phids;
        $rem_phids = array_diff($old_phids, $add_phids);

        $txn_editor = $object->getApplicationTransactionEditor()
          ->setActor($user)
          ->setContentSourceFromRequest($request)
          ->setContinueOnMissingFields(true)
          ->setContinueOnNoEffect(true);

        $txn_template = $object->getApplicationTransactionTemplate()
          ->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
          ->setMetadataValue('edge:type', $edge_type)
          ->setNewValue(array(
            '+' => array_fuse($add_phids),
            '-' => array_fuse($rem_phids),
          ));

        try {
          $txn_editor->applyTransactions(
            $object->getApplicationTransactionObject(),
            array($txn_template));
        } catch (PhabricatorEdgeCycleException $ex) {
          $this->raiseGraphCycleException($ex);
        }

        return id(new AphrontReloadResponse())->setURI($handle->getURI());
      } else {
        return $this->performMerge($object, $handle, $phids);
      }
    } else {
      if ($edge_type) {
        $phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
          $this->phid,
          $edge_type);
      } else {
        // This is a merge.
        $phids = array();
      }
    }

{'fraction_non_alphanumeric': 0.13830783640337183, 'fraction_numerical': 0.0009366219169528567, 'mean_word_length': 2.6116121758737316, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 78, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5327143', 'n_tokens_mistral': 1964, 'n_tokens_neox': 1926, 'n_words': 331}