diff --git a/src/applications/project/controller/PhabricatorProjectProfileController.php b/src/applications/project/controller/PhabricatorProjectProfileController.php --- a/src/applications/project/controller/PhabricatorProjectProfileController.php +++ b/src/applications/project/controller/PhabricatorProjectProfileController.php @@ -186,7 +186,9 @@ ->setName('#'.$slug->getSlug()); } - $view->addProperty(pht('Hashtags'), phutil_implode_html(' ', $hashtags)); + if ($hashtags) { + $view->addProperty(pht('Hashtags'), phutil_implode_html(' ', $hashtags)); + } $view->addProperty( pht('Members'), diff --git a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php --- a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php +++ b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php @@ -3,6 +3,17 @@ final class PhabricatorProjectTransactionEditor extends PhabricatorApplicationTransactionEditor { + private $isMilestone; + + private function setIsMilestone($is_milestone) { + $this->isMilestone = $is_milestone; + return $this; + } + + private function getIsMilestone() { + return $this->isMilestone; + } + public function getEditorApplicationClass() { return 'PhabricatorProjectApplication'; } @@ -91,7 +102,9 @@ case PhabricatorProjectTransaction::TYPE_NAME: $name = $xaction->getNewValue(); $object->setName($name); - $object->setPrimarySlug(PhabricatorSlug::normalizeProjectSlug($name)); + if ($this->getIsMilestone()) { + $object->setPrimarySlug(PhabricatorSlug::normalizeProjectSlug($name)); + } return; case PhabricatorProjectTransaction::TYPE_SLUGS: return; @@ -114,19 +127,7 @@ $object->setParentProjectPHID($xaction->getNewValue()); return; case PhabricatorProjectTransaction::TYPE_MILESTONE: - $current = queryfx_one( - $object->establishConnection('w'), - 'SELECT MAX(milestoneNumber) n - FROM %T - WHERE parentProjectPHID = %s', - $object->getTableName(), - $object->getParentProject()->getPHID()); - if (!$current) { - $number = 1; - } else { - $number = (int)$current['n'] + 1; - } - + $number = $object->getParentProject()->loadNextMilestoneNumber(); $object->setMilestoneNumber($number); $object->setParentProjectPHID($xaction->getNewValue()); return; @@ -275,16 +276,7 @@ } } - $is_milestone = $object->isMilestone(); - foreach ($xactions as $xaction) { - switch ($xaction->getTransactionType()) { - case PhabricatorProjectTransaction::TYPE_MILESTONE: - if ($xaction->getNewValue() !== null) { - $is_milestone = true; - } - break; - } - } + $is_milestone = $this->getIsMilestone(); $is_parent = $object->getHasSubprojects(); @@ -346,6 +338,10 @@ break; } + if ($this->getIsMilestone()) { + break; + } + $name = last($xactions)->getNewValue(); if (!PhabricatorSlug::isValidProjectSlug($name)) { @@ -358,20 +354,6 @@ break; } - $name_used_already = id(new PhabricatorProjectQuery()) - ->setViewer($this->getActor()) - ->withNames(array($name)) - ->executeOne(); - if ($name_used_already && - ($name_used_already->getPHID() != $object->getPHID())) { - $error = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Duplicate'), - pht('Project name is already used.'), - nonempty(last($xactions), null)); - $errors[] = $error; - } - $slug = PhabricatorSlug::normalizeProjectSlug($name); $slug_used_already = id(new PhabricatorProjectSlug()) @@ -381,7 +363,10 @@ $error = new PhabricatorApplicationTransactionValidationError( $type, pht('Duplicate'), - pht('Project name can not be used due to hashtag collision.'), + pht( + 'Project name generates the same hashtag ("%s") as another '. + 'existing project. Choose a unique name.', + '#'.$slug), nonempty(last($xactions), null)); $errors[] = $error; } @@ -883,6 +868,19 @@ } } + $is_milestone = $object->isMilestone(); + foreach ($xactions as $xaction) { + switch ($xaction->getTransactionType()) { + case PhabricatorProjectTransaction::TYPE_MILESTONE: + if ($xaction->getNewValue() !== null) { + $is_milestone = true; + } + break; + } + } + + $this->setIsMilestone($is_milestone); + return $results; } diff --git a/src/applications/project/engine/PhabricatorProjectEditEngine.php b/src/applications/project/engine/PhabricatorProjectEditEngine.php --- a/src/applications/project/engine/PhabricatorProjectEditEngine.php +++ b/src/applications/project/engine/PhabricatorProjectEditEngine.php @@ -43,7 +43,17 @@ } protected function newEditableObject() { - return PhabricatorProject::initializeNewProject($this->getViewer()); + $project = PhabricatorProject::initializeNewProject($this->getViewer()); + + $milestone = $this->getMilestoneProject(); + if ($milestone) { + $default_name = pht( + 'Milestone %s', + new PhutilNumber($milestone->loadNextMilestoneNumber())); + $project->setName($default_name); + } + + return $project; } protected function newObjectQuery() { @@ -92,6 +102,28 @@ ProjectCreateProjectsCapability::CAPABILITY); } + protected function willConfigureFields($object, array $fields) { + $is_milestone = ($this->getMilestoneProject() || $object->isMilestone()); + + $unavailable = array( + PhabricatorTransactions::TYPE_VIEW_POLICY, + PhabricatorTransactions::TYPE_EDIT_POLICY, + PhabricatorTransactions::TYPE_JOIN_POLICY, + ); + $unavailable = array_fuse($unavailable); + + if ($is_milestone) { + foreach ($fields as $key => $field) { + $xaction_type = $field->getTransactionType(); + if (isset($unavailable[$xaction_type])) { + unset($fields[$key]); + } + } + } + + return $fields; + } + protected function newBuiltinEngineConfigurations() { $configuration = head(parent::newBuiltinEngineConfigurations()); 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 @@ -211,21 +211,12 @@ 'projectPathKey' => 'bytes4', ), self::CONFIG_KEY_SCHEMA => array( - 'key_phid' => null, - 'phid' => array( - 'columns' => array('phid'), - 'unique' => true, - ), 'key_icon' => array( 'columns' => array('icon'), ), 'key_color' => array( 'columns' => array('color'), ), - 'name' => array( - 'columns' => array('name'), - 'unique' => true, - ), 'key_milestone' => array( 'columns' => array('parentProjectPHID', 'milestoneNumber'), 'unique' => true, @@ -475,6 +466,24 @@ return true; } + public function loadNextMilestoneNumber() { + $current = queryfx_one( + $this->establishConnection('w'), + 'SELECT MAX(milestoneNumber) n + FROM %T + WHERE parentProjectPHID = %s', + $this->getTableName(), + $this->getPHID()); + + if (!$current) { + $number = 1; + } else { + $number = (int)$current['n'] + 1; + } + + return $number; + } + /* -( PhabricatorSubscribableInterface )----------------------------------- */ diff --git a/src/applications/transactions/editengine/PhabricatorEditEngine.php b/src/applications/transactions/editengine/PhabricatorEditEngine.php --- a/src/applications/transactions/editengine/PhabricatorEditEngine.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngine.php @@ -64,10 +64,6 @@ abstract public function getEngineApplicationClass(); abstract protected function buildCustomEditFields($object); - protected function didBuildCustomEditFields($object, array $fields) { - return; - } - public function getFieldsForConfig( PhabricatorEditEngineConfiguration $config) { @@ -93,7 +89,6 @@ } $fields = mpull($fields, null, 'getKey'); - $this->didBuildCustomEditFields($object, $fields); $extensions = PhabricatorEditEngineExtension::getAllEnabledExtensions(); foreach ($extensions as $extension) { @@ -115,7 +110,6 @@ } $extension_fields = mpull($extension_fields, null, 'getKey'); - $extension->didBuildCustomEditFields($this, $object, $extension_fields); foreach ($extension_fields as $key => $field) { $fields[$key] = $field; @@ -123,11 +117,16 @@ } $config = $this->getEditEngineConfiguration(); + $fields = $this->willConfigureFields($object, $fields); $fields = $config->applyConfigurationToFields($this, $object, $fields); return $fields; } + protected function willConfigureFields($object, array $fields) { + return $fields; + } + /* -( Display Text )------------------------------------------------------- */ diff --git a/src/applications/transactions/engineextension/PhabricatorEditEngineExtension.php b/src/applications/transactions/engineextension/PhabricatorEditEngineExtension.php --- a/src/applications/transactions/engineextension/PhabricatorEditEngineExtension.php +++ b/src/applications/transactions/engineextension/PhabricatorEditEngineExtension.php @@ -32,13 +32,6 @@ PhabricatorEditEngine $engine, PhabricatorApplicationTransactionInterface $object); - public function didBuildCustomEditFields( - PhabricatorEditEngine $engine, - PhabricatorApplicationTransactionInterface $object, - array $fields) { - return; - } - final public static function getAllExtensions() { return id(new PhutilClassMapQuery()) ->setAncestorClass(__CLASS__)