private function performMerge(ManiphestTask $task, PhabricatorObjectHandle $handle, array $phids) { $user = $this->getRequest()->getUser(); $response = id(new AphrontReloadResponse())->setURI($handle->getURI()); $phids = array_fill_keys($phids, true); unset($phids[$task->getPHID()]); // Prevent merging a task into itself. if (!$phids) { return $response; } $targets = id(new ManiphestTaskQuery())->setViewer($user)->withPHIDs(array_keys($phids))->execute(); if (empty($targets)) { return $response; } $editor = id(new ManiphestTransactionEditor())->setActor($user)->setContentSourceFromRequest($this->getRequest())->setContinueOnNoEffect(true)->setContinueOnMissingFields(true); $cc_vector = array(); $cc_vector[] = $task->getCCPHIDs(); foreach ($targets as $target) { $cc_vector[] = $target->getCCPHIDs(); $cc_vector[] = array($target->getAuthorPHID(), $target->getOwnerPHID()); $merged_into_txn = id(new ManiphestTransaction())->setTransactionType(ManiphestTransaction::TYPE_MERGED_INTO)->setNewValue($task->getPHID()); $editor->applyTransactions($target, array($merged_into_txn)); } $all_ccs = array_mergev($cc_vector); $all_ccs = array_filter($all_ccs); $all_ccs = array_unique($all_ccs); $add_ccs = id(new ManiphestTransaction())->setTransactionType(ManiphestTransaction::TYPE_CCS)->setNewValue($all_ccs); $merged_from_txn = id(new ManiphestTransaction())->setTransactionType(ManiphestTransaction::TYPE_MERGED_FROM)->setNewValue(mpull($targets, 'getPHID')); $editor->applyTransactions($task, array($add_ccs, $merged_from_txn)); return $response; }
private function getPriorityTransactions(ManiphestTask $task, $after_phid, $before_phid) { list($after_task, $before_task) = $this->loadPriorityTasks($after_phid, $before_phid); $must_move = false; if ($after_task && !$task->isLowerPriorityThan($after_task)) { $must_move = true; } if ($before_task && !$task->isHigherPriorityThan($before_task)) { $must_move = true; } // The move doesn't require a priority change to be valid, so don't // change the priority since we are not being forced to. if (!$must_move) { return array(); } $try = array(array($after_task, true), array($before_task, false)); $pri = null; $sub = null; foreach ($try as $spec) { list($task, $is_after) = $spec; if (!$task) { continue; } list($pri, $sub) = ManiphestTransactionEditor::getAdjacentSubpriority($task, $is_after); } $xactions = array(); if ($pri !== null) { $xactions[] = id(new ManiphestTransaction())->setTransactionType(ManiphestTransaction::TYPE_PRIORITY)->setNewValue($pri); $xactions[] = id(new ManiphestTransaction())->setTransactionType(ManiphestTransaction::TYPE_SUBPRIORITY)->setNewValue($sub); } return $xactions; }
protected function execute(ConduitAPIRequest $request) { $task = new ManiphestTask(); $task->setPriority(ManiphestTaskPriority::PRIORITY_TRIAGE); $task->setAuthorPHID($request->getUser()->getPHID()); $this->applyRequest($task, $request, $is_new = true); return $this->buildTaskInfoDictionary($task); }
public function processRequest() { $request = $this->getRequest(); $description = $request->getStr('description'); $task = new ManiphestTask(); $task->setDescription($description); $output = PhabricatorMarkupEngine::renderOneObject($task, ManiphestTask::MARKUP_FIELD_DESCRIPTION, $request->getUser()); $content = phutil_tag_div('phabricator-remarkup', $output); return id(new AphrontAjaxResponse())->setContent($content); }
public function processRequest() { $request = $this->getRequest(); $description = $request->getStr('description'); $task = new ManiphestTask(); $task->setDescription($description); $output = PhabricatorMarkupEngine::renderOneObject($task, ManiphestTask::MARKUP_FIELD_DESCRIPTION); $content = '<div class="phabricator-remarkup">' . $output . '</div>'; return id(new AphrontAjaxResponse())->setContent($content); }
protected function renderSingleTask(ManiphestTask $task) { $request = $this->getRequest(); $user = $request->getUser(); $phids = $task->getProjectPHIDs(); if ($task->getOwnerPHID()) { $phids[] = $task->getOwnerPHID(); } $handles = id(new PhabricatorHandleQuery())->setViewer($user)->withPHIDs($phids)->execute(); $view = id(new ManiphestTaskListView())->setUser($user)->setShowSubpriorityControls(!$request->getStr('ungrippable'))->setShowBatchControls(true)->setHandles($handles)->setTasks(array($task)); return $view; }
public static function updateTaskProjects(ManiphestTask $task) { $dao = new ManiphestTaskProject(); $conn = $dao->establishConnection('w'); $sql = array(); foreach ($task->getProjectPHIDs() as $project_phid) { $sql[] = qsprintf($conn, '(%s, %s)', $task->getPHID(), $project_phid); } queryfx($conn, 'DELETE FROM %T WHERE taskPHID = %s', $dao->getTableName(), $task->getPHID()); if ($sql) { queryfx($conn, 'INSERT INTO %T (taskPHID, projectPHID) VALUES %Q', $dao->getTableName(), implode(', ', $sql)); } }
public function testFileVisibility() { $engine = new PhabricatorTestStorageEngine(); $data = Filesystem::readRandomCharacters(64); $author = $this->generateNewTestUser(); $viewer = $this->generateNewTestUser(); $users = array($author, $viewer); $params = array('name' => 'test.dat', 'viewPolicy' => PhabricatorPolicies::POLICY_NOONE, 'authorPHID' => $author->getPHID(), 'storageEngines' => array($engine)); $file = PhabricatorFile::newFromFileData($data, $params); $filter = new PhabricatorPolicyFilter(); // Test bare file policies. $this->assertEqual(array(true, false), $this->canViewFile($users, $file), pht('File Visibility')); // Create an object and test object policies. $object = ManiphestTask::initializeNewTask($author); $object->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy()); $object->save(); $this->assertTrue($filter->hasCapability($author, $object, PhabricatorPolicyCapability::CAN_VIEW), pht('Object Visible to Author')); $this->assertTrue($filter->hasCapability($viewer, $object, PhabricatorPolicyCapability::CAN_VIEW), pht('Object Visible to Others')); // Attach the file to the object and test that the association opens a // policy exception for the non-author viewer. $file->attachToObject($object->getPHID()); // Test the attached file's visibility. $this->assertEqual(array(true, true), $this->canViewFile($users, $file), pht('Attached File Visibility')); // Create a "thumbnail" of the original file. $params = array('name' => 'test.thumb.dat', 'viewPolicy' => PhabricatorPolicies::POLICY_NOONE, 'storageEngines' => array($engine)); $xform = PhabricatorFile::newFromFileData($data, $params); id(new PhabricatorTransformedFile())->setOriginalPHID($file->getPHID())->setTransform('test-thumb')->setTransformedPHID($xform->getPHID())->save(); // Test the thumbnail's visibility. $this->assertEqual(array(true, true), $this->canViewFile($users, $xform), pht('Attached Thumbnail Visibility')); // Detach the object and make sure it affects the thumbnail. $file->detachFromObject($object->getPHID()); // Test the detached thumbnail's visibility. $this->assertEqual(array(true, false), $this->canViewFile($users, $xform), pht('Detached Thumbnail Visibility')); }
public static function updateTaskSubscribers(ManiphestTask $task) { $dao = new ManiphestTaskSubscriber(); $conn = $dao->establishConnection('w'); $sql = array(); $subscribers = $task->getCCPHIDs(); $subscribers[] = $task->getOwnerPHID(); $subscribers = array_unique($subscribers); foreach ($subscribers as $subscriber_phid) { $sql[] = qsprintf($conn, '(%s, %s)', $task->getPHID(), $subscriber_phid); } queryfx($conn, 'DELETE FROM %T WHERE taskPHID = %s', $dao->getTableName(), $task->getPHID()); if ($sql) { queryfx($conn, 'INSERT INTO %T (taskPHID, subscriberPHID) VALUES %Q', $dao->getTableName(), implode(', ', $sql)); } }
public function generateObject() { $author_phid = $this->loadPhabrictorUserPHID(); $author = id(new PhabricatorUser())->loadOneWhere('phid = %s', $author_phid); $task = ManiphestTask::initializeNewTask($author)->setSubPriority($this->generateTaskSubPriority())->setTitle($this->generateTitle()); $content_source = PhabricatorContentSource::newForSource(PhabricatorContentSource::SOURCE_UNKNOWN, array()); $template = new ManiphestTransaction(); // Accumulate Transactions $changes = array(); $changes[ManiphestTransaction::TYPE_TITLE] = $this->generateTitle(); $changes[ManiphestTransaction::TYPE_DESCRIPTION] = $this->generateDescription(); $changes[ManiphestTransaction::TYPE_OWNER] = $this->loadOwnerPHID(); $changes[ManiphestTransaction::TYPE_STATUS] = $this->generateTaskStatus(); $changes[ManiphestTransaction::TYPE_PRIORITY] = $this->generateTaskPriority(); $changes[PhabricatorTransactions::TYPE_SUBSCRIBERS] = array('=' => $this->getCCPHIDs()); $transactions = array(); foreach ($changes as $type => $value) { $transaction = clone $template; $transaction->setTransactionType($type); $transaction->setNewValue($value); $transactions[] = $transaction; } $transactions[] = id(new ManiphestTransaction())->setTransactionType(PhabricatorTransactions::TYPE_EDGE)->setMetadataValue('edge:type', PhabricatorProjectObjectHasProjectEdgeType::EDGECONST)->setNewValue(array('=' => array_fuse($this->getProjectPHIDs()))); // Apply Transactions $editor = id(new ManiphestTransactionEditor())->setActor($author)->setContentSource($content_source)->setContinueOnNoEffect(true)->setContinueOnMissingFields(true)->applyTransactions($task, $transactions); return $task; }
private function performMerge(ManiphestTask $task, PhabricatorObjectHandle $handle, array $phids) { $user = $this->getRequest()->getUser(); $response = id(new AphrontReloadResponse())->setURI($handle->getURI()); $phids = array_fill_keys($phids, true); unset($phids[$task->getPHID()]); // Prevent merging a task into itself. if (!$phids) { return $response; } $targets = id(new ManiphestTaskQuery())->setViewer($user)->requireCapabilities(array(PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT))->withPHIDs(array_keys($phids))->needSubscriberPHIDs(true)->needProjectPHIDs(true)->execute(); if (empty($targets)) { return $response; } $editor = id(new ManiphestTransactionEditor())->setActor($user)->setContentSourceFromRequest($this->getRequest())->setContinueOnNoEffect(true)->setContinueOnMissingFields(true); $cc_vector = array(); // since we loaded this via a generic object query, go ahead and get the // attach the subscriber and project phids now $task->attachSubscriberPHIDs(PhabricatorSubscribersQuery::loadSubscribersForPHID($task->getPHID())); $task->attachProjectPHIDs(PhabricatorEdgeQuery::loadDestinationPHIDs($task->getPHID(), PhabricatorProjectObjectHasProjectEdgeType::EDGECONST)); $cc_vector[] = $task->getSubscriberPHIDs(); foreach ($targets as $target) { $cc_vector[] = $target->getSubscriberPHIDs(); $cc_vector[] = array($target->getAuthorPHID(), $target->getOwnerPHID()); $merged_into_txn = id(new ManiphestTransaction())->setTransactionType(ManiphestTransaction::TYPE_MERGED_INTO)->setNewValue($task->getPHID()); $editor->applyTransactions($target, array($merged_into_txn)); } $all_ccs = array_mergev($cc_vector); $all_ccs = array_filter($all_ccs); $all_ccs = array_unique($all_ccs); $add_ccs = id(new ManiphestTransaction())->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS)->setNewValue(array('=' => $all_ccs)); $merged_from_txn = id(new ManiphestTransaction())->setTransactionType(ManiphestTransaction::TYPE_MERGED_FROM)->setNewValue(mpull($targets, 'getPHID')); $editor->applyTransactions($task, array($add_ccs, $merged_from_txn)); return $response; }
protected function processReceivedMail(PhabricatorMetaMTAReceivedMail $mail, PhabricatorUser $sender) { $task = ManiphestTask::initializeNewTask($sender); $task->setOriginalEmailSource($mail->getHeader('From')); $handler = PhabricatorEnv::newObjectFromConfig('metamta.maniphest.reply-handler'); $handler->setMailReceiver($task); $handler->setActor($sender); $handler->setExcludeMailRecipientPHIDs($mail->loadExcludeMailRecipientPHIDs()); $handler->processEmail($mail); $mail->setRelatedPHID($task->getPHID()); }
protected function processReceivedMail(PhabricatorMetaMTAReceivedMail $mail, PhabricatorUser $sender) { $task = ManiphestTask::initializeNewTask($sender); $task->setOriginalEmailSource($mail->getHeader('From')); $handler = new ManiphestReplyHandler(); $handler->setMailReceiver($task); $handler->setActor($sender); $handler->setExcludeMailRecipientPHIDs($mail->loadAllRecipientPHIDs()); if ($this->getApplicationEmail()) { $handler->setApplicationEmail($this->getApplicationEmail()); } $handler->processEmail($mail); $mail->setRelatedPHID($task->getPHID()); }
private function performMerge(ManiphestTask $task, PhabricatorObjectHandle $handle, array $phids) { $user = $this->getRequest()->getUser(); $response = id(new AphrontReloadResponse())->setURI($handle->getURI()); $phids = array_fill_keys($phids, true); unset($phids[$task->getPHID()]); // Prevent merging a task into itself. if (!$phids) { return $response; } $targets = id(new ManiphestTaskQuery())->setViewer($user)->withPHIDs(array_keys($phids))->execute(); if (empty($targets)) { return $response; } $editor = id(new ManiphestTransactionEditor())->setActor($user)->setContentSourceFromRequest($this->getRequest())->setContinueOnNoEffect(true)->setContinueOnMissingFields(true); $task_names = array(); $merge_into_name = 'T' . $task->getID(); $cc_vector = array(); $cc_vector[] = $task->getCCPHIDs(); foreach ($targets as $target) { $cc_vector[] = $target->getCCPHIDs(); $cc_vector[] = array($target->getAuthorPHID(), $target->getOwnerPHID()); $close_task = id(new ManiphestTransaction())->setTransactionType(ManiphestTransaction::TYPE_STATUS)->setNewValue(ManiphestTaskStatus::getDuplicateStatus()); $merge_comment = id(new ManiphestTransaction())->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)->attachComment(id(new ManiphestTransactionComment())->setContent("✘ Merged into {$merge_into_name}.")); $editor->applyTransactions($target, array($close_task, $merge_comment)); $task_names[] = 'T' . $target->getID(); } $all_ccs = array_mergev($cc_vector); $all_ccs = array_filter($all_ccs); $all_ccs = array_unique($all_ccs); $task_names = implode(', ', $task_names); $add_ccs = id(new ManiphestTransaction())->setTransactionType(ManiphestTransaction::TYPE_CCS)->setNewValue($all_ccs); $merged_comment = id(new ManiphestTransaction())->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)->attachComment(id(new ManiphestTransactionComment())->setContent("◀ Merged tasks: {$task_names}.")); $editor->applyTransactions($task, array($add_ccs, $merged_comment)); return $response; }
public function testCustomPolicyRuleLunarPhase() { $user_a = $this->generateNewTestUser(); $author = $this->generateNewTestUser(); $policy = id(new PhabricatorPolicy())->setRules(array(array('action' => PhabricatorPolicy::ACTION_ALLOW, 'rule' => 'PhabricatorPolicyRuleLunarPhase', 'value' => 'new')))->save(); $task = ManiphestTask::initializeNewTask($author); $task->setViewPolicy($policy->getPHID()); $task->save(); $time_a = PhabricatorTime::pushTime(934354800, 'UTC'); $can_a_view = PhabricatorPolicyFilter::hasCapability($user_a, $task, PhabricatorPolicyCapability::CAN_VIEW); $this->assertTrue($can_a_view); unset($time_a); $time_b = PhabricatorTime::pushTime(1116745200, 'UTC'); $can_a_view = PhabricatorPolicyFilter::hasCapability($user_a, $task, PhabricatorPolicyCapability::CAN_VIEW); $this->assertFalse($can_a_view); unset($time_b); }
function addTask($kan_task, $project_id) { $user = getAdmin(); $task = ManiphestTask::initializeNewTask($user); $changes = array(); $transactions = array(); $changes[ManiphestTransaction::TYPE_TITLE] = $kan_task['title']; $changes[ManiphestTransaction::TYPE_DESCRIPTION] = $kan_task['description']; $changes[ManiphestTransaction::TYPE_STATUS] = ManiphestTaskStatus::getDefaultStatus(); $changes[PhabricatorTransactions::TYPE_COMMENT] = null; $project_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST; $transactions[] = id(new ManiphestTransaction())->setTransactionType(PhabricatorTransactions::TYPE_EDGE)->setMetadataValue('edge:type', $project_type)->setNewValue(array('=' => array_fuse(array($project_id)))); $template = new ManiphestTransaction(); foreach ($changes as $type => $value) { $transaction = clone $template; $transaction->setTransactionType($type); $transaction->setNewValue($value); $transactions[] = $transaction; } $editor = id(new ManiphestTransactionEditor())->setActor($user)->setContentSourceFromConduitRequest(new ConduitAPIRequest(array()))->setContinueOnNoEffect(true); $editor->applyTransactions($task, $transactions); }
protected function execute(ConduitAPIRequest $request) { $task = new ManiphestTask(); $task->setPriority(ManiphestTaskPriority::PRIORITY_TRIAGE); $task->setAuthorPHID($request->getUser()->getPHID()); $task->setTitle((string) $request->getValue('title')); $task->setDescription((string) $request->getValue('description')); $changes = array(); $changes[ManiphestTransactionType::TYPE_STATUS] = ManiphestTaskStatus::STATUS_OPEN; $priority = $request->getValue('priority'); if ($priority !== null) { $changes[ManiphestTransactionType::TYPE_PRIORITY] = $priority; } $owner_phid = $request->getValue('ownerPHID'); if ($owner_phid !== null) { $changes[ManiphestTransactionType::TYPE_OWNER] = $owner_phid; } $ccs = $request->getValue('ccPHIDs'); if ($ccs !== null) { $changes[ManiphestTransactionType::TYPE_CCS] = $ccs; } $project_phids = $request->getValue('projectPHIDs'); if ($project_phids !== null) { $changes[ManiphestTransactionType::TYPE_PROJECTS] = $project_phids; } $file_phids = $request->getValue('filePHIDs'); if ($file_phids !== null) { $file_map = array_fill_keys($file_phids, true); $changes[ManiphestTransactionType::TYPE_ATTACH] = array(PhabricatorPHIDConstants::PHID_TYPE_FILE => $file_map); } $content_source = PhabricatorContentSource::newForSource(PhabricatorContentSource::SOURCE_CONDUIT, array()); $template = new ManiphestTransaction(); $template->setContentSource($content_source); $template->setAuthorPHID($request->getUser()->getPHID()); $transactions = array(); foreach ($changes as $type => $value) { $transaction = clone $template; $transaction->setTransactionType($type); $transaction->setNewValue($value); $transactions[] = $transaction; } $editor = new ManiphestTransactionEditor(); $editor->applyTransactions($task, $transactions); return $this->buildTaskInfoDictionary($task); }
public function execute() { $task_dao = new ManiphestTask(); $conn = $task_dao->establishConnection('r'); if ($this->calculateRows) { $calc = 'SQL_CALC_FOUND_ROWS'; } else { $calc = ''; } $where = array(); $where[] = $this->buildTaskIDsWhereClause($conn); $where[] = $this->buildStatusWhereClause($conn); $where[] = $this->buildPriorityWhereClause($conn); $where[] = $this->buildAuthorWhereClause($conn); $where[] = $this->buildOwnerWhereClause($conn); $where[] = $this->buildSubscriberWhereClause($conn); $where[] = $this->buildProjectWhereClause($conn); $where[] = $this->buildXProjectWhereClause($conn); $where[] = $this->buildFullTextWhereClause($conn); $where = array_filter($where); if ($where) { $where = 'WHERE (' . implode(') AND (', $where) . ')'; } else { $where = ''; } $join = array(); $join[] = $this->buildProjectJoinClause($conn); $join[] = $this->buildXProjectJoinClause($conn); $join[] = $this->buildSubscriberJoinClause($conn); $join = array_filter($join); if ($join) { $join = implode(' ', $join); } else { $join = ''; } $having = ''; $count = ''; $group = ''; if (count($this->projectPHIDs) > 1) { // If we're searching for more than one project: // - We'll get multiple rows for tasks when they join the project table // multiple times. We use GROUP BY to make them distinct again. // - We want to treat the query as an intersection query, not a union // query. We sum the project count and require it be the same as the // number of projects we're searching for. (If 'anyProject' is set, // we do union instead.) $group = 'GROUP BY task.id'; if (!$this->anyProject) { $count = ', COUNT(project.projectPHID) projectCount'; $having = qsprintf($conn, 'HAVING projectCount = %d', count($this->projectPHIDs)); } } $order = $this->buildOrderClause($conn); $offset = (int) nonempty($this->offset, 0); $limit = (int) nonempty($this->limit, self::DEFAULT_PAGE_SIZE); if ($this->groupBy == self::GROUP_PROJECT) { $limit = PHP_INT_MAX; $offset = 0; } $data = queryfx_all($conn, 'SELECT %Q * %Q FROM %T task %Q %Q %Q %Q %Q LIMIT %d, %d', $calc, $count, $task_dao->getTableName(), $join, $where, $group, $having, $order, $offset, $limit); if ($this->calculateRows) { $count = queryfx_one($conn, 'SELECT FOUND_ROWS() N'); $this->rowCount = $count['N']; } else { $this->rowCount = null; } $tasks = $task_dao->loadAllFromArray($data); if ($this->groupBy == self::GROUP_PROJECT) { $tasks = $this->applyGroupByProject($tasks); } return $tasks; }
private function buildCardResponse(ManiphestTask $task) { $controller = $this->getController(); $request = $controller->getRequest(); $viewer = $request->getViewer(); $column_phid = $request->getStr('columnPHID'); $order = $request->getStr('order'); $column = id(new PhabricatorProjectColumnQuery())->setViewer($viewer)->withPHIDs(array($column_phid))->executeOne(); if (!$column) { return new Aphront404Response(); } // If the workboard's project has been removed from the card's project // list, we are going to remove it from the board completely. $project_map = array_fuse($task->getProjectPHIDs()); $remove_card = empty($project_map[$column->getProjectPHID()]); $positions = id(new PhabricatorProjectColumnPositionQuery())->setViewer($viewer)->withColumns(array($column))->execute(); $task_phids = mpull($positions, 'getObjectPHID'); $column_tasks = id(new ManiphestTaskQuery())->setViewer($viewer)->withPHIDs($task_phids)->execute(); if ($order == PhabricatorProjectColumn::ORDER_NATURAL) { // TODO: This is a little bit awkward, because PHP and JS use // slightly different sort order parameters to achieve the same // effect. It would be good to unify this a bit at some point. $sort_map = array(); foreach ($positions as $position) { $sort_map[$position->getObjectPHID()] = array(-$position->getSequence(), $position->getID()); } } else { $sort_map = mpull($column_tasks, 'getPrioritySortVector', 'getPHID'); } $data = array('removeFromBoard' => $remove_card, 'sortMap' => $sort_map); // TODO: This should just use HandlePool once we get through the EditEngine // transition. $owner = null; if ($task->getOwnerPHID()) { $owner = id(new PhabricatorHandleQuery())->setViewer($viewer)->withPHIDs(array($task->getOwnerPHID()))->executeOne(); } $tasks = id(new ProjectBoardTaskCard())->setViewer($viewer)->setTask($task)->setOwner($owner)->setCanEdit(true)->getItem(); $payload = array('tasks' => $tasks, 'data' => $data); return id(new AphrontAjaxResponse())->setContent($payload); }
public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $response_type = $request->getStr('responseType', 'task'); $order = $request->getStr('order', PhabricatorProjectColumn::DEFAULT_ORDER); $can_edit_assign = $this->hasApplicationCapability(ManiphestEditAssignCapability::CAPABILITY); $can_edit_policies = $this->hasApplicationCapability(ManiphestEditPoliciesCapability::CAPABILITY); $can_edit_priority = $this->hasApplicationCapability(ManiphestEditPriorityCapability::CAPABILITY); $can_edit_projects = $this->hasApplicationCapability(ManiphestEditProjectsCapability::CAPABILITY); $can_edit_status = $this->hasApplicationCapability(ManiphestEditStatusCapability::CAPABILITY); $parent_task = null; $template_id = null; if ($this->id) { $task = id(new ManiphestTaskQuery())->setViewer($user)->requireCapabilities(array(PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT))->withIDs(array($this->id))->executeOne(); if (!$task) { return new Aphront404Response(); } } else { $task = ManiphestTask::initializeNewTask($user); // We currently do not allow you to set the task status when creating // a new task, although now that statuses are custom it might make // sense. $can_edit_status = false; // These allow task creation with defaults. if (!$request->isFormPost()) { $task->setTitle($request->getStr('title')); if ($can_edit_projects) { $projects = $request->getStr('projects'); if ($projects) { $tokens = $request->getStrList('projects'); $type_project = PhabricatorProjectProjectPHIDType::TYPECONST; foreach ($tokens as $key => $token) { if (phid_get_type($token) == $type_project) { // If this is formatted like a PHID, leave it as-is. continue; } if (preg_match('/^#/', $token)) { // If this already has a "#", leave it as-is. continue; } // Add a "#" prefix. $tokens[$key] = '#' . $token; } $default_projects = id(new PhabricatorObjectQuery())->setViewer($user)->withNames($tokens)->execute(); $default_projects = mpull($default_projects, 'getPHID'); if ($default_projects) { $task->attachProjectPHIDs($default_projects); } } } if ($can_edit_priority) { $priority = $request->getInt('priority'); if ($priority !== null) { $priority_map = ManiphestTaskPriority::getTaskPriorityMap(); if (isset($priority_map[$priority])) { $task->setPriority($priority); } } } $task->setDescription($request->getStr('description')); if ($can_edit_assign) { $assign = $request->getStr('assign'); if (strlen($assign)) { $assign_user = id(new PhabricatorPeopleQuery())->setViewer($user)->withUsernames(array($assign))->executeOne(); if (!$assign_user) { $assign_user = id(new PhabricatorPeopleQuery())->setViewer($user)->withPHIDs(array($assign))->executeOne(); } if ($assign_user) { $task->setOwnerPHID($assign_user->getPHID()); } } } } $template_id = $request->getInt('template'); // You can only have a parent task if you're creating a new task. $parent_id = $request->getInt('parent'); if ($parent_id) { $parent_task = id(new ManiphestTaskQuery())->setViewer($user)->withIDs(array($parent_id))->executeOne(); if (!$template_id) { $template_id = $parent_id; } } } $errors = array(); $e_title = true; $field_list = PhabricatorCustomField::getObjectFields($task, PhabricatorCustomField::ROLE_EDIT); $field_list->setViewer($user); $field_list->readFieldsFromStorage($task); $aux_fields = $field_list->getFields(); if ($request->isFormPost()) { $changes = array(); $new_title = $request->getStr('title'); $new_desc = $request->getStr('description'); $new_status = $request->getStr('status'); if (!$task->getID()) { $workflow = 'create'; } else { $workflow = ''; } $changes[ManiphestTransaction::TYPE_TITLE] = $new_title; $changes[ManiphestTransaction::TYPE_DESCRIPTION] = $new_desc; if ($can_edit_status) { $changes[ManiphestTransaction::TYPE_STATUS] = $new_status; } else { if (!$task->getID()) { // Create an initial status transaction for the burndown chart. // TODO: We can probably remove this once Facts comes online. $changes[ManiphestTransaction::TYPE_STATUS] = $task->getStatus(); } } $owner_tokenizer = $request->getArr('assigned_to'); $owner_phid = reset($owner_tokenizer); if (!strlen($new_title)) { $e_title = pht('Required'); $errors[] = pht('Title is required.'); } $old_values = array(); foreach ($aux_fields as $aux_arr_key => $aux_field) { // TODO: This should be buildFieldTransactionsFromRequest() once we // switch to ApplicationTransactions properly. $aux_old_value = $aux_field->getOldValueForApplicationTransactions(); $aux_field->readValueFromRequest($request); $aux_new_value = $aux_field->getNewValueForApplicationTransactions(); // TODO: We're faking a call to the ApplicaitonTransaction validation // logic here. We need valid objects to pass, but they aren't used // in a meaningful way. For now, build User objects. Once the Maniphest // objects exist, this will switch over automatically. This is a big // hack but shouldn't be long for this world. $placeholder_editor = new PhabricatorUserProfileEditor(); $field_errors = $aux_field->validateApplicationTransactions($placeholder_editor, PhabricatorTransactions::TYPE_CUSTOMFIELD, array(id(new ManiphestTransaction())->setOldValue($aux_old_value)->setNewValue($aux_new_value))); foreach ($field_errors as $error) { $errors[] = $error->getMessage(); } $old_values[$aux_field->getFieldKey()] = $aux_old_value; } if ($errors) { $task->setTitle($new_title); $task->setDescription($new_desc); $task->setPriority($request->getInt('priority')); $task->setOwnerPHID($owner_phid); $task->setCCPHIDs($request->getArr('cc')); $task->attachProjectPHIDs($request->getArr('projects')); } else { if ($can_edit_priority) { $changes[ManiphestTransaction::TYPE_PRIORITY] = $request->getInt('priority'); } if ($can_edit_assign) { $changes[ManiphestTransaction::TYPE_OWNER] = $owner_phid; } $changes[ManiphestTransaction::TYPE_CCS] = $request->getArr('cc'); if ($can_edit_projects) { $projects = $request->getArr('projects'); $changes[ManiphestTransaction::TYPE_PROJECTS] = $projects; $column_phid = $request->getStr('columnPHID'); // allow for putting a task in a project column at creation -only- if (!$task->getID() && $column_phid && $projects) { $column = id(new PhabricatorProjectColumnQuery())->setViewer($user)->withProjectPHIDs($projects)->withPHIDs(array($column_phid))->executeOne(); if ($column) { $changes[ManiphestTransaction::TYPE_PROJECT_COLUMN] = array('new' => array('projectPHID' => $column->getProjectPHID(), 'columnPHIDs' => array($column_phid)), 'old' => array('projectPHID' => $column->getProjectPHID(), 'columnPHIDs' => array())); } } } if ($can_edit_policies) { $changes[PhabricatorTransactions::TYPE_VIEW_POLICY] = $request->getStr('viewPolicy'); $changes[PhabricatorTransactions::TYPE_EDIT_POLICY] = $request->getStr('editPolicy'); } $template = new ManiphestTransaction(); $transactions = array(); foreach ($changes as $type => $value) { $transaction = clone $template; $transaction->setTransactionType($type); if ($type == ManiphestTransaction::TYPE_PROJECT_COLUMN) { $transaction->setNewValue($value['new']); $transaction->setOldValue($value['old']); } else { if ($type == ManiphestTransaction::TYPE_PROJECTS) { // TODO: Gross. $project_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST; $transaction->setTransactionType(PhabricatorTransactions::TYPE_EDGE)->setMetadataValue('edge:type', $project_type)->setNewValue(array('=' => array_fuse($value))); } else { $transaction->setNewValue($value); } } $transactions[] = $transaction; } if ($aux_fields) { foreach ($aux_fields as $aux_field) { $transaction = clone $template; $transaction->setTransactionType(PhabricatorTransactions::TYPE_CUSTOMFIELD); $aux_key = $aux_field->getFieldKey(); $transaction->setMetadataValue('customfield:key', $aux_key); $old = idx($old_values, $aux_key); $new = $aux_field->getNewValueForApplicationTransactions(); $transaction->setOldValue($old); $transaction->setNewValue($new); $transactions[] = $transaction; } } if ($transactions) { $is_new = !$task->getID(); $event = new PhabricatorEvent(PhabricatorEventType::TYPE_MANIPHEST_WILLEDITTASK, array('task' => $task, 'new' => $is_new, 'transactions' => $transactions)); $event->setUser($user); $event->setAphrontRequest($request); PhutilEventEngine::dispatchEvent($event); $task = $event->getValue('task'); $transactions = $event->getValue('transactions'); $editor = id(new ManiphestTransactionEditor())->setActor($user)->setContentSourceFromRequest($request)->setContinueOnNoEffect(true)->applyTransactions($task, $transactions); $event = new PhabricatorEvent(PhabricatorEventType::TYPE_MANIPHEST_DIDEDITTASK, array('task' => $task, 'new' => $is_new, 'transactions' => $transactions)); $event->setUser($user); $event->setAphrontRequest($request); PhutilEventEngine::dispatchEvent($event); } if ($parent_task) { // TODO: This should be transactional now. id(new PhabricatorEdgeEditor())->addEdge($parent_task->getPHID(), PhabricatorEdgeConfig::TYPE_TASK_DEPENDS_ON_TASK, $task->getPHID())->save(); $workflow = $parent_task->getID(); } if ($request->isAjax()) { switch ($response_type) { case 'card': $owner = null; if ($task->getOwnerPHID()) { $owner = id(new PhabricatorHandleQuery())->setViewer($user)->withPHIDs(array($task->getOwnerPHID()))->executeOne(); } $tasks = id(new ProjectBoardTaskCard())->setViewer($user)->setTask($task)->setOwner($owner)->setCanEdit(true)->getItem(); $column = id(new PhabricatorProjectColumnQuery())->setViewer($user)->withPHIDs(array($request->getStr('columnPHID')))->executeOne(); if (!$column) { return new Aphront404Response(); } $positions = id(new PhabricatorProjectColumnPositionQuery())->setViewer($user)->withColumns(array($column))->execute(); $task_phids = mpull($positions, 'getObjectPHID'); $column_tasks = id(new ManiphestTaskQuery())->setViewer($user)->withPHIDs($task_phids)->execute(); if ($order == PhabricatorProjectColumn::ORDER_NATURAL) { // TODO: This is a little bit awkward, because PHP and JS use // slightly different sort order parameters to achieve the same // effect. It would be unify this a bit at some point. $sort_map = array(); foreach ($positions as $position) { $sort_map[$position->getObjectPHID()] = array(-$position->getSequence(), $position->getID()); } } else { $sort_map = mpull($column_tasks, 'getPrioritySortVector', 'getPHID'); } $data = array('sortMap' => $sort_map); break; case 'task': default: $tasks = $this->renderSingleTask($task); $data = array(); break; } return id(new AphrontAjaxResponse())->setContent(array('tasks' => $tasks, 'data' => $data)); } $redirect_uri = '/T' . $task->getID(); if ($workflow) { $redirect_uri .= '?workflow=' . $workflow; } return id(new AphrontRedirectResponse())->setURI($redirect_uri); } } else { if (!$task->getID()) { $task->setCCPHIDs(array($user->getPHID())); if ($template_id) { $template_task = id(new ManiphestTaskQuery())->setViewer($user)->withIDs(array($template_id))->executeOne(); if ($template_task) { $cc_phids = array_unique(array_merge($template_task->getCCPHIDs(), array($user->getPHID()))); $task->setCCPHIDs($cc_phids); $task->attachProjectPHIDs($template_task->getProjectPHIDs()); $task->setOwnerPHID($template_task->getOwnerPHID()); $task->setPriority($template_task->getPriority()); $task->setViewPolicy($template_task->getViewPolicy()); $task->setEditPolicy($template_task->getEditPolicy()); $template_fields = PhabricatorCustomField::getObjectFields($template_task, PhabricatorCustomField::ROLE_EDIT); $fields = $template_fields->getFields(); foreach ($fields as $key => $field) { if (!$field->shouldCopyWhenCreatingSimilarTask()) { unset($fields[$key]); } if (empty($aux_fields[$key])) { unset($fields[$key]); } } if ($fields) { id(new PhabricatorCustomFieldList($fields))->setViewer($user)->readFieldsFromStorage($template_task); foreach ($fields as $key => $field) { $aux_fields[$key]->setValueFromStorage($field->getValueForStorage()); } } } } } } $phids = array_merge(array($task->getOwnerPHID()), $task->getCCPHIDs(), $task->getProjectPHIDs()); if ($parent_task) { $phids[] = $parent_task->getPHID(); } $phids = array_filter($phids); $phids = array_unique($phids); $handles = $this->loadViewerHandles($phids); $error_view = null; if ($errors) { $error_view = new AphrontErrorView(); $error_view->setErrors($errors); } $priority_map = ManiphestTaskPriority::getTaskPriorityMap(); if ($task->getOwnerPHID()) { $assigned_value = array($handles[$task->getOwnerPHID()]); } else { $assigned_value = array(); } if ($task->getCCPHIDs()) { $cc_value = array_select_keys($handles, $task->getCCPHIDs()); } else { $cc_value = array(); } if ($task->getProjectPHIDs()) { $projects_value = array_select_keys($handles, $task->getProjectPHIDs()); } else { $projects_value = array(); } $cancel_id = nonempty($task->getID(), $template_id); if ($cancel_id) { $cancel_uri = '/T' . $cancel_id; } else { $cancel_uri = '/maniphest/'; } if ($task->getID()) { $button_name = pht('Save Task'); $header_name = pht('Edit Task'); } else { if ($parent_task) { $cancel_uri = '/T' . $parent_task->getID(); $button_name = pht('Create Task'); $header_name = pht('Create New Subtask'); } else { $button_name = pht('Create Task'); $header_name = pht('Create New Task'); } } require_celerity_resource('maniphest-task-edit-css'); $project_tokenizer_id = celerity_generate_unique_node_id(); $form = new AphrontFormView(); $form->setUser($user)->addHiddenInput('template', $template_id)->addHiddenInput('responseType', $response_type)->addHiddenInput('order', $order)->addHiddenInput('ungrippable', $request->getStr('ungrippable'))->addHiddenInput('columnPHID', $request->getStr('columnPHID')); if ($parent_task) { $form->appendChild(id(new AphrontFormStaticControl())->setLabel(pht('Parent Task'))->setValue($handles[$parent_task->getPHID()]->getFullName()))->addHiddenInput('parent', $parent_task->getID()); } $form->appendChild(id(new AphrontFormTextAreaControl())->setLabel(pht('Title'))->setName('title')->setError($e_title)->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_SHORT)->setValue($task->getTitle())); if ($can_edit_status) { // See T4819. $status_map = ManiphestTaskStatus::getTaskStatusMap(); $dup_status = ManiphestTaskStatus::getDuplicateStatus(); if ($task->getStatus() != $dup_status) { unset($status_map[$dup_status]); } $form->appendChild(id(new AphrontFormSelectControl())->setLabel(pht('Status'))->setName('status')->setValue($task->getStatus())->setOptions($status_map)); } $policies = id(new PhabricatorPolicyQuery())->setViewer($user)->setObject($task)->execute(); if ($can_edit_assign) { $form->appendChild(id(new AphrontFormTokenizerControl())->setLabel(pht('Assigned To'))->setName('assigned_to')->setValue($assigned_value)->setUser($user)->setDatasource(new PhabricatorPeopleDatasource())->setLimit(1)); } $form->appendChild(id(new AphrontFormTokenizerControl())->setLabel(pht('CC'))->setName('cc')->setValue($cc_value)->setUser($user)->setDatasource(new PhabricatorMetaMTAMailableDatasource())); if ($can_edit_priority) { $form->appendChild(id(new AphrontFormSelectControl())->setLabel(pht('Priority'))->setName('priority')->setOptions($priority_map)->setValue($task->getPriority())); } if ($can_edit_policies) { $form->appendChild(id(new AphrontFormPolicyControl())->setUser($user)->setCapability(PhabricatorPolicyCapability::CAN_VIEW)->setPolicyObject($task)->setPolicies($policies)->setName('viewPolicy'))->appendChild(id(new AphrontFormPolicyControl())->setUser($user)->setCapability(PhabricatorPolicyCapability::CAN_EDIT)->setPolicyObject($task)->setPolicies($policies)->setName('editPolicy')); } if ($can_edit_projects) { $form->appendChild(id(new AphrontFormTokenizerControl())->setLabel(pht('Projects'))->setName('projects')->setValue($projects_value)->setID($project_tokenizer_id)->setCaption(javelin_tag('a', array('href' => '/project/create/', 'mustcapture' => true, 'sigil' => 'project-create'), pht('Create New Project')))->setDatasource(new PhabricatorProjectDatasource())); } $field_list->appendFieldsToForm($form); require_celerity_resource('aphront-error-view-css'); Javelin::initBehavior('project-create', array('tokenizerID' => $project_tokenizer_id)); $description_control = new PhabricatorRemarkupControl(); // "Upsell" creating tasks via email in create flows if the instance is // configured for this awesomeness. $email_create = PhabricatorEnv::getEnvConfig('metamta.maniphest.public-create-email'); if (!$task->getID() && $email_create) { $email_hint = pht('You can also create tasks by sending an email to: %s', phutil_tag('tt', array(), $email_create)); $description_control->setCaption($email_hint); } $description_control->setLabel(pht('Description'))->setName('description')->setID('description-textarea')->setValue($task->getDescription())->setUser($user); $form->appendChild($description_control); if ($request->isAjax()) { $dialog = id(new AphrontDialogView())->setUser($user)->setWidth(AphrontDialogView::WIDTH_FULL)->setTitle($header_name)->appendChild(array($error_view, $form->buildLayoutView()))->addCancelButton($cancel_uri)->addSubmitButton($button_name); return id(new AphrontDialogResponse())->setDialog($dialog); } $form->appendChild(id(new AphrontFormSubmitControl())->addCancelButton($cancel_uri)->setValue($button_name)); $form_box = id(new PHUIObjectBoxView())->setHeaderText($header_name)->setFormErrors($errors)->setForm($form); $preview = id(new PHUIRemarkupPreviewPanel())->setHeader(pht('Description Preview'))->setControlID('description-textarea')->setPreviewURI($this->getApplicationURI('task/descriptionpreview/')); if ($task->getID()) { $page_objects = array($task->getPHID()); } else { $page_objects = array(); } $crumbs = $this->buildApplicationCrumbs(); if ($task->getID()) { $crumbs->addTextCrumb('T' . $task->getID(), '/T' . $task->getID()); } $crumbs->addTextCrumb($header_name); return $this->buildApplicationPage(array($crumbs, $form_box, $preview), array('title' => $header_name, 'pageObjects' => $page_objects)); }
private function buildTransactions($actions, ManiphestTask $task) { $value_map = array(); $type_map = array('add_comment' => PhabricatorTransactions::TYPE_COMMENT, 'assign' => ManiphestTransaction::TYPE_OWNER, 'status' => ManiphestTransaction::TYPE_STATUS, 'priority' => ManiphestTransaction::TYPE_PRIORITY, 'add_project' => ManiphestTransaction::TYPE_PROJECTS, 'remove_project' => ManiphestTransaction::TYPE_PROJECTS, 'add_ccs' => ManiphestTransaction::TYPE_CCS, 'remove_ccs' => ManiphestTransaction::TYPE_CCS); $edge_edit_types = array('add_project' => true, 'remove_project' => true, 'add_ccs' => true, 'remove_ccs' => true); $xactions = array(); foreach ($actions as $action) { if (empty($type_map[$action['action']])) { throw new Exception("Unknown batch edit action '{$action}'!"); } $type = $type_map[$action['action']]; // Figure out the current value, possibly after modifications by other // batch actions of the same type. For example, if the user chooses to // "Add Comment" twice, we should add both comments. More notably, if the // user chooses "Remove Project..." and also "Add Project...", we should // avoid restoring the removed project in the second transaction. if (array_key_exists($type, $value_map)) { $current = $value_map[$type]; } else { switch ($type) { case PhabricatorTransactions::TYPE_COMMENT: $current = null; break; case ManiphestTransaction::TYPE_OWNER: $current = $task->getOwnerPHID(); break; case ManiphestTransaction::TYPE_STATUS: $current = $task->getStatus(); break; case ManiphestTransaction::TYPE_PRIORITY: $current = $task->getPriority(); break; case ManiphestTransaction::TYPE_PROJECTS: $current = $task->getProjectPHIDs(); break; case ManiphestTransaction::TYPE_CCS: $current = $task->getCCPHIDs(); break; } } // Check if the value is meaningful / provided, and normalize it if // necessary. This discards, e.g., empty comments and empty owner // changes. $value = $action['value']; switch ($type) { case PhabricatorTransactions::TYPE_COMMENT: if (!strlen($value)) { continue 2; } break; case ManiphestTransaction::TYPE_OWNER: if (empty($value)) { continue 2; } $value = head($value); if ($value === ManiphestTaskOwner::OWNER_UP_FOR_GRABS) { $value = null; } break; case ManiphestTransaction::TYPE_PROJECTS: if (empty($value)) { continue 2; } break; case ManiphestTransaction::TYPE_CCS: if (empty($value)) { continue 2; } break; } // If the edit doesn't change anything, go to the next action. This // check is only valid for changes like "owner", "status", etc, not // for edge edits, because we should still apply an edit like // "Remove Projects: A, B" to a task with projects "A, B". if (empty($edge_edit_types[$action['action']])) { if ($value == $current) { continue; } } // Apply the value change; for most edits this is just replacement, but // some need to merge the current and edited values (add/remove project). switch ($type) { case PhabricatorTransactions::TYPE_COMMENT: if (strlen($current)) { $value = $current . "\n\n" . $value; } break; case ManiphestTransaction::TYPE_PROJECTS: case ManiphestTransaction::TYPE_CCS: $remove_actions = array('remove_project' => true, 'remove_ccs' => true); $is_remove = isset($remove_actions[$action['action']]); $current = array_fill_keys($current, true); $value = array_fill_keys($value, true); $new = $current; $did_something = false; if ($is_remove) { foreach ($value as $phid => $ignored) { if (isset($new[$phid])) { unset($new[$phid]); $did_something = true; } } } else { foreach ($value as $phid => $ignored) { if (empty($new[$phid])) { $new[$phid] = true; $did_something = true; } } } if (!$did_something) { continue 2; } $value = array_keys($new); break; } $value_map[$type] = $value; } $template = new ManiphestTransaction(); foreach ($value_map as $type => $value) { $xaction = clone $template; $xaction->setTransactionType($type); switch ($type) { case PhabricatorTransactions::TYPE_COMMENT: $xaction->attachComment(id(new ManiphestTransactionComment())->setContent($value)); break; case ManiphestTransaction::TYPE_PROJECTS: // TODO: Clean this mess up. $project_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST; $xaction->setTransactionType(PhabricatorTransactions::TYPE_EDGE)->setMetadataValue('edge:type', $project_type)->setNewValue(array('=' => array_fuse($value))); break; default: $xaction->setNewValue($value); break; } $xactions[] = $xaction; } return $xactions; }
protected function loadPage() { $task_dao = new ManiphestTask(); $conn = $task_dao->establishConnection('r'); $where = $this->buildWhereClause($conn); $group_column = ''; switch ($this->groupBy) { case self::GROUP_PROJECT: $group_column = qsprintf($conn, ', projectGroupName.indexedObjectPHID projectGroupPHID'); break; } $rows = queryfx_all($conn, '%Q %Q FROM %T task %Q %Q %Q %Q %Q %Q', $this->buildSelectClause($conn), $group_column, $task_dao->getTableName(), $this->buildJoinClause($conn), $where, $this->buildGroupClause($conn), $this->buildHavingClause($conn), $this->buildOrderClause($conn), $this->buildLimitClause($conn)); switch ($this->groupBy) { case self::GROUP_PROJECT: $data = ipull($rows, null, 'id'); break; default: $data = $rows; break; } $tasks = $task_dao->loadAllFromArray($data); switch ($this->groupBy) { case self::GROUP_PROJECT: $results = array(); foreach ($rows as $row) { $task = clone $tasks[$row['id']]; $task->attachGroupByProjectPHID($row['projectGroupPHID']); $results[] = $task; } $tasks = $results; break; } return $tasks; }
<?php // NOTE: If you need to make any significant updates to this to deal with // future changes to objects, it's probably better to just wipe the whole // migration. This feature doesn't see overwhelming amounts of use, and users // who do use it can recreate their queries fairly easily with the new // interface. By the time this needs to be updated, the vast majority of // users who it impacts will likely have migrated their data already. $table = new ManiphestTask(); $conn_w = $table->establishConnection('w'); $search_table = new PhabricatorSearchQuery(); $search_conn_w = $search_table->establishConnection('w'); // See T1812. This is an old status constant from the time of this migration. $old_open_status = 0; echo "Updating saved Maniphest queries...\n"; $rows = new LiskRawMigrationIterator($conn_w, 'maniphest_savedquery'); foreach ($rows as $row) { $id = $row['id']; echo "Updating query {$id}...\n"; $data = queryfx_one($search_conn_w, 'SELECT parameters FROM %T WHERE queryKey = %s', $search_table->getTableName(), $row['queryKey']); if (!$data) { echo "Unable to locate query data.\n"; continue; } $data = json_decode($data['parameters'], true); if (!is_array($data)) { echo "Unable to decode query data.\n"; continue; } if (idx($data, 'view') != 'custom') { echo "Query is not a custom query.\n";
/** * Load all the tasks that have been recently closed. */ private function loadRecentlyClosedTasks() { list($ignored, $window_epoch) = $this->getWindow(); $table = new ManiphestTask(); $xtable = new ManiphestTransaction(); $conn_r = $table->establishConnection('r'); $tasks = queryfx_all($conn_r, 'SELECT t.* FROM %T t JOIN %T x ON x.taskID = t.id WHERE t.status != 0 AND x.oldValue IN (null, %s, %s) AND x.newValue NOT IN (%s, %s) AND t.dateModified >= %d AND x.dateCreated >= %d', $table->getTableName(), $xtable->getTableName(), json_encode((int) ManiphestTaskStatus::STATUS_OPEN), json_encode((string) ManiphestTaskStatus::STATUS_OPEN), json_encode((int) ManiphestTaskStatus::STATUS_OPEN), json_encode((string) ManiphestTaskStatus::STATUS_OPEN), $window_epoch, $window_epoch); return id(new ManiphestTask())->loadAllFromArray($tasks); }
/** * Load all the tasks that have been recently closed. */ private function loadRecentlyClosedTasks() { list($ignored, $window_epoch) = $this->getWindow(); $table = new ManiphestTask(); $xtable = new ManiphestTransaction(); $conn_r = $table->establishConnection('r'); // TODO: Gross. This table is not meant to be queried like this. Build // real stats tables. $open_status_list = array(); foreach (ManiphestTaskStatus::getOpenStatusConstants() as $constant) { $open_status_list[] = json_encode((string) $constant); } $rows = queryfx_all($conn_r, 'SELECT t.id FROM %T t JOIN %T x ON x.objectPHID = t.phid WHERE t.status NOT IN (%Ls) AND x.oldValue IN (null, %Ls) AND x.newValue NOT IN (%Ls) AND t.dateModified >= %d AND x.dateCreated >= %d', $table->getTableName(), $xtable->getTableName(), ManiphestTaskStatus::getOpenStatusConstants(), $open_status_list, $open_status_list, $window_epoch, $window_epoch); if (!$rows) { return array(); } $ids = ipull($rows, 'id'); $query = id(new ManiphestTaskQuery())->setViewer($this->getRequest()->getUser())->withIDs($ids); switch ($this->view) { case 'project': $query->needProjectPHIDs(true); break; } return $query->execute(); }
public function loadHandles() { $types = phid_group_by_type($this->phids); $handles = array(); $external_loaders = PhabricatorEnv::getEnvConfig('phid.external-loaders'); foreach ($types as $type => $phids) { switch ($type) { case PhabricatorPHIDConstants::PHID_TYPE_MAGIC: // Black magic! foreach ($phids as $phid) { $handle = new PhabricatorObjectHandle(); $handle->setPHID($phid); $handle->setType($type); switch ($phid) { case ManiphestTaskOwner::OWNER_UP_FOR_GRABS: $handle->setName('Up For Grabs'); $handle->setFullName('upforgrabs (Up For Grabs)'); $handle->setComplete(true); break; case ManiphestTaskOwner::PROJECT_NO_PROJECT: $handle->setName('No Project'); $handle->setFullName('noproject (No Project)'); $handle->setComplete(true); break; default: $handle->setName('Foul Magicks'); break; } $handles[$phid] = $handle; } break; case PhabricatorPHIDConstants::PHID_TYPE_USER: $object = new PhabricatorUser(); $users = $object->loadAllWhere('phid IN (%Ls)', $phids); $users = mpull($users, null, 'getPHID'); $image_phids = mpull($users, 'getProfileImagePHID'); $image_phids = array_unique(array_filter($image_phids)); $images = array(); if ($image_phids) { $images = id(new PhabricatorFile())->loadAllWhere('phid IN (%Ls)', $image_phids); $images = mpull($images, 'getBestURI', 'getPHID'); } $statuses = id(new PhabricatorUserStatus())->loadCurrentStatuses($phids); foreach ($phids as $phid) { $handle = new PhabricatorObjectHandle(); $handle->setPHID($phid); $handle->setType($type); if (empty($users[$phid])) { $handle->setName('Unknown User'); } else { $user = $users[$phid]; $handle->setName($user->getUsername()); $handle->setURI('/p/' . $user->getUsername() . '/'); $handle->setFullName($user->getUsername() . ' (' . $user->getRealName() . ')'); $handle->setAlternateID($user->getID()); $handle->setComplete(true); if (isset($statuses[$phid])) { $handle->setStatus($statuses[$phid]->getTextStatus()); } $handle->setDisabled($user->getIsDisabled()); $img_uri = idx($images, $user->getProfileImagePHID()); if ($img_uri) { $handle->setImageURI($img_uri); } else { $handle->setImageURI(PhabricatorUser::getDefaultProfileImageURI()); } } $handles[$phid] = $handle; } break; case PhabricatorPHIDConstants::PHID_TYPE_MLST: $object = new PhabricatorMetaMTAMailingList(); $lists = $object->loadAllWhere('phid IN (%Ls)', $phids); $lists = mpull($lists, null, 'getPHID'); foreach ($phids as $phid) { $handle = new PhabricatorObjectHandle(); $handle->setPHID($phid); $handle->setType($type); if (empty($lists[$phid])) { $handle->setName('Unknown Mailing List'); } else { $list = $lists[$phid]; $handle->setName($list->getName()); $handle->setURI($list->getURI()); $handle->setFullName($list->getName()); $handle->setComplete(true); } $handles[$phid] = $handle; } break; case PhabricatorPHIDConstants::PHID_TYPE_DREV: $object = new DifferentialRevision(); $revs = $object->loadAllWhere('phid in (%Ls)', $phids); $revs = mpull($revs, null, 'getPHID'); foreach ($phids as $phid) { $handle = new PhabricatorObjectHandle(); $handle->setPHID($phid); $handle->setType($type); if (empty($revs[$phid])) { $handle->setName('Unknown Revision'); } else { $rev = $revs[$phid]; $handle->setName($rev->getTitle()); $handle->setURI('/D' . $rev->getID()); $handle->setFullName('D' . $rev->getID() . ': ' . $rev->getTitle()); $handle->setComplete(true); $status = $rev->getStatus(); if ($status == ArcanistDifferentialRevisionStatus::CLOSED || $status == ArcanistDifferentialRevisionStatus::ABANDONED) { $closed = PhabricatorObjectHandleStatus::STATUS_CLOSED; $handle->setStatus($closed); } } $handles[$phid] = $handle; } break; case PhabricatorPHIDConstants::PHID_TYPE_CMIT: $object = new PhabricatorRepositoryCommit(); $commits = $object->loadAllWhere('phid in (%Ls)', $phids); $commits = mpull($commits, null, 'getPHID'); $repository_ids = array(); $callsigns = array(); if ($commits) { $repository_ids = mpull($commits, 'getRepositoryID'); $repositories = id(new PhabricatorRepository())->loadAllWhere('id in (%Ld)', array_unique($repository_ids)); $callsigns = mpull($repositories, 'getCallsign'); } foreach ($phids as $phid) { $handle = new PhabricatorObjectHandle(); $handle->setPHID($phid); $handle->setType($type); if (empty($commits[$phid]) || !isset($callsigns[$repository_ids[$phid]])) { $handle->setName('Unknown Commit'); } else { $commit = $commits[$phid]; $callsign = $callsigns[$repository_ids[$phid]]; $repository = $repositories[$repository_ids[$phid]]; $commit_identifier = $commit->getCommitIdentifier(); // In case where the repository for the commit was deleted, // we don't have have info about the repository anymore. if ($repository) { $name = $repository->formatCommitName($commit_identifier); $handle->setName($name); } else { $handle->setName('Commit ' . 'r' . $callsign . $commit_identifier); } $handle->setURI('/r' . $callsign . $commit_identifier); $handle->setFullName('r' . $callsign . $commit_identifier); $handle->setTimestamp($commit->getEpoch()); $handle->setComplete(true); } $handles[$phid] = $handle; } break; case PhabricatorPHIDConstants::PHID_TYPE_TASK: $object = new ManiphestTask(); $tasks = $object->loadAllWhere('phid in (%Ls)', $phids); $tasks = mpull($tasks, null, 'getPHID'); foreach ($phids as $phid) { $handle = new PhabricatorObjectHandle(); $handle->setPHID($phid); $handle->setType($type); if (empty($tasks[$phid])) { $handle->setName('Unknown Revision'); } else { $task = $tasks[$phid]; $handle->setName($task->getTitle()); $handle->setURI('/T' . $task->getID()); $handle->setFullName('T' . $task->getID() . ': ' . $task->getTitle()); $handle->setComplete(true); $handle->setAlternateID($task->getID()); if ($task->getStatus() != ManiphestTaskStatus::STATUS_OPEN) { $closed = PhabricatorObjectHandleStatus::STATUS_CLOSED; $handle->setStatus($closed); } } $handles[$phid] = $handle; } break; case PhabricatorPHIDConstants::PHID_TYPE_FILE: $object = new PhabricatorFile(); $files = $object->loadAllWhere('phid IN (%Ls)', $phids); $files = mpull($files, null, 'getPHID'); foreach ($phids as $phid) { $handle = new PhabricatorObjectHandle(); $handle->setPHID($phid); $handle->setType($type); if (empty($files[$phid])) { $handle->setName('Unknown File'); } else { $file = $files[$phid]; $handle->setName($file->getName()); $handle->setURI($file->getBestURI()); $handle->setComplete(true); } $handles[$phid] = $handle; } break; case PhabricatorPHIDConstants::PHID_TYPE_PROJ: $object = new PhabricatorProject(); $projects = $object->loadAllWhere('phid IN (%Ls)', $phids); $projects = mpull($projects, null, 'getPHID'); foreach ($phids as $phid) { $handle = new PhabricatorObjectHandle(); $handle->setPHID($phid); $handle->setType($type); if (empty($projects[$phid])) { $handle->setName('Unknown Project'); } else { $project = $projects[$phid]; $handle->setName($project->getName()); $handle->setURI('/project/view/' . $project->getID() . '/'); $handle->setComplete(true); } $handles[$phid] = $handle; } break; case PhabricatorPHIDConstants::PHID_TYPE_REPO: $object = new PhabricatorRepository(); $repositories = $object->loadAllWhere('phid in (%Ls)', $phids); $repositories = mpull($repositories, null, 'getPHID'); foreach ($phids as $phid) { $handle = new PhabricatorObjectHandle(); $handle->setPHID($phid); $handle->setType($type); if (empty($repositories[$phid])) { $handle->setName('Unknown Repository'); } else { $repository = $repositories[$phid]; $handle->setName($repository->getCallsign()); $handle->setURI('/diffusion/' . $repository->getCallsign() . '/'); $handle->setComplete(true); } $handles[$phid] = $handle; } break; case PhabricatorPHIDConstants::PHID_TYPE_OPKG: $object = new PhabricatorOwnersPackage(); $packages = $object->loadAllWhere('phid in (%Ls)', $phids); $packages = mpull($packages, null, 'getPHID'); foreach ($phids as $phid) { $handle = new PhabricatorObjectHandle(); $handle->setPHID($phid); $handle->setType($type); if (empty($packages[$phid])) { $handle->setName('Unknown Package'); } else { $package = $packages[$phid]; $handle->setName($package->getName()); $handle->setURI('/owners/package/' . $package->getID() . '/'); $handle->setComplete(true); } $handles[$phid] = $handle; } break; case PhabricatorPHIDConstants::PHID_TYPE_APRJ: $project_dao = new PhabricatorRepositoryArcanistProject(); $projects = $project_dao->loadAllWhere('phid IN (%Ls)', $phids); $projects = mpull($projects, null, 'getPHID'); foreach ($phids as $phid) { $handle = new PhabricatorObjectHandle(); $handle->setPHID($phid); $handle->setType($type); if (empty($projects[$phid])) { $handle->setName('Unknown Arcanist Project'); } else { $project = $projects[$phid]; $handle->setName($project->getName()); $handle->setComplete(true); } $handles[$phid] = $handle; } break; case PhabricatorPHIDConstants::PHID_TYPE_WIKI: $document_dao = new PhrictionDocument(); $content_dao = new PhrictionContent(); $conn = $document_dao->establishConnection('r'); $documents = queryfx_all($conn, 'SELECT * FROM %T document JOIN %T content ON document.contentID = content.id WHERE document.phid IN (%Ls)', $document_dao->getTableName(), $content_dao->getTableName(), $phids); $documents = ipull($documents, null, 'phid'); foreach ($phids as $phid) { $handle = new PhabricatorObjectHandle(); $handle->setPHID($phid); $handle->setType($type); if (empty($documents[$phid])) { $handle->setName('Unknown Document'); } else { $info = $documents[$phid]; $handle->setName($info['title']); $handle->setURI(PhrictionDocument::getSlugURI($info['slug'])); $handle->setComplete(true); } $handles[$phid] = $handle; } break; default: $loader = null; if (isset($external_loaders[$type])) { $loader = $external_loaders[$type]; } else { if (isset($external_loaders['*'])) { $loader = $external_loaders['*']; } } if ($loader) { $object = newv($loader, array()); $handles += $object->loadHandles($phids); break; } foreach ($phids as $phid) { $handle = new PhabricatorObjectHandle(); $handle->setType($type); $handle->setPHID($phid); $handle->setName('Unknown Object'); $handle->setFullName('An Unknown Object'); $handles[$phid] = $handle; } break; } } return $handles; }
public static function indexTask(ManiphestTask $task) { $doc = new PhabricatorSearchAbstractDocument(); $doc->setPHID($task->getPHID()); $doc->setDocumentType(PhabricatorPHIDConstants::PHID_TYPE_TASK); $doc->setDocumentTitle($task->getTitle()); $doc->setDocumentCreated($task->getDateCreated()); $doc->setDocumentModified($task->getDateModified()); $doc->addField(PhabricatorSearchField::FIELD_BODY, $task->getDescription()); $doc->addRelationship(PhabricatorSearchRelationship::RELATIONSHIP_AUTHOR, $task->getAuthorPHID(), PhabricatorPHIDConstants::PHID_TYPE_USER, $task->getDateCreated()); if ($task->getStatus() == ManiphestTaskStatus::STATUS_OPEN) { $doc->addRelationship(PhabricatorSearchRelationship::RELATIONSHIP_OPEN, $task->getPHID(), PhabricatorPHIDConstants::PHID_TYPE_TASK, time()); } $transactions = id(new ManiphestTransaction())->loadAllWhere('taskID = %d', $task->getID()); $current_ccs = $task->getCCPHIDs(); $touches = array(); $owner = null; $ccs = array(); foreach ($transactions as $transaction) { if ($transaction->hasComments()) { $doc->addField(PhabricatorSearchField::FIELD_COMMENT, $transaction->getComments()); } $author = $transaction->getAuthorPHID(); // Record the most recent time they touched this object. $touches[$author] = $transaction->getDateCreated(); switch ($transaction->getTransactionType()) { case ManiphestTransactionType::TYPE_OWNER: $owner = $transaction; break; case ManiphestTransactionType::TYPE_CCS: // For users who are still CC'd, record the first time they were // added to CC. foreach ($transaction->getNewValue() as $added_cc) { if (in_array($added_cc, $current_ccs)) { if (empty($ccs[$added_cc])) { $ccs[$added_cc] = $transaction->getDateCreated(); } } } break; } } foreach ($task->getProjectPHIDs() as $phid) { $doc->addRelationship(PhabricatorSearchRelationship::RELATIONSHIP_PROJECT, $phid, PhabricatorPHIDConstants::PHID_TYPE_PROJ, $task->getDateModified()); // Bogus. } if ($owner && $owner->getNewValue()) { $doc->addRelationship(PhabricatorSearchRelationship::RELATIONSHIP_OWNER, $owner->getNewValue(), PhabricatorPHIDConstants::PHID_TYPE_USER, $owner->getDateCreated()); } else { $doc->addRelationship(PhabricatorSearchRelationship::RELATIONSHIP_OWNER, ManiphestTaskOwner::OWNER_UP_FOR_GRABS, PhabricatorPHIDConstants::PHID_TYPE_MAGIC, $owner ? $owner->getDateCreated() : $task->getDateCreated()); } foreach ($touches as $touch => $time) { $doc->addRelationship(PhabricatorSearchRelationship::RELATIONSHIP_TOUCH, $touch, PhabricatorPHIDConstants::PHID_TYPE_USER, $time); } // We need to load handles here since non-users may subscribe (mailing // lists, e.g.) $handles = id(new PhabricatorObjectHandleData(array_keys($ccs)))->loadHandles(); foreach ($ccs as $cc => $time) { $doc->addRelationship(PhabricatorSearchRelationship::RELATIONSHIP_SUBSCRIBER, $handles[$cc]->getPHID(), $handles[$cc]->getType(), $time); } self::reindexAbstractDocument($doc); }
public function testObjectPolicyRuleSubscribers() { $author = $this->generateNewTestUser(); $rule = new PhabricatorSubscriptionsSubscribersPolicyRule(); $task = ManiphestTask::initializeNewTask($author); $task->setViewPolicy($rule->getObjectPolicyFullKey()); $task->save(); $this->assertFalse(PhabricatorPolicyFilter::hasCapability($author, $task, PhabricatorPolicyCapability::CAN_VIEW)); id(new PhabricatorSubscriptionsEditor())->setActor($author)->setObject($task)->subscribeExplicit(array($author->getPHID()))->save(); $this->assertTrue(PhabricatorPolicyFilter::hasCapability($author, $task, PhabricatorPolicyCapability::CAN_VIEW)); }
/** * Get priorities for moving a task before or after another task. */ public static function getAdjacentSubpriority(ManiphestTask $dst, $is_after, $allow_recursion = true) { $query = id(new ManiphestTaskQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->setOrder(ManiphestTaskQuery::ORDER_PRIORITY)->withPriorities(array($dst->getPriority()))->setLimit(1); if ($is_after) { $query->setAfterID($dst->getID()); } else { $query->setBeforeID($dst->getID()); } $adjacent = $query->executeOne(); $base = $dst->getSubpriority(); $step = (double) (2 << 32); // If we find an adjacent task, we average the two subpriorities and // return the result. if ($adjacent) { $epsilon = 0.01; // If the adjacent task has a subpriority that is identical or very // close to the task we're looking at, we're going to move it and all // tasks with the same subpriority a little farther down the subpriority // scale. if ($allow_recursion && abs($adjacent->getSubpriority() - $base) < $epsilon) { $conn_w = $adjacent->establishConnection('w'); $min = $adjacent->getSubpriority() - $epsilon; $max = $adjacent->getSubpriority() + $epsilon; // Get all of the tasks with the similar subpriorities to the adjacent // task, including the adjacent task itself. $query = id(new ManiphestTaskQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withPriorities(array($adjacent->getPriority()))->withSubpriorityBetween($min, $max); if (!$is_after) { $query->setOrderVector(array('-priority', '-subpriority', '-id')); } else { $query->setOrderVector(array('priority', 'subpriority', 'id')); } $shift_all = $query->execute(); $shift_last = last($shift_all); // Select the most extreme subpriority in the result set as the // base value. $shift_base = head($shift_all)->getSubpriority(); // Find the subpriority before or after the task at the end of the // block. list($shift_pri, $shift_sub) = self::getAdjacentSubpriority($shift_last, $is_after, $allow_recursion = false); $delta = $shift_sub - $shift_base; $count = count($shift_all); $shift = array(); $cursor = 1; foreach ($shift_all as $shift_task) { $shift_target = $shift_base + $cursor / $count * $delta; $cursor++; queryfx($conn_w, 'UPDATE %T SET subpriority = %f WHERE id = %d', $adjacent->getTableName(), $shift_target, $shift_task->getID()); // If we're shifting the adjacent task, update it. if ($shift_task->getID() == $adjacent->getID()) { $adjacent->setSubpriority($shift_target); } // If we're shifting the original target task, update the base // subpriority. if ($shift_task->getID() == $dst->getID()) { $base = $shift_target; } } } $sub = ($adjacent->getSubpriority() + $base) / 2; } else { // Otherwise, we take a step away from the target's subpriority and // use that. if ($is_after) { $sub = $base - $step; } else { $sub = $base + $step; } } return array($dst->getPriority(), $sub); }
protected function applyRequest(ManiphestTask $task, ConduitAPIRequest $request, $is_new) { $changes = array(); if ($is_new) { $task->setTitle((string) $request->getValue('title')); $task->setDescription((string) $request->getValue('description')); $changes[ManiphestTransaction::TYPE_STATUS] = ManiphestTaskStatus::getDefaultStatus(); $changes[PhabricatorTransactions::TYPE_SUBSCRIBERS] = array('+' => array($request->getUser()->getPHID())); } else { $comments = $request->getValue('comments'); if (!$is_new && $comments !== null) { $changes[PhabricatorTransactions::TYPE_COMMENT] = null; } $title = $request->getValue('title'); if ($title !== null) { $changes[ManiphestTransaction::TYPE_TITLE] = $title; } $desc = $request->getValue('description'); if ($desc !== null) { $changes[ManiphestTransaction::TYPE_DESCRIPTION] = $desc; } $status = $request->getValue('status'); if ($status !== null) { $valid_statuses = ManiphestTaskStatus::getTaskStatusMap(); if (!isset($valid_statuses[$status])) { throw id(new ConduitException('ERR-INVALID-PARAMETER'))->setErrorDescription(pht('Status set to invalid value.')); } $changes[ManiphestTransaction::TYPE_STATUS] = $status; } } $priority = $request->getValue('priority'); if ($priority !== null) { $valid_priorities = ManiphestTaskPriority::getTaskPriorityMap(); if (!isset($valid_priorities[$priority])) { throw id(new ConduitException('ERR-INVALID-PARAMETER'))->setErrorDescription(pht('Priority set to invalid value.')); } $changes[ManiphestTransaction::TYPE_PRIORITY] = $priority; } $owner_phid = $request->getValue('ownerPHID'); if ($owner_phid !== null) { $this->validatePHIDList(array($owner_phid), PhabricatorPeopleUserPHIDType::TYPECONST, 'ownerPHID'); $changes[ManiphestTransaction::TYPE_OWNER] = $owner_phid; } $ccs = $request->getValue('ccPHIDs'); if ($ccs !== null) { $changes[PhabricatorTransactions::TYPE_SUBSCRIBERS] = array('=' => array_fuse($ccs)); } $transactions = array(); $view_policy = $request->getValue('viewPolicy'); if ($view_policy !== null) { $transactions[] = id(new ManiphestTransaction())->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY)->setNewValue($view_policy); } $edit_policy = $request->getValue('editPolicy'); if ($edit_policy !== null) { $transactions[] = id(new ManiphestTransaction())->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY)->setNewValue($edit_policy); } $project_phids = $request->getValue('projectPHIDs'); if ($project_phids !== null) { $this->validatePHIDList($project_phids, PhabricatorProjectProjectPHIDType::TYPECONST, 'projectPHIDS'); $project_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST; $transactions[] = id(new ManiphestTransaction())->setTransactionType(PhabricatorTransactions::TYPE_EDGE)->setMetadataValue('edge:type', $project_type)->setNewValue(array('=' => array_fuse($project_phids))); } $template = new ManiphestTransaction(); foreach ($changes as $type => $value) { $transaction = clone $template; $transaction->setTransactionType($type); if ($type == PhabricatorTransactions::TYPE_COMMENT) { $transaction->attachComment(id(new ManiphestTransactionComment())->setContent($comments)); } else { $transaction->setNewValue($value); } $transactions[] = $transaction; } $field_list = PhabricatorCustomField::getObjectFields($task, PhabricatorCustomField::ROLE_EDIT); $field_list->readFieldsFromStorage($task); $auxiliary = $request->getValue('auxiliary'); if ($auxiliary) { foreach ($field_list->getFields() as $key => $field) { if (!array_key_exists($key, $auxiliary)) { continue; } $transaction = clone $template; $transaction->setTransactionType(PhabricatorTransactions::TYPE_CUSTOMFIELD); $transaction->setMetadataValue('customfield:key', $key); $transaction->setOldValue($field->getOldValueForApplicationTransactions()); $transaction->setNewValue($auxiliary[$key]); $transactions[] = $transaction; } } if (!$transactions) { return; } $content_source = $request->newContentSource(); $editor = id(new ManiphestTransactionEditor())->setActor($request->getUser())->setContentSource($content_source)->setContinueOnNoEffect(true); if (!$is_new) { $editor->setContinueOnMissingFields(true); } $editor->applyTransactions($task, $transactions); // reload the task now that we've done all the fun stuff return id(new ManiphestTaskQuery())->setViewer($request->getUser())->withPHIDs(array($task->getPHID()))->needSubscriberPHIDs(true)->needProjectPHIDs(true)->executeOne(); }