diff --git a/resources/sql/autopatches/20190127.project.01.subtype.sql b/resources/sql/autopatches/20190127.project.01.subtype.sql new file mode 100644 --- /dev/null +++ b/resources/sql/autopatches/20190127.project.01.subtype.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_project.project + ADD subtype VARCHAR(64) COLLATE {$COLLATE_TEXT} NOT NULL; diff --git a/resources/sql/autopatches/20190127.project.02.default.sql b/resources/sql/autopatches/20190127.project.02.default.sql new file mode 100644 --- /dev/null +++ b/resources/sql/autopatches/20190127.project.02.default.sql @@ -0,0 +1,2 @@ +UPDATE {$NAMESPACE}_project.project + SET subtype = 'default' WHERE subtype = ''; 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 @@ -4114,6 +4114,8 @@ 'PhabricatorProjectSubprojectWarningController' => 'applications/project/controller/PhabricatorProjectSubprojectWarningController.php', 'PhabricatorProjectSubprojectsController' => 'applications/project/controller/PhabricatorProjectSubprojectsController.php', 'PhabricatorProjectSubprojectsProfileMenuItem' => 'applications/project/menuitem/PhabricatorProjectSubprojectsProfileMenuItem.php', + 'PhabricatorProjectSubtypeDatasource' => 'applications/project/typeahead/PhabricatorProjectSubtypeDatasource.php', + 'PhabricatorProjectSubtypesConfigType' => 'applications/project/config/PhabricatorProjectSubtypesConfigType.php', 'PhabricatorProjectTestDataGenerator' => 'applications/project/lipsum/PhabricatorProjectTestDataGenerator.php', 'PhabricatorProjectTransaction' => 'applications/project/storage/PhabricatorProjectTransaction.php', 'PhabricatorProjectTransactionEditor' => 'applications/project/editor/PhabricatorProjectTransactionEditor.php', @@ -10029,6 +10031,7 @@ 'PhabricatorConduitResultInterface', 'PhabricatorColumnProxyInterface', 'PhabricatorSpacesInterface', + 'PhabricatorEditEngineSubtypeInterface', ), 'PhabricatorProjectAddHeraldAction' => 'PhabricatorProjectHeraldAction', 'PhabricatorProjectApplication' => 'PhabricatorApplication', @@ -10156,6 +10159,8 @@ 'PhabricatorProjectSubprojectWarningController' => 'PhabricatorProjectController', 'PhabricatorProjectSubprojectsController' => 'PhabricatorProjectController', 'PhabricatorProjectSubprojectsProfileMenuItem' => 'PhabricatorProfileMenuItem', + 'PhabricatorProjectSubtypeDatasource' => 'PhabricatorTypeaheadDatasource', + 'PhabricatorProjectSubtypesConfigType' => 'PhabricatorJSONConfigType', 'PhabricatorProjectTestDataGenerator' => 'PhabricatorTestDataGenerator', 'PhabricatorProjectTransaction' => 'PhabricatorModularTransaction', 'PhabricatorProjectTransactionEditor' => 'PhabricatorApplicationTransactionEditor', diff --git a/src/applications/project/config/PhabricatorProjectConfigOptions.php b/src/applications/project/config/PhabricatorProjectConfigOptions.php --- a/src/applications/project/config/PhabricatorProjectConfigOptions.php +++ b/src/applications/project/config/PhabricatorProjectConfigOptions.php @@ -83,6 +83,34 @@ $custom_field_type = 'custom:PhabricatorCustomFieldConfigOptionType'; + + $subtype_type = 'projects.subtypes'; + $subtype_default_key = PhabricatorEditEngineSubtype::SUBTYPE_DEFAULT; + $subtype_example = array( + array( + 'key' => $subtype_default_key, + 'name' => pht('Project'), + ), + array( + 'key' => 'team', + 'name' => pht('Team'), + ), + ); + $subtype_example = id(new PhutilJSON())->encodeAsList($subtype_example); + + $subtype_default = array( + array( + 'key' => $subtype_default_key, + 'name' => pht('Project'), + ), + ); + + $subtype_description = $this->deformat(pht(<<newOption('projects.custom-field-definitions', 'wild', array()) ->setSummary(pht('Custom Projects fields.')) @@ -102,6 +130,11 @@ $this->newOption('projects.colors', $colors_type, $default_colors) ->setSummary(pht('Adjust project colors.')) ->setDescription($colors_description), + $this->newOption('projects.subtypes', $subtype_type, $subtype_default) + ->setSummary(pht('Define project subtypes.')) + ->setDescription($subtype_description) + ->addExample($subtype_example, pht('Simple Subtypes')), + ); } diff --git a/src/applications/project/config/PhabricatorProjectSubtypesConfigType.php b/src/applications/project/config/PhabricatorProjectSubtypesConfigType.php new file mode 100644 --- /dev/null +++ b/src/applications/project/config/PhabricatorProjectSubtypesConfigType.php @@ -0,0 +1,14 @@ +renderWatchAction($project); $header->addActionLink($watch_action); + $subtype = $project->newSubtypeObject(); + if ($subtype && $subtype->hasTagView()) { + $subtype_tag = $subtype->newTagView(); + $header->addTag($subtype_tag); + } + $milestone_list = $this->buildMilestoneList($project); $subproject_list = $this->buildSubprojectList($project); diff --git a/src/applications/project/query/PhabricatorProjectQuery.php b/src/applications/project/query/PhabricatorProjectQuery.php --- a/src/applications/project/query/PhabricatorProjectQuery.php +++ b/src/applications/project/query/PhabricatorProjectQuery.php @@ -24,6 +24,7 @@ private $maxDepth; private $minMilestoneNumber; private $maxMilestoneNumber; + private $subtypes; private $status = 'status-any'; const STATUS_ANY = 'status-any'; @@ -131,6 +132,11 @@ return $this; } + public function withSubtypes(array $subtypes) { + $this->subtypes = $subtypes; + return $this; + } + public function needMembers($need_members) { $this->needMembers = $need_members; return $this; @@ -618,6 +624,13 @@ $this->maxMilestoneNumber); } + if ($this->subtypes !== null) { + $where[] = qsprintf( + $conn, + 'subtype IN (%Ls)', + $this->subtypes); + } + return $where; } diff --git a/src/applications/project/query/PhabricatorProjectSearchEngine.php b/src/applications/project/query/PhabricatorProjectSearchEngine.php --- a/src/applications/project/query/PhabricatorProjectSearchEngine.php +++ b/src/applications/project/query/PhabricatorProjectSearchEngine.php @@ -19,6 +19,9 @@ } protected function buildCustomSearchFields() { + $subtype_map = id(new PhabricatorProject())->newEditEngineSubtypeMap(); + $hide_subtypes = ($subtype_map->getCount() == 1); + return array( id(new PhabricatorSearchTextField()) ->setLabel(pht('Name')) @@ -62,6 +65,14 @@ pht( 'Pass true to find only milestones, or false to omit '. 'milestones.')), + id(new PhabricatorSearchDatasourceField()) + ->setLabel(pht('Subtypes')) + ->setKey('subtypes') + ->setAliases(array('subtype')) + ->setDescription( + pht('Search for projects with given subtypes.')) + ->setDatasource(new PhabricatorProjectSubtypeDatasource()) + ->setIsHidden($hide_subtypes), id(new PhabricatorSearchCheckboxesField()) ->setLabel(pht('Icons')) ->setKey('icons') @@ -134,6 +145,10 @@ $query->withAncestorProjectPHIDs($map['ancestorPHIDs']); } + if ($map['subtypes']) { + $query->withSubtypes($map['subtypes']); + } + return $query; } diff --git a/src/applications/project/storage/PhabricatorProject.php b/src/applications/project/storage/PhabricatorProject.php --- a/src/applications/project/storage/PhabricatorProject.php +++ b/src/applications/project/storage/PhabricatorProject.php @@ -12,7 +12,8 @@ PhabricatorFerretInterface, PhabricatorConduitResultInterface, PhabricatorColumnProxyInterface, - PhabricatorSpacesInterface { + PhabricatorSpacesInterface, + PhabricatorEditEngineSubtypeInterface { protected $name; protected $status = PhabricatorProjectStatus::STATUS_ACTIVE; @@ -40,6 +41,7 @@ protected $properties = array(); protected $spacePHID; + protected $subtype; private $memberPHIDs = self::ATTACHABLE; private $watcherPHIDs = self::ATTACHABLE; @@ -102,6 +104,7 @@ ->setHasWorkboard(0) ->setHasMilestones(0) ->setHasSubprojects(0) + ->setSubtype(PhabricatorEditEngineSubtype::SUBTYPE_DEFAULT) ->attachParentProject(null); } @@ -237,6 +240,7 @@ 'projectPath' => 'hashpath64', 'projectDepth' => 'uint32', 'projectPathKey' => 'bytes4', + 'subtype' => 'text64', ), self::CONFIG_KEY_SCHEMA => array( 'key_icon' => array( @@ -765,6 +769,10 @@ ->setKey('slug') ->setType('string') ->setDescription(pht('Primary slug/hashtag.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('subtype') + ->setType('string') + ->setDescription(pht('Subtype of the project.')), id(new PhabricatorConduitSearchFieldSpecification()) ->setKey('milestone') ->setType('int?') @@ -814,6 +822,7 @@ return array( 'name' => $this->getName(), 'slug' => $this->getPrimarySlug(), + 'subtype' => $this->getSubtype(), 'milestone' => $milestone, 'depth' => (int)$this->getProjectDepth(), 'parent' => $parent_ref, @@ -873,4 +882,26 @@ } +/* -( PhabricatorEditEngineSubtypeInterface )------------------------------ */ + + + public function getEditEngineSubtype() { + return $this->getSubtype(); + } + + public function setEditEngineSubtype($value) { + return $this->setSubtype($value); + } + + public function newEditEngineSubtypeMap() { + $config = PhabricatorEnv::getEnvConfig('projects.subtypes'); + return PhabricatorEditEngineSubtype::newSubtypeMap($config); + } + + public function newSubtypeObject() { + $subtype_key = $this->getEditEngineSubtype(); + $subtype_map = $this->newEditEngineSubtypeMap(); + return $subtype_map->getSubtype($subtype_key); + } + } diff --git a/src/applications/project/typeahead/PhabricatorProjectSubtypeDatasource.php b/src/applications/project/typeahead/PhabricatorProjectSubtypeDatasource.php new file mode 100644 --- /dev/null +++ b/src/applications/project/typeahead/PhabricatorProjectSubtypeDatasource.php @@ -0,0 +1,45 @@ +buildResults(); + return $this->filterResultsAgainstTokens($results); + } + + protected function renderSpecialTokens(array $values) { + return $this->renderTokensFromResults($this->buildResults(), $values); + } + + private function buildResults() { + $results = array(); + + $subtype_map = id(new PhabricatorProject())->newEditEngineSubtypeMap(); + foreach ($subtype_map->getSubtypes() as $key => $subtype) { + + $result = id(new PhabricatorTypeaheadResult()) + ->setIcon($subtype->getIcon()) + ->setColor($subtype->getColor()) + ->setPHID($key) + ->setName($subtype->getName()); + + $results[$key] = $result; + } + + return $results; + } + +} diff --git a/src/applications/project/view/PhabricatorProjectListView.php b/src/applications/project/view/PhabricatorProjectListView.php --- a/src/applications/project/view/PhabricatorProjectListView.php +++ b/src/applications/project/view/PhabricatorProjectListView.php @@ -87,6 +87,13 @@ } } + $subtype = $project->newSubtypeObject(); + if ($subtype && $subtype->hasTagView()) { + $subtype_tag = $subtype->newTagView() + ->setSlimShady(true); + $item->addAttribute($subtype_tag); + } + $list->addItem($item); }