public function handleRequest(AphrontRequest $request)
 {
     $user = $request->getUser();
     $phid = $request->getURIData('phid');
     $attach_type = $request->getURIData('type');
     $action = $request->getURIData('action', self::ACTION_ATTACH);
     $handle = id(new PhabricatorHandleQuery())->setViewer($user)->withPHIDs(array($phid))->executeOne();
     $object_type = $handle->getType();
     $object = id(new PhabricatorObjectQuery())->setViewer($user)->withPHIDs(array($phid))->executeOne();
     if (!$object) {
         return new Aphront404Response();
     }
     $edge_type = null;
     switch ($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($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($phid, $edge_type);
         } else {
             // This is a merge.
             $phids = array();
         }
     }
     $strings = $this->getStrings($attach_type, $action);
     $handles = $this->loadViewerHandles($phids);
     $obj_dialog = new PhabricatorObjectSelectorDialog();
     $obj_dialog->setUser($user)->setHandles($handles)->setFilters($this->getFilters($strings, $attach_type))->setSelectedFilter($strings['selected'])->setExcluded($phid)->setCancelURI($handle->getURI())->setSearchURI('/search/select/' . $attach_type . '/' . $action . '/')->setTitle($strings['title'])->setHeader($strings['header'])->setButtonText($strings['button'])->setInstructions($strings['instructions']);
     $dialog = $obj_dialog->buildDialog();
     return id(new AphrontDialogResponse())->setDialog($dialog);
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     $user = $request->getUser();
     $handle_data = new PhabricatorObjectHandleData(array($this->phid));
     $handles = $handle_data->loadHandles();
     $handle = $handles[$this->phid];
     $object_type = $handle->getType();
     $attach_type = $this->type;
     $objects = $handle_data->loadObjects();
     $object = idx($objects, $this->phid);
     if (!$object) {
         return new Aphront404Response();
     }
     if ($request->isFormPost()) {
         $phids = explode(';', $request->getStr('phids'));
         $phids = array_filter($phids);
         $phids = array_values($phids);
         switch ($this->action) {
             case self::ACTION_MERGE:
                 return $this->performMerge($object, $handle, $phids);
             case self::ACTION_DEPENDENCIES:
             case self::ACTION_ATTACH:
                 $two_way = true;
                 if ($this->action == self::ACTION_DEPENDENCIES) {
                     $two_way = false;
                     $this->detectGraphCycles($object, $attach_type, $phids);
                 }
                 $editor = new PhabricatorObjectAttachmentEditor($object_type, $object);
                 $editor->setUser($this->getRequest()->getUser());
                 $editor->attachObjects($attach_type, $phids, $two_way);
                 return id(new AphrontReloadResponse())->setURI($handle->getURI());
             default:
                 throw new Exception("Unsupported attach action.");
         }
     } else {
         switch ($this->action) {
             case self::ACTION_ATTACH:
             case self::ACTION_DEPENDENCIES:
                 $phids = $object->getAttachedPHIDs($attach_type);
                 break;
             default:
                 $phids = array();
                 break;
         }
     }
     $strings = $this->getStrings();
     $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
     $obj_dialog = new PhabricatorObjectSelectorDialog();
     $obj_dialog->setUser($user)->setHandles($handles)->setFilters(array('assigned' => 'Assigned to Me', 'created' => 'Created By Me', 'open' => 'All Open ' . $strings['target_plural_noun'], 'all' => 'All ' . $strings['target_plural_noun']))->setSelectedFilter($strings['selected'])->setCancelURI($handle->getURI())->setSearchURI('/search/select/' . $attach_type . '/')->setTitle($strings['title'])->setHeader($strings['header'])->setButtonText($strings['button'])->setInstructions($strings['instructions']);
     $dialog = $obj_dialog->buildDialog();
     return id(new AphrontDialogResponse())->setDialog($dialog);
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     $user = $request->getUser();
     $handle_data = new PhabricatorObjectHandleData(array($this->phid));
     $handles = $handle_data->loadHandles();
     $handle = $handles[$this->phid];
     $object_type = $handle->getType();
     $attach_type = $this->type;
     $objects = $handle_data->loadObjects();
     $object = idx($objects, $this->phid);
     if (!$object) {
         return new Aphront404Response();
     }
     $edge_type = null;
     switch ($this->action) {
         case self::ACTION_EDGE:
         case self::ACTION_DEPENDENCIES:
         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) {
             $old_phids = PhabricatorEdgeQuery::loadDestinationPHIDs($this->phid, $edge_type);
             $add_phids = $phids;
             $rem_phids = array_diff($old_phids, $add_phids);
             $editor = id(new PhabricatorEdgeEditor());
             $editor->setUser($user);
             foreach ($add_phids as $phid) {
                 $editor->addEdge($this->phid, $edge_type, $phid);
             }
             foreach ($rem_phids as $phid) {
                 $editor->removeEdge($this->phid, $edge_type, $phid);
             }
             try {
                 $editor->save();
             } 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();
         }
     }
     $strings = $this->getStrings();
     $handles = $this->loadViewerHandles($phids);
     $obj_dialog = new PhabricatorObjectSelectorDialog();
     $obj_dialog->setUser($user)->setHandles($handles)->setFilters(array('assigned' => 'Assigned to Me', 'created' => 'Created By Me', 'open' => 'All Open ' . $strings['target_plural_noun'], 'all' => 'All ' . $strings['target_plural_noun']))->setSelectedFilter($strings['selected'])->setExcluded($this->phid)->setCancelURI($handle->getURI())->setSearchURI('/search/select/' . $attach_type . '/')->setTitle($strings['title'])->setHeader($strings['header'])->setButtonText($strings['button'])->setInstructions($strings['instructions']);
     $dialog = $obj_dialog->buildDialog();
     return id(new AphrontDialogResponse())->setDialog($dialog);
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     $user = $request->getUser();
     $handle_data = new PhabricatorObjectHandleData(array($this->phid));
     $handles = $handle_data->loadHandles();
     $handle = $handles[$this->phid];
     $object_phid = $this->phid;
     $object_type = $handle->getType();
     $attach_type = $this->type;
     $objects = $handle_data->loadObjects();
     $object = idx($objects, $this->phid);
     if (!$object) {
         return new Aphront404Response();
     }
     if ($request->isFormPost()) {
         $phids = explode(';', $request->getStr('phids'));
         $phids = array_filter($phids);
         $phids = array_values($phids);
         switch ($this->action) {
             case self::ACTION_MERGE:
                 return $this->performMerge($object, $handle, $phids);
             case self::ACTION_ATTACH:
                 // Fall through to the workflow below.
                 break;
             default:
                 throw new Exception("Unsupported attach action.");
         }
         // sort() so that removing [X, Y] and then adding [Y, X] is correctly
         // detected as a no-op.
         sort($phids);
         $old_phids = $object->getAttachedPHIDs($attach_type);
         sort($old_phids);
         if (($phids || $old_phids) && $phids !== $old_phids) {
             $all_phids = array_merge($phids, $old_phids);
             $attach_objs = id(new PhabricatorObjectHandleData($all_phids))->loadObjects();
             // Remove PHIDs which don't actually exist, to prevent silliness.
             $phids = array_keys(array_select_keys($attach_objs, $phids));
             if ($phids) {
                 $phids = array_combine($phids, $phids);
             }
             // Update the primary object.
             switch ($object_type) {
                 case PhabricatorPHIDConstants::PHID_TYPE_DREV:
                     $object->setAttachedPHIDs($attach_type, $phids);
                     $object->save();
                     break;
                 case PhabricatorPHIDConstants::PHID_TYPE_TASK:
                     $this->applyTaskTransaction($object, $attach_type, $phids);
                     break;
             }
             // Loop through all of the attached/detached objects and update them.
             foreach ($attach_objs as $phid => $attach_obj) {
                 $attached_phids = $attach_obj->getAttachedPHIDs($object_type);
                 // Figure out if we're attaching or detaching this object.
                 if (isset($phids[$phid])) {
                     $attached_phids[] = $object_phid;
                 } else {
                     $attached_phids = array_fill_keys($attached_phids, true);
                     unset($attached_phids[$object_phid]);
                     $attached_phids = array_keys($attached_phids);
                 }
                 switch ($attach_type) {
                     case PhabricatorPHIDConstants::PHID_TYPE_DREV:
                         $attach_obj->setAttachedPHIDs($object_type, $attached_phids);
                         $attach_obj->save();
                         break;
                     case PhabricatorPHIDConstants::PHID_TYPE_TASK:
                         $this->applyTaskTransaction($attach_obj, $object_type, $attached_phids);
                         break;
                 }
             }
         }
         return id(new AphrontReloadResponse())->setURI($handle->getURI());
     } else {
         switch ($this->action) {
             case self::ACTION_ATTACH:
                 $phids = $object->getAttachedPHIDs($attach_type);
                 break;
             default:
                 $phids = array();
                 break;
         }
     }
     switch ($this->type) {
         case PhabricatorPHIDConstants::PHID_TYPE_DREV:
             $noun = 'Revisions';
             $selected = 'created';
             break;
         case PhabricatorPHIDConstants::PHID_TYPE_TASK:
             $noun = 'Tasks';
             $selected = 'assigned';
             break;
     }
     switch ($this->action) {
         case self::ACTION_ATTACH:
             $dialog_title = "Manage Attached {$noun}";
             $header_text = "Currently Attached {$noun}";
             $button_text = "Save {$noun}";
             $instructions = null;
             break;
         case self::ACTION_MERGE:
             $dialog_title = "Merge Duplicate Tasks";
             $header_text = "Tasks To Merge";
             $button_text = "Merge {$noun}";
             $instructions = "These tasks will be merged into the current task and then closed. " . "The current task (\"" . phutil_escape_html($handle->getName()) . "\") " . "will grow stronger.";
             break;
     }
     $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
     $obj_dialog = new PhabricatorObjectSelectorDialog();
     $obj_dialog->setUser($user)->setHandles($handles)->setFilters(array('assigned' => 'Assigned to Me', 'created' => 'Created By Me', 'open' => 'All Open ' . $noun, 'all' => 'All ' . $noun))->setSelectedFilter($selected)->setCancelURI($handle->getURI())->setSearchURI('/search/select/' . $attach_type . '/')->setTitle($dialog_title)->setHeader($header_text)->setButtonText($button_text)->setInstructions($instructions);
     $dialog = $obj_dialog->buildDialog();
     return id(new AphrontDialogResponse())->setDialog($dialog);
 }