diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1281,6 +1281,7 @@ 'HeraldPreCommitContentAdapter' => 'applications/diffusion/herald/HeraldPreCommitContentAdapter.php', 'HeraldPreCommitRefAdapter' => 'applications/diffusion/herald/HeraldPreCommitRefAdapter.php', 'HeraldPreventActionGroup' => 'applications/herald/action/HeraldPreventActionGroup.php', + 'HeraldProjectColumnField' => 'applications/project/herald/HeraldProjectColumnField.php', 'HeraldProjectsField' => 'applications/project/herald/HeraldProjectsField.php', 'HeraldRecursiveConditionsException' => 'applications/herald/engine/exception/HeraldRecursiveConditionsException.php', 'HeraldRelatedFieldGroup' => 'applications/herald/field/HeraldRelatedFieldGroup.php', @@ -3375,6 +3376,7 @@ 'PhabricatorProjectCardView' => 'applications/project/view/PhabricatorProjectCardView.php', 'PhabricatorProjectColorsConfigOptionType' => 'applications/project/config/PhabricatorProjectColorsConfigOptionType.php', 'PhabricatorProjectColumn' => 'applications/project/storage/PhabricatorProjectColumn.php', + 'PhabricatorProjectColumnDatasource' => 'applications/project/typeahead/PhabricatorProjectColumnDatasource.php', 'PhabricatorProjectColumnDetailController' => 'applications/project/controller/PhabricatorProjectColumnDetailController.php', 'PhabricatorProjectColumnEditController' => 'applications/project/controller/PhabricatorProjectColumnEditController.php', 'PhabricatorProjectColumnHideController' => 'applications/project/controller/PhabricatorProjectColumnHideController.php', @@ -6013,6 +6015,7 @@ 'HeraldPreCommitContentAdapter' => 'HeraldPreCommitAdapter', 'HeraldPreCommitRefAdapter' => 'HeraldPreCommitAdapter', 'HeraldPreventActionGroup' => 'HeraldActionGroup', + 'HeraldProjectColumnField' => 'ManiphestTaskHeraldField', 'HeraldProjectsField' => 'HeraldField', 'HeraldRecursiveConditionsException' => 'Exception', 'HeraldRelatedFieldGroup' => 'HeraldFieldGroup', @@ -8466,6 +8469,7 @@ 'PhabricatorDestructibleInterface', 'PhabricatorExtendedPolicyInterface', ), + 'PhabricatorProjectColumnDatasource' => 'PhabricatorTypeaheadDatasource', 'PhabricatorProjectColumnDetailController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectColumnEditController' => 'PhabricatorProjectBoardController', 'PhabricatorProjectColumnHideController' => 'PhabricatorProjectBoardController', diff --git a/src/applications/herald/adapter/HeraldAdapter.php b/src/applications/herald/adapter/HeraldAdapter.php --- a/src/applications/herald/adapter/HeraldAdapter.php +++ b/src/applications/herald/adapter/HeraldAdapter.php @@ -26,6 +26,7 @@ const CONDITION_NOT_BIT = '!bit'; const CONDITION_IS_TRUE = 'true'; const CONDITION_IS_FALSE = 'false'; + const CONDITION_MOVED_TO = 'moved-to'; private $contentSource; private $isNewObject; @@ -349,6 +350,7 @@ self::CONDITION_REGEXP_PAIR => pht('matches regexp pair'), self::CONDITION_HAS_BIT => pht('has bit'), self::CONDITION_NOT_BIT => pht('lacks bit'), + self::CONDITION_MOVED_TO => pht('moved to any of'), ); } @@ -403,6 +405,7 @@ case self::CONDITION_IS_NOT_ME: return ($field_value != $rule->getAuthorPHID()); case self::CONDITION_IS_ANY: + case self::CONDITION_MOVED_TO: if (!is_array($condition_value)) { throw new HeraldInvalidConditionException( pht('Expected condition value to be an array.')); @@ -605,6 +608,7 @@ case self::CONDITION_NOT_BIT: case self::CONDITION_IS_TRUE: case self::CONDITION_IS_FALSE: + case self::CONDITION_MOVED_TO: // No explicit validation for these types, although there probably // should be in some cases. break; diff --git a/src/applications/herald/field/HeraldField.php b/src/applications/herald/field/HeraldField.php --- a/src/applications/herald/field/HeraldField.php +++ b/src/applications/herald/field/HeraldField.php @@ -53,6 +53,7 @@ return array( HeraldAdapter::CONDITION_IS_ANY, HeraldAdapter::CONDITION_IS_NOT_ANY, + HeraldAdapter::CONDITION_MOVED_TO, ); case self::STANDARD_PHID_LIST: return array( diff --git a/src/applications/project/herald/HeraldProjectColumnField.php b/src/applications/project/herald/HeraldProjectColumnField.php new file mode 100644 --- /dev/null +++ b/src/applications/project/herald/HeraldProjectColumnField.php @@ -0,0 +1,47 @@ +getPHID(); + + $project_phids = PhabricatorEdgeQuery::loadDestinationPHIDs( + $object_phid, + PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); + + $engine = id(new PhabricatorBoardLayoutEngine()) + ->setViewer(PhabricatorUser::getOmnipotentUser()) + ->setBoardPHIDs($project_phids) + ->setObjectPHIDs(array($object_phid)) + ->executeLayout(); + + $result = array(); + foreach ($project_phids as $project_phid) { + $columns = $engine->getObjectColumns($project_phid, $object_phid); + $result = array_merge($result, array_keys($columns)); + } + + return reset($result); + } + + public function getHeraldFieldConditions() { + return array( + HeraldAdapter::CONDITION_MOVED_TO, + ); + } + + protected function getHeraldFieldStandardType() { + return self::STANDARD_PHID; + } + + protected function getDatasource() { + return new PhabricatorProjectColumnDatasource(); + } + +} diff --git a/src/applications/project/phid/PhabricatorProjectColumnPHIDType.php b/src/applications/project/phid/PhabricatorProjectColumnPHIDType.php --- a/src/applications/project/phid/PhabricatorProjectColumnPHIDType.php +++ b/src/applications/project/phid/PhabricatorProjectColumnPHIDType.php @@ -35,9 +35,13 @@ foreach ($handles as $phid => $handle) { $column = $objects[$phid]; + $project = $column->getProject(); - $handle->setName($column->getDisplayName()); - $handle->setURI('/project/board/'.$column->getProject()->getID().'/'); + $name = $project->getDisplayName().': '.$column->getDisplayName(); + + $handle->setName($name); + $handle->setURI('/project/board/'.$project->getID().'/'); + $handle->setTagColor($project->getColor()); if ($column->isHidden()) { $handle->setStatus(PhabricatorObjectHandle::STATUS_CLOSED); diff --git a/src/applications/project/query/PhabricatorProjectColumnQuery.php b/src/applications/project/query/PhabricatorProjectColumnQuery.php --- a/src/applications/project/query/PhabricatorProjectColumnQuery.php +++ b/src/applications/project/query/PhabricatorProjectColumnQuery.php @@ -8,6 +8,7 @@ private $projectPHIDs; private $proxyPHIDs; private $statuses; + private $datasourceQuery; public function withIDs(array $ids) { $this->ids = $ids; @@ -34,6 +35,11 @@ return $this; } + public function withDatasourceQuery($query) { + $this->datasourceQuery = $query; + return $this; + } + public function newResultObject() { return new PhabricatorProjectColumn(); } @@ -118,44 +124,85 @@ return $page; } + protected function getPrimaryTableAlias() { + return 'col'; + } + + protected function buildSelectClauseParts(AphrontDatabaseConnection $conn) { + $parts = parent::buildSelectClauseParts($conn); + + $parts[] = 'col.*'; + + if ($this->shouldJoinProjectTable()) { + $parts[] = 'proj.name projectName'; + } + + return $parts; + } + + protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { + $joins = parent::buildJoinClauseParts($conn); + + if ($this->shouldJoinProjectTable()) { + $joins[] = qsprintf( + $conn, + 'LEFT JOIN %T proj ON col.projectPHID = proj.phid', + id(new PhabricatorProject())->getTableName()); + } + + return $joins; + } + + private function shouldJoinProjectTable() { + return (strlen($this->datasourceQuery) ? true : false); + } + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, - 'id IN (%Ld)', + 'col.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, - 'phid IN (%Ls)', + 'col.phid IN (%Ls)', $this->phids); } if ($this->projectPHIDs !== null) { $where[] = qsprintf( $conn, - 'projectPHID IN (%Ls)', + 'col.projectPHID IN (%Ls)', $this->projectPHIDs); } if ($this->proxyPHIDs !== null) { $where[] = qsprintf( $conn, - 'proxyPHID IN (%Ls)', + 'col.proxyPHID IN (%Ls)', $this->proxyPHIDs); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, - 'status IN (%Ld)', + 'col.status IN (%Ld)', $this->statuses); } + if (strlen($this->datasourceQuery)) { + $where[] = qsprintf( + $conn, + 'LOWER(col.name) LIKE LOWER(%>) OR proj.name LIKE %>', + $this->datasourceQuery, + $this->datasourceQuery); + } + return $where; } diff --git a/src/applications/project/typeahead/PhabricatorProjectColumnDatasource.php b/src/applications/project/typeahead/PhabricatorProjectColumnDatasource.php new file mode 100644 --- /dev/null +++ b/src/applications/project/typeahead/PhabricatorProjectColumnDatasource.php @@ -0,0 +1,78 @@ +getViewer(); + $is_browse = $this->getIsBrowse(); + + $phid_type = new PhabricatorProjectColumnPHIDType(); + + $query = id(new PhabricatorProjectColumnQuery()) + ->withDatasourceQuery($this->getRawQuery()); + $columns = $this->executeQuery($query); + + $results = array(); + foreach ($columns as $column) { + $project = $column->getProject(); + $project = id(new PhabricatorProjectQuery()) + ->setViewer($viewer) + ->withPHIDs(array($project->getPHID())) + ->needImages(true) + ->executeOne(); + + $name = array( + $project->getDisplayName(), + $column->getDisplayName(), + ); + + $display_name = implode(': ', $name); + $name = implode(' ', $name); + + $closed = $column->isHidden() ? pht('Hidden') : null; + + $result = id(new PhabricatorTypeaheadResult()) + ->setName($name) + ->setDisplayName($display_name) + ->setDisplayType($column->getDisplayType()) + ->setImageURI($project->getProfileImageURI()) + ->setURI('/project/board/'.$project->getID().'/') + ->setPHID($column->getPHID()) + ->setIcon($phid_type->getTypeIcon()) + ->setColor($this->getProjectColumnColor($column)) + ->setClosed($closed); + + if ($is_browse) { + $result->addAttribute($phid_type->getTypeName()); + } + + $results[] = $result; + } + + return $results; + } + + private function getProjectColumnColor(PhabricatorProjectColumn $column) { + $project = $column->getProject(); + + if ($column->isHidden() || $project->isArchived()) { + return PHUITagView::COLOR_DISABLED; + } + + return $project->getColor(); + } + +}