diff --git a/src/applications/maniphest/constants/ManiphestTaskPriority.php b/src/applications/maniphest/constants/ManiphestTaskPriority.php index 8bc682f7e4..86c7adbcb7 100644 --- a/src/applications/maniphest/constants/ManiphestTaskPriority.php +++ b/src/applications/maniphest/constants/ManiphestTaskPriority.php @@ -1,197 +1,249 @@ $spec) { $map[$key] = idx($spec, 'name', $key); } return $map; } /** * Get the priorities and their command keywords. * * @return map Priorities to lists of command keywords. */ public static function getTaskPriorityKeywordsMap() { $map = self::getConfig(); foreach ($map as $key => $spec) { $words = idx($spec, 'keywords', array()); if (!is_array($words)) { $words = array($words); } foreach ($words as $word_key => $word) { $words[$word_key] = phutil_utf8_strtolower($word); } $words = array_unique($words); $map[$key] = $words; } return $map; } + /** + * Get the canonical keyword for a given priority constant. + * + * @return string|null Keyword, or `null` if no keyword is configured. + */ + public static function getKeywordForTaskPriority($priority) { + $map = self::getConfig(); + + $spec = idx($map, $priority); + if (!$spec) { + return null; + } + + $keywords = idx($spec, 'keywords'); + if (!$keywords) { + return null; + } + + return head($keywords); + } + + + /** + * Get a map of supported alternate names for each priority. + * + * Keys are aliases, like "wish" and "wishlist". Values are canonical + * priority keywords, like "wishlist". + * + * @return map Map of aliases to canonical priority keywords. + */ + public static function getTaskPriorityAliasMap() { + $keyword_map = self::getTaskPriorityKeywordsMap(); + + $result = array(); + foreach ($keyword_map as $key => $keywords) { + $target = self::getKeywordForTaskPriority($key); + if ($target === null) { + continue; + } + + // NOTE: Include the raw priority value, like "25", in the list of + // aliases. This supports legacy sources like saved EditEngine forms. + $result[$key] = $target; + + foreach ($keywords as $keyword) { + $result[$keyword] = $target; + } + } + + return $result; + } + /** * Get the priorities and their related short (one-word) descriptions. * * @return map Priorities to short descriptions. */ public static function getShortNameMap() { $map = self::getConfig(); foreach ($map as $key => $spec) { $map[$key] = idx($spec, 'short', idx($spec, 'name', $key)); } return $map; } /** * Get a map from priority constants to their colors. * * @return map Priorities to colors. */ public static function getColorMap() { $map = self::getConfig(); foreach ($map as $key => $spec) { $map[$key] = idx($spec, 'color', 'grey'); } return $map; } /** * Return the default priority for this instance of Phabricator. * * @return int The value of the default priority constant. */ public static function getDefaultPriority() { return PhabricatorEnv::getEnvConfig('maniphest.default-priority'); } /** * Retrieve the full name of the priority level provided. * * @param int A priority level. * @return string The priority name if the level is a valid one. */ public static function getTaskPriorityName($priority) { return idx(self::getTaskPriorityMap(), $priority, $priority); } /** * Retrieve the color of the priority level given * * @param int A priority level. * @return string The color of the priority if the level is valid, * or black if it is not. */ public static function getTaskPriorityColor($priority) { return idx(self::getColorMap(), $priority, 'black'); } public static function getTaskPriorityIcon($priority) { return 'fa-arrow-right'; } public static function getTaskPriorityFromKeyword($keyword) { $map = self::getTaskPriorityKeywordsMap(); foreach ($map as $priority => $keywords) { if (in_array($keyword, $keywords)) { return $priority; } } return null; } public static function isDisabledPriority($priority) { $config = idx(self::getConfig(), $priority, array()); return idx($config, 'disabled', false); } public static function getConfig() { $config = PhabricatorEnv::getEnvConfig('maniphest.priorities'); krsort($config); return $config; } private static function isValidPriorityKeyword($keyword) { if (!strlen($keyword) || strlen($keyword) > 64) { return false; } // Alphanumeric, but not exclusively numeric if (!preg_match('/^(?![0-9]*$)[a-zA-Z0-9]+$/', $keyword)) { return false; } return true; } public static function validateConfiguration($config) { if (!is_array($config)) { throw new Exception( pht( 'Configuration is not valid. Maniphest priority configurations '. 'must be dictionaries.', $config)); } foreach ($config as $key => $value) { if (!ctype_digit((string)$key)) { throw new Exception( pht( 'Key "%s" is not a valid priority constant. Priority constants '. 'must be nonnegative integers.', $key)); } if (!is_array($value)) { throw new Exception( pht( 'Value for key "%s" should be a dictionary.', $key)); } PhutilTypeSpec::checkMap( $value, array( 'name' => 'string', 'short' => 'optional string', 'color' => 'optional string', 'keywords' => 'list', 'disabled' => 'optional bool', )); $keywords = $value['keywords']; foreach ($keywords as $keyword) { if (!self::isValidPriorityKeyword($keyword)) { throw new Exception( pht( 'Key "%s" is not a valid priority keyword. Priority keywords '. 'must be 1-64 alphanumeric characters and cannot be '. 'exclusively digits. For example, "%s" or "%s" are '. 'reasonable choices.', $keyword, 'low', 'critical')); } } } } } diff --git a/src/applications/maniphest/editor/ManiphestEditEngine.php b/src/applications/maniphest/editor/ManiphestEditEngine.php index acd27fc908..9f5e647ca9 100644 --- a/src/applications/maniphest/editor/ManiphestEditEngine.php +++ b/src/applications/maniphest/editor/ManiphestEditEngine.php @@ -1,469 +1,472 @@ addInt(100); } protected function newEditableObject() { return ManiphestTask::initializeNewTask($this->getViewer()); } protected function newObjectQuery() { return id(new ManiphestTaskQuery()); } protected function getObjectCreateTitleText($object) { return pht('Create New Task'); } protected function getObjectEditTitleText($object) { return pht('Edit Task: %s', $object->getTitle()); } protected function getObjectEditShortText($object) { return $object->getMonogram(); } protected function getObjectCreateShortText() { return pht('Create Task'); } protected function getObjectName() { return pht('Task'); } protected function getEditorURI() { return $this->getApplication()->getApplicationURI('task/edit/'); } protected function getCommentViewHeaderText($object) { return pht('Weigh In'); } protected function getCommentViewButtonText($object) { return pht('Set Sail for Adventure'); } protected function getObjectViewURI($object) { return '/'.$object->getMonogram(); } protected function buildCustomEditFields($object) { $status_map = $this->getTaskStatusMap($object); $priority_map = $this->getTaskPriorityMap($object); + $alias_map = ManiphestTaskPriority::getTaskPriorityAliasMap(); + if ($object->isClosed()) { $default_status = ManiphestTaskStatus::getDefaultStatus(); } else { $default_status = ManiphestTaskStatus::getDefaultClosedStatus(); } if ($object->getOwnerPHID()) { $owner_value = array($object->getOwnerPHID()); } else { $owner_value = array($this->getViewer()->getPHID()); } $column_documentation = pht(<<getColumnMap($object); $fields = array( id(new PhabricatorHandlesEditField()) ->setKey('parent') ->setLabel(pht('Parent Task')) ->setDescription(pht('Task to make this a subtask of.')) ->setConduitDescription(pht('Create as a subtask of another task.')) ->setConduitTypeDescription(pht('PHID of the parent task.')) ->setAliases(array('parentPHID')) ->setTransactionType(ManiphestTaskParentTransaction::TRANSACTIONTYPE) ->setHandleParameterType(new ManiphestTaskListHTTPParameterType()) ->setSingleValue(null) ->setIsReorderable(false) ->setIsDefaultable(false) ->setIsLockable(false), id(new PhabricatorColumnsEditField()) ->setKey('column') ->setLabel(pht('Column')) ->setDescription(pht('Create a task in a workboard column.')) ->setConduitDescription( pht('Move a task to one or more workboard columns.')) ->setConduitTypeDescription( pht('List of columns to move the task to.')) ->setConduitDocumentation($column_documentation) ->setAliases(array('columnPHID', 'columns', 'columnPHIDs')) ->setTransactionType(PhabricatorTransactions::TYPE_COLUMNS) ->setIsReorderable(false) ->setIsDefaultable(false) ->setIsLockable(false) ->setCommentActionLabel(pht('Move on Workboard')) ->setCommentActionOrder(2000) ->setColumnMap($column_map), id(new PhabricatorTextEditField()) ->setKey('title') ->setLabel(pht('Title')) ->setDescription(pht('Name of the task.')) ->setConduitDescription(pht('Rename the task.')) ->setConduitTypeDescription(pht('New task name.')) ->setTransactionType(ManiphestTaskTitleTransaction::TRANSACTIONTYPE) ->setIsRequired(true) ->setValue($object->getTitle()), id(new PhabricatorUsersEditField()) ->setKey('owner') ->setAliases(array('ownerPHID', 'assign', 'assigned')) ->setLabel(pht('Assigned To')) ->setDescription(pht('User who is responsible for the task.')) ->setConduitDescription(pht('Reassign the task.')) ->setConduitTypeDescription( pht('New task owner, or `null` to unassign.')) ->setTransactionType(ManiphestTaskOwnerTransaction::TRANSACTIONTYPE) ->setIsCopyable(true) ->setSingleValue($object->getOwnerPHID()) ->setCommentActionLabel(pht('Assign / Claim')) ->setCommentActionValue($owner_value), id(new PhabricatorSelectEditField()) ->setKey('status') ->setLabel(pht('Status')) ->setDescription(pht('Status of the task.')) ->setConduitDescription(pht('Change the task status.')) ->setConduitTypeDescription(pht('New task status constant.')) ->setTransactionType(ManiphestTaskStatusTransaction::TRANSACTIONTYPE) ->setIsCopyable(true) ->setValue($object->getStatus()) ->setOptions($status_map) ->setCommentActionLabel(pht('Change Status')) ->setCommentActionValue($default_status), id(new PhabricatorSelectEditField()) ->setKey('priority') ->setLabel(pht('Priority')) ->setDescription(pht('Priority of the task.')) ->setConduitDescription(pht('Change the priority of the task.')) ->setConduitTypeDescription(pht('New task priority constant.')) ->setTransactionType(ManiphestTaskPriorityTransaction::TRANSACTIONTYPE) ->setIsCopyable(true) ->setValue($object->getPriorityKeyword()) ->setOptions($priority_map) + ->setOptionAliases($alias_map) ->setCommentActionLabel(pht('Change Priority')), ); if (ManiphestTaskPoints::getIsEnabled()) { $points_label = ManiphestTaskPoints::getPointsLabel(); $action_label = ManiphestTaskPoints::getPointsActionLabel(); $fields[] = id(new PhabricatorPointsEditField()) ->setKey('points') ->setLabel($points_label) ->setDescription(pht('Point value of the task.')) ->setConduitDescription(pht('Change the task point value.')) ->setConduitTypeDescription(pht('New task point value.')) ->setTransactionType(ManiphestTaskPointsTransaction::TRANSACTIONTYPE) ->setIsCopyable(true) ->setValue($object->getPoints()) ->setCommentActionLabel($action_label); } $fields[] = id(new PhabricatorRemarkupEditField()) ->setKey('description') ->setLabel(pht('Description')) ->setDescription(pht('Task description.')) ->setConduitDescription(pht('Update the task description.')) ->setConduitTypeDescription(pht('New task description.')) ->setTransactionType(ManiphestTaskDescriptionTransaction::TRANSACTIONTYPE) ->setValue($object->getDescription()) ->setPreviewPanel( id(new PHUIRemarkupPreviewPanel()) ->setHeader(pht('Description Preview'))); return $fields; } private function getTaskStatusMap(ManiphestTask $task) { $status_map = ManiphestTaskStatus::getTaskStatusMap(); $current_status = $task->getStatus(); // If the current status is something we don't recognize (maybe an older // status which was deleted), put a dummy entry in the status map so that // saving the form doesn't destroy any data by accident. if (idx($status_map, $current_status) === null) { $status_map[$current_status] = pht('', $current_status); } $dup_status = ManiphestTaskStatus::getDuplicateStatus(); foreach ($status_map as $status => $status_name) { // Always keep the task's current status. if ($status == $current_status) { continue; } // Don't allow tasks to be changed directly into "Closed, Duplicate" // status. Instead, you have to merge them. See T4819. if ($status == $dup_status) { unset($status_map[$status]); continue; } // Don't let new or existing tasks be moved into a disabled status. if (ManiphestTaskStatus::isDisabledStatus($status)) { unset($status_map[$status]); continue; } } return $status_map; } private function getTaskPriorityMap(ManiphestTask $task) { $priority_map = ManiphestTaskPriority::getTaskPriorityMap(); $priority_keywords = ManiphestTaskPriority::getTaskPriorityKeywordsMap(); $current_priority = $task->getPriority(); $results = array(); foreach ($priority_map as $priority => $priority_name) { $disabled = ManiphestTaskPriority::isDisabledPriority($priority); if ($disabled && !($priority == $current_priority)) { continue; } $keyword = head(idx($priority_keywords, $priority)); $results[$keyword] = $priority_name; } // If the current value isn't a legitimate one, put it in the dropdown // anyway so saving the form doesn't cause any side effects. if (idx($priority_map, $current_priority) === null) { $results[ManiphestTaskPriority::UNKNOWN_PRIORITY_KEYWORD] = pht( '', $current_priority); } return $results; } protected function newEditResponse( AphrontRequest $request, $object, array $xactions) { if ($request->isAjax()) { // Reload the task to make sure we pick up the final task state. $viewer = $this->getViewer(); $task = id(new ManiphestTaskQuery()) ->setViewer($viewer) ->withIDs(array($object->getID())) ->needSubscriberPHIDs(true) ->needProjectPHIDs(true) ->executeOne(); switch ($request->getStr('responseType')) { case 'card': return $this->buildCardResponse($task); default: return $this->buildListResponse($task); } } return parent::newEditResponse($request, $object, $xactions); } private function buildListResponse(ManiphestTask $task) { $controller = $this->getController(); $payload = array( 'tasks' => $controller->renderSingleTask($task), 'data' => array(), ); return id(new AphrontAjaxResponse())->setContent($payload); } private function buildCardResponse(ManiphestTask $task) { $controller = $this->getController(); $request = $controller->getRequest(); $viewer = $request->getViewer(); $column_phid = $request->getStr('columnPHID'); $visible_phids = $request->getStrList('visiblePHIDs'); if (!$visible_phids) { $visible_phids = array(); } $column = id(new PhabricatorProjectColumnQuery()) ->setViewer($viewer) ->withPHIDs(array($column_phid)) ->executeOne(); if (!$column) { return new Aphront404Response(); } $board_phid = $column->getProjectPHID(); $object_phid = $task->getPHID(); return id(new PhabricatorBoardResponseEngine()) ->setViewer($viewer) ->setBoardPHID($board_phid) ->setObjectPHID($object_phid) ->setVisiblePHIDs($visible_phids) ->buildResponse(); } private function getColumnMap(ManiphestTask $task) { $phid = $task->getPHID(); if (!$phid) { return array(); } $board_phids = PhabricatorEdgeQuery::loadDestinationPHIDs( $phid, PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); if (!$board_phids) { return array(); } $viewer = $this->getViewer(); $layout_engine = id(new PhabricatorBoardLayoutEngine()) ->setViewer($viewer) ->setBoardPHIDs($board_phids) ->setObjectPHIDs(array($task->getPHID())) ->executeLayout(); $map = array(); foreach ($board_phids as $board_phid) { $in_columns = $layout_engine->getObjectColumns($board_phid, $phid); $in_columns = mpull($in_columns, null, 'getPHID'); $all_columns = $layout_engine->getColumns($board_phid); if (!$all_columns) { // This could be a project with no workboard, or a project the viewer // does not have permission to see. continue; } $board = head($all_columns)->getProject(); $options = array(); foreach ($all_columns as $column) { $name = $column->getDisplayName(); $is_hidden = $column->isHidden(); $is_selected = isset($in_columns[$column->getPHID()]); // Don't show hidden, subproject or milestone columns in this map // unless the object is currently in the column. $skip_column = ($is_hidden || $column->getProxyPHID()); if ($skip_column) { if (!$is_selected) { continue; } } if ($is_hidden) { $name = pht('(%s)', $name); } if ($is_selected) { $name = pht("\xE2\x97\x8F %s", $name); } else { $name = pht("\xE2\x97\x8B %s", $name); } $option = array( 'key' => $column->getPHID(), 'label' => $name, 'selected' => (bool)$is_selected, ); $options[] = $option; } $map[] = array( 'label' => $board->getDisplayName(), 'options' => $options, ); } $map = isort($map, 'label'); $map = array_values($map); return $map; } } diff --git a/src/applications/maniphest/storage/ManiphestTask.php b/src/applications/maniphest/storage/ManiphestTask.php index 3b312fc9cc..d8a174bd0a 100644 --- a/src/applications/maniphest/storage/ManiphestTask.php +++ b/src/applications/maniphest/storage/ManiphestTask.php @@ -1,603 +1,606 @@ setViewer($actor) ->withClasses(array('PhabricatorManiphestApplication')) ->executeOne(); $view_policy = $app->getPolicy(ManiphestDefaultViewCapability::CAPABILITY); $edit_policy = $app->getPolicy(ManiphestDefaultEditCapability::CAPABILITY); return id(new ManiphestTask()) ->setStatus(ManiphestTaskStatus::getDefaultStatus()) ->setPriority(ManiphestTaskPriority::getDefaultPriority()) ->setAuthorPHID($actor->getPHID()) ->setViewPolicy($view_policy) ->setEditPolicy($edit_policy) ->setSpacePHID($actor->getDefaultSpacePHID()) ->setSubtype(PhabricatorEditEngineSubtype::SUBTYPE_DEFAULT) ->attachProjectPHIDs(array()) ->attachSubscriberPHIDs(array()); } protected function getConfiguration() { return array( self::CONFIG_AUX_PHID => true, self::CONFIG_SERIALIZATION => array( 'properties' => self::SERIALIZATION_JSON, ), self::CONFIG_COLUMN_SCHEMA => array( 'ownerPHID' => 'phid?', 'status' => 'text64', 'priority' => 'uint32', 'title' => 'sort', 'originalTitle' => 'text', 'description' => 'text', 'mailKey' => 'bytes20', 'ownerOrdering' => 'text64?', 'originalEmailSource' => 'text255?', 'subpriority' => 'double', 'points' => 'double?', 'bridgedObjectPHID' => 'phid?', 'subtype' => 'text64', ), self::CONFIG_KEY_SCHEMA => array( 'key_phid' => null, 'phid' => array( 'columns' => array('phid'), 'unique' => true, ), 'priority' => array( 'columns' => array('priority', 'status'), ), 'status' => array( 'columns' => array('status'), ), 'ownerPHID' => array( 'columns' => array('ownerPHID', 'status'), ), 'authorPHID' => array( 'columns' => array('authorPHID', 'status'), ), 'ownerOrdering' => array( 'columns' => array('ownerOrdering'), ), 'priority_2' => array( 'columns' => array('priority', 'subpriority'), ), 'key_dateCreated' => array( 'columns' => array('dateCreated'), ), 'key_dateModified' => array( 'columns' => array('dateModified'), ), 'key_title' => array( 'columns' => array('title(64)'), ), 'key_bridgedobject' => array( 'columns' => array('bridgedObjectPHID'), 'unique' => true, ), 'key_subtype' => array( 'columns' => array('subtype'), ), ), ) + parent::getConfiguration(); } public function loadDependsOnTaskPHIDs() { return PhabricatorEdgeQuery::loadDestinationPHIDs( $this->getPHID(), ManiphestTaskDependsOnTaskEdgeType::EDGECONST); } public function loadDependedOnByTaskPHIDs() { return PhabricatorEdgeQuery::loadDestinationPHIDs( $this->getPHID(), ManiphestTaskDependedOnByTaskEdgeType::EDGECONST); } public function generatePHID() { return PhabricatorPHID::generateNewPHID(ManiphestTaskPHIDType::TYPECONST); } public function getSubscriberPHIDs() { return $this->assertAttached($this->subscriberPHIDs); } public function getProjectPHIDs() { return $this->assertAttached($this->edgeProjectPHIDs); } public function attachProjectPHIDs(array $phids) { $this->edgeProjectPHIDs = $phids; return $this; } public function attachSubscriberPHIDs(array $phids) { $this->subscriberPHIDs = $phids; return $this; } public function setOwnerPHID($phid) { $this->ownerPHID = nonempty($phid, null); return $this; } public function setTitle($title) { $this->title = $title; if (!$this->getID()) { $this->originalTitle = $title; } return $this; } public function getMonogram() { return 'T'.$this->getID(); } public function getURI() { return '/'.$this->getMonogram(); } public function attachGroupByProjectPHID($phid) { $this->groupByProjectPHID = $phid; return $this; } public function getGroupByProjectPHID() { return $this->assertAttached($this->groupByProjectPHID); } public function save() { if (!$this->mailKey) { $this->mailKey = Filesystem::readRandomCharacters(20); } $result = parent::save(); return $result; } public function isClosed() { return ManiphestTaskStatus::isClosedStatus($this->getStatus()); } public function isLocked() { return ManiphestTaskStatus::isLockedStatus($this->getStatus()); } public function setProperty($key, $value) { $this->properties[$key] = $value; return $this; } public function getProperty($key, $default = null) { return idx($this->properties, $key, $default); } public function getCoverImageFilePHID() { return idx($this->properties, 'cover.filePHID'); } public function getCoverImageThumbnailPHID() { return idx($this->properties, 'cover.thumbnailPHID'); } public function getWorkboardOrderVectors() { return array( PhabricatorProjectColumn::ORDER_PRIORITY => array( (int)-$this->getPriority(), (double)-$this->getSubpriority(), (int)-$this->getID(), ), ); } public function getPriorityKeyword() { $priority = $this->getPriority(); - $map = ManiphestTaskPriority::getTaskPriorityKeywordsMap(); - $default = array(ManiphestTaskPriority::UNKNOWN_PRIORITY_KEYWORD); - $keywords = idx($map, $priority, $default); - return head($keywords); + + $keyword = ManiphestTaskPriority::getKeywordForTaskPriority($priority); + if ($keyword !== null) { + return $keyword; + } + + return ManiphestTaskPriority::UNKNOWN_PRIORITY_KEYWORD; } private function comparePriorityTo(ManiphestTask $other) { $upri = $this->getPriority(); $vpri = $other->getPriority(); if ($upri != $vpri) { return ($upri - $vpri); } $usub = $this->getSubpriority(); $vsub = $other->getSubpriority(); if ($usub != $vsub) { return ($usub - $vsub); } $uid = $this->getID(); $vid = $other->getID(); if ($uid != $vid) { return ($uid - $vid); } return 0; } public function isLowerPriorityThan(ManiphestTask $other) { return ($this->comparePriorityTo($other) < 0); } public function isHigherPriorityThan(ManiphestTask $other) { return ($this->comparePriorityTo($other) > 0); } public function getWorkboardProperties() { return array( 'status' => $this->getStatus(), 'points' => (double)$this->getPoints(), ); } /* -( PhabricatorSubscribableInterface )----------------------------------- */ public function isAutomaticallySubscribed($phid) { return ($phid == $this->getOwnerPHID()); } /* -( Markup Interface )--------------------------------------------------- */ /** * @task markup */ public function getMarkupFieldKey($field) { $content = $this->getMarkupText($field); return PhabricatorMarkupEngine::digestRemarkupContent($this, $content); } /** * @task markup */ public function getMarkupText($field) { return $this->getDescription(); } /** * @task markup */ public function newMarkupEngine($field) { return PhabricatorMarkupEngine::newManiphestMarkupEngine(); } /** * @task markup */ public function didMarkupText( $field, $output, PhutilMarkupEngine $engine) { return $output; } /** * @task markup */ public function shouldUseMarkupCache($field) { return (bool)$this->getID(); } /* -( Policy Interface )--------------------------------------------------- */ public function getCapabilities() { return array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_INTERACT, PhabricatorPolicyCapability::CAN_EDIT, ); } public function getPolicy($capability) { switch ($capability) { case PhabricatorPolicyCapability::CAN_VIEW: return $this->getViewPolicy(); case PhabricatorPolicyCapability::CAN_INTERACT: if ($this->isLocked()) { return PhabricatorPolicies::POLICY_NOONE; } else { return $this->getViewPolicy(); } case PhabricatorPolicyCapability::CAN_EDIT: return $this->getEditPolicy(); } } public function hasAutomaticCapability($capability, PhabricatorUser $user) { // The owner of a task can always view and edit it. $owner_phid = $this->getOwnerPHID(); if ($owner_phid) { $user_phid = $user->getPHID(); if ($user_phid == $owner_phid) { return true; } } return false; } public function describeAutomaticCapability($capability) { return pht('The owner of a task can always view and edit it.'); } /* -( PhabricatorTokenReceiverInterface )---------------------------------- */ public function getUsersToNotifyOfTokenGiven() { // Sort of ambiguous who this was intended for; just let them both know. return array_filter( array_unique( array( $this->getAuthorPHID(), $this->getOwnerPHID(), ))); } /* -( PhabricatorCustomFieldInterface )------------------------------------ */ public function getCustomFieldSpecificationForRole($role) { return PhabricatorEnv::getEnvConfig('maniphest.fields'); } public function getCustomFieldBaseClass() { return 'ManiphestCustomField'; } public function getCustomFields() { return $this->assertAttached($this->customFields); } public function attachCustomFields(PhabricatorCustomFieldAttachment $fields) { $this->customFields = $fields; return $this; } /* -( PhabricatorDestructibleInterface )----------------------------------- */ public function destroyObjectPermanently( PhabricatorDestructionEngine $engine) { $this->openTransaction(); $this->delete(); $this->saveTransaction(); } /* -( PhabricatorApplicationTransactionInterface )------------------------- */ public function getApplicationTransactionEditor() { return new ManiphestTransactionEditor(); } public function getApplicationTransactionObject() { return $this; } public function getApplicationTransactionTemplate() { return new ManiphestTransaction(); } public function willRenderTimeline( PhabricatorApplicationTransactionView $timeline, AphrontRequest $request) { return $timeline; } /* -( PhabricatorSpacesInterface )----------------------------------------- */ public function getSpacePHID() { return $this->spacePHID; } /* -( PhabricatorConduitResultInterface )---------------------------------- */ public function getFieldSpecificationsForConduit() { return array( id(new PhabricatorConduitSearchFieldSpecification()) ->setKey('title') ->setType('string') ->setDescription(pht('The title of the task.')), id(new PhabricatorConduitSearchFieldSpecification()) ->setKey('description') ->setType('remarkup') ->setDescription(pht('The task description.')), id(new PhabricatorConduitSearchFieldSpecification()) ->setKey('authorPHID') ->setType('phid') ->setDescription(pht('Original task author.')), id(new PhabricatorConduitSearchFieldSpecification()) ->setKey('ownerPHID') ->setType('phid?') ->setDescription(pht('Current task owner, if task is assigned.')), id(new PhabricatorConduitSearchFieldSpecification()) ->setKey('status') ->setType('map') ->setDescription(pht('Information about task status.')), id(new PhabricatorConduitSearchFieldSpecification()) ->setKey('priority') ->setType('map') ->setDescription(pht('Information about task priority.')), id(new PhabricatorConduitSearchFieldSpecification()) ->setKey('points') ->setType('points') ->setDescription(pht('Point value of the task.')), id(new PhabricatorConduitSearchFieldSpecification()) ->setKey('subtype') ->setType('string') ->setDescription(pht('Subtype of the task.')), ); } public function getFieldValuesForConduit() { $status_value = $this->getStatus(); $status_info = array( 'value' => $status_value, 'name' => ManiphestTaskStatus::getTaskStatusName($status_value), 'color' => ManiphestTaskStatus::getStatusColor($status_value), ); $priority_value = (int)$this->getPriority(); $priority_info = array( 'value' => $priority_value, 'subpriority' => (double)$this->getSubpriority(), 'name' => ManiphestTaskPriority::getTaskPriorityName($priority_value), 'color' => ManiphestTaskPriority::getTaskPriorityColor($priority_value), ); return array( 'name' => $this->getTitle(), 'description' => array( 'raw' => $this->getDescription(), ), 'authorPHID' => $this->getAuthorPHID(), 'ownerPHID' => $this->getOwnerPHID(), 'status' => $status_info, 'priority' => $priority_info, 'points' => $this->getPoints(), 'subtype' => $this->getSubtype(), ); } public function getConduitSearchAttachments() { return array( id(new PhabricatorBoardColumnsSearchEngineAttachment()) ->setAttachmentKey('columns'), ); } public function newSubtypeObject() { $subtype_key = $this->getEditEngineSubtype(); $subtype_map = $this->newEditEngineSubtypeMap(); return idx($subtype_map, $subtype_key); } /* -( PhabricatorFulltextInterface )--------------------------------------- */ public function newFulltextEngine() { return new ManiphestTaskFulltextEngine(); } /* -( DoorkeeperBridgedObjectInterface )----------------------------------- */ public function getBridgedObject() { return $this->assertAttached($this->bridgedObject); } public function attachBridgedObject( DoorkeeperExternalObject $object = null) { $this->bridgedObject = $object; return $this; } /* -( PhabricatorEditEngineSubtypeInterface )------------------------------ */ public function getEditEngineSubtype() { return $this->getSubtype(); } public function setEditEngineSubtype($value) { return $this->setSubtype($value); } public function newEditEngineSubtypeMap() { $config = PhabricatorEnv::getEnvConfig('maniphest.subtypes'); return PhabricatorEditEngineSubtype::newSubtypeMap($config); } /* -( PhabricatorEditEngineLockableInterface )----------------------------- */ public function newEditEngineLock() { return new ManiphestTaskEditEngineLock(); } } diff --git a/src/applications/transactions/editfield/PhabricatorSelectEditField.php b/src/applications/transactions/editfield/PhabricatorSelectEditField.php index d8e99d610c..b72cafc5d1 100644 --- a/src/applications/transactions/editfield/PhabricatorSelectEditField.php +++ b/src/applications/transactions/editfield/PhabricatorSelectEditField.php @@ -1,38 +1,62 @@ options = $options; return $this; } public function getOptions() { if ($this->options === null) { throw new PhutilInvalidStateException('setOptions'); } return $this->options; } + public function setOptionAliases(array $option_aliases) { + $this->optionAliases = $option_aliases; + return $this; + } + + public function getOptionAliases() { + return $this->optionAliases; + } + + protected function getValueForControl() { + $value = parent::getValueForControl(); + + $options = $this->getOptions(); + if (!isset($options[$value])) { + $aliases = $this->getOptionAliases(); + if (isset($aliases[$value])) { + $value = $aliases[$value]; + } + } + + return $value; + } + protected function newControl() { return id(new AphrontFormSelectControl()) ->setOptions($this->getOptions()); } protected function newHTTPParameterType() { return new AphrontSelectHTTPParameterType(); } protected function newCommentAction() { return id(new PhabricatorEditEngineSelectCommentAction()) ->setOptions($this->getOptions()); } protected function newConduitParameterType() { return new ConduitStringParameterType(); } }