From aeae0e702830c1e2ba762afe63e761b7ea00739f Mon Sep 17 00:00:00 2001 From: epriestley Date: Sat, 19 Dec 2015 08:08:26 -0800 Subject: [PATCH] Prepare Projects schema for subprojects Summary: Ref T10010. This does some cleanups on the schema: - `viewPolicy`, `editPolicy` and `joinPolicy` were nullable, but should never be `null`. Set them to defaults if they're null, then make the column non-nullable. - Rename `phrictionSlug` to `primarySlug` and stop adding and removing trailing slashes from it. - Add new columns to support milestones and non-milestone subprojects. - Drop very old subprojectPHIDs column. This hasn't done anything in the UI for years and years, and isn't particularly realistic to migrate forward. The new columns aren't reachable from the UI. Test Plan: - Applied patches. - Grepped for `phrictionSlug`. - Grepped for `subprojectPHIDs`. - Created tasks. - Edited tasks. - Verified existing tasks still had primary slugs. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10010 Differential Revision: https://secure.phabricator.com/D14825 --- .../autopatches/20151219.proj.01.prislug.sql | 2 + .../20151219.proj.02.prislugkey.sql | 2 + .../autopatches/20151219.proj.03.copyslug.sql | 2 + .../20151219.proj.04.dropslugkey.sql | 2 + .../autopatches/20151219.proj.05.dropslug.sql | 2 + .../20151219.proj.06.defaultpolicy.php | 28 ++++++++ .../autopatches/20151219.proj.07.viewnull.sql | 2 + .../autopatches/20151219.proj.08.editnull.sql | 2 + .../autopatches/20151219.proj.09.joinnull.sql | 2 + .../20151219.proj.10.subcolumns.sql | 17 +++++ .../20151219.proj.11.subprojectphids.sql | 2 + .../PhabricatorPolicyDataTestCase.php | 12 +--- .../project/storage/PhabricatorProject.php | 70 +++++++++++-------- 13 files changed, 105 insertions(+), 40 deletions(-) create mode 100644 resources/sql/autopatches/20151219.proj.01.prislug.sql create mode 100644 resources/sql/autopatches/20151219.proj.02.prislugkey.sql create mode 100644 resources/sql/autopatches/20151219.proj.03.copyslug.sql create mode 100644 resources/sql/autopatches/20151219.proj.04.dropslugkey.sql create mode 100644 resources/sql/autopatches/20151219.proj.05.dropslug.sql create mode 100644 resources/sql/autopatches/20151219.proj.06.defaultpolicy.php create mode 100644 resources/sql/autopatches/20151219.proj.07.viewnull.sql create mode 100644 resources/sql/autopatches/20151219.proj.08.editnull.sql create mode 100644 resources/sql/autopatches/20151219.proj.09.joinnull.sql create mode 100644 resources/sql/autopatches/20151219.proj.10.subcolumns.sql create mode 100644 resources/sql/autopatches/20151219.proj.11.subprojectphids.sql diff --git a/resources/sql/autopatches/20151219.proj.01.prislug.sql b/resources/sql/autopatches/20151219.proj.01.prislug.sql new file mode 100644 index 0000000000..8001dd756c --- /dev/null +++ b/resources/sql/autopatches/20151219.proj.01.prislug.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_project.project + ADD COLUMN primarySlug VARCHAR(128) COLLATE {$COLLATE_TEXT}; diff --git a/resources/sql/autopatches/20151219.proj.02.prislugkey.sql b/resources/sql/autopatches/20151219.proj.02.prislugkey.sql new file mode 100644 index 0000000000..a1dbf6ff99 --- /dev/null +++ b/resources/sql/autopatches/20151219.proj.02.prislugkey.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_project.project + ADD UNIQUE KEY `key_primaryslug` (primarySlug); diff --git a/resources/sql/autopatches/20151219.proj.03.copyslug.sql b/resources/sql/autopatches/20151219.proj.03.copyslug.sql new file mode 100644 index 0000000000..2b954c6cdc --- /dev/null +++ b/resources/sql/autopatches/20151219.proj.03.copyslug.sql @@ -0,0 +1,2 @@ +UPDATE {$NAMESPACE}_project.project + SET primarySlug = TRIM(TRAILING "/" FROM phrictionSlug); diff --git a/resources/sql/autopatches/20151219.proj.04.dropslugkey.sql b/resources/sql/autopatches/20151219.proj.04.dropslugkey.sql new file mode 100644 index 0000000000..bfe1f2eb4b --- /dev/null +++ b/resources/sql/autopatches/20151219.proj.04.dropslugkey.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_project.project + DROP KEY `phrictionSlug`; diff --git a/resources/sql/autopatches/20151219.proj.05.dropslug.sql b/resources/sql/autopatches/20151219.proj.05.dropslug.sql new file mode 100644 index 0000000000..924f31ff95 --- /dev/null +++ b/resources/sql/autopatches/20151219.proj.05.dropslug.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_project.project + DROP COLUMN phrictionSlug; diff --git a/resources/sql/autopatches/20151219.proj.06.defaultpolicy.php b/resources/sql/autopatches/20151219.proj.06.defaultpolicy.php new file mode 100644 index 0000000000..e3548d026c --- /dev/null +++ b/resources/sql/autopatches/20151219.proj.06.defaultpolicy.php @@ -0,0 +1,28 @@ +getPolicy(ProjectDefaultViewCapability::CAPABILITY); +$edit_policy = $app->getPolicy(ProjectDefaultEditCapability::CAPABILITY); +$join_policy = $app->getPolicy(ProjectDefaultJoinCapability::CAPABILITY); + +$table = new PhabricatorProject(); +$conn_w = $table->establishConnection('w'); + +queryfx( + $conn_w, + 'UPDATE %T SET viewPolicy = %s WHERE viewPolicy IS NULL', + $table->getTableName(), + $view_policy); + +queryfx( + $conn_w, + 'UPDATE %T SET editPolicy = %s WHERE editPolicy IS NULL', + $table->getTableName(), + $edit_policy); + +queryfx( + $conn_w, + 'UPDATE %T SET joinPolicy = %s WHERE joinPolicy IS NULL', + $table->getTableName(), + $join_policy); diff --git a/resources/sql/autopatches/20151219.proj.07.viewnull.sql b/resources/sql/autopatches/20151219.proj.07.viewnull.sql new file mode 100644 index 0000000000..0e5539fc21 --- /dev/null +++ b/resources/sql/autopatches/20151219.proj.07.viewnull.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_project.project + CHANGE viewPolicy viewPolicy VARBINARY(64) NOT NULL; diff --git a/resources/sql/autopatches/20151219.proj.08.editnull.sql b/resources/sql/autopatches/20151219.proj.08.editnull.sql new file mode 100644 index 0000000000..362e72bf4f --- /dev/null +++ b/resources/sql/autopatches/20151219.proj.08.editnull.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_project.project + CHANGE editPolicy editPolicy VARBINARY(64) NOT NULL; diff --git a/resources/sql/autopatches/20151219.proj.09.joinnull.sql b/resources/sql/autopatches/20151219.proj.09.joinnull.sql new file mode 100644 index 0000000000..9517ed54f7 --- /dev/null +++ b/resources/sql/autopatches/20151219.proj.09.joinnull.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_project.project + CHANGE joinPolicy joinPolicy VARBINARY(64) NOT NULL; diff --git a/resources/sql/autopatches/20151219.proj.10.subcolumns.sql b/resources/sql/autopatches/20151219.proj.10.subcolumns.sql new file mode 100644 index 0000000000..ea6725a8ab --- /dev/null +++ b/resources/sql/autopatches/20151219.proj.10.subcolumns.sql @@ -0,0 +1,17 @@ +ALTER TABLE {$NAMESPACE}_project.project + ADD parentProjectPHID VARBINARY(64); + +ALTER TABLE {$NAMESPACE}_project.project + ADD hasWorkboard BOOL NOT NULL; + +ALTER TABLE {$NAMESPACE}_project.project + ADD hasMilestones BOOL NOT NULL; + +ALTER TABLE {$NAMESPACE}_project.project + ADD hasSubprojects BOOL NOT NULL; + +ALTER TABLE {$NAMESPACE}_project.project + ADD milestoneNumber INT UNSIGNED; + +ALTER TABLE {$NAMESPACE}_project.project + ADD UNIQUE KEY `key_milestone` (parentProjectPHID, milestoneNumber); diff --git a/resources/sql/autopatches/20151219.proj.11.subprojectphids.sql b/resources/sql/autopatches/20151219.proj.11.subprojectphids.sql new file mode 100644 index 0000000000..4e51b1358d --- /dev/null +++ b/resources/sql/autopatches/20151219.proj.11.subprojectphids.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_project.project + DROP subprojectPHIDs; diff --git a/src/applications/policy/__tests__/PhabricatorPolicyDataTestCase.php b/src/applications/policy/__tests__/PhabricatorPolicyDataTestCase.php index f68eae2b49..ad44b6a56e 100644 --- a/src/applications/policy/__tests__/PhabricatorPolicyDataTestCase.php +++ b/src/applications/policy/__tests__/PhabricatorPolicyDataTestCase.php @@ -11,19 +11,11 @@ final class PhabricatorPolicyDataTestCase extends PhabricatorTestCase { public function testProjectPolicyMembership() { $author = $this->generateNewTestUser(); - $proj_a = id(new PhabricatorProject()) + $proj_a = PhabricatorProject::initializeNewProject($author) ->setName('A') - ->setAuthorPHID($author->getPHID()) - ->setIcon(PhabricatorProject::DEFAULT_ICON) - ->setColor(PhabricatorProject::DEFAULT_COLOR) - ->setIsMembershipLocked(0) ->save(); - $proj_b = id(new PhabricatorProject()) + $proj_b = PhabricatorProject::initializeNewProject($author) ->setName('B') - ->setAuthorPHID($author->getPHID()) - ->setIcon(PhabricatorProject::DEFAULT_ICON) - ->setColor(PhabricatorProject::DEFAULT_COLOR) - ->setIsMembershipLocked(0) ->save(); $proj_a->setViewPolicy($proj_b->getPHID())->save(); diff --git a/src/applications/project/storage/PhabricatorProject.php b/src/applications/project/storage/PhabricatorProject.php index 8bbc6e14ad..8e4f3fbfc9 100644 --- a/src/applications/project/storage/PhabricatorProject.php +++ b/src/applications/project/storage/PhabricatorProject.php @@ -12,8 +12,7 @@ final class PhabricatorProject extends PhabricatorProjectDAO protected $name; protected $status = PhabricatorProjectStatus::STATUS_ACTIVE; protected $authorPHID; - protected $subprojectPHIDs = array(); - protected $phrictionSlug; + protected $primarySlug; protected $profileImagePHID; protected $icon; protected $color; @@ -24,6 +23,12 @@ final class PhabricatorProject extends PhabricatorProjectDAO protected $joinPolicy; protected $isMembershipLocked; + protected $parentProjectPHID; + protected $hasWorkboard; + protected $hasMilestones; + protected $hasSubprojects; + protected $milestoneNumber; + private $memberPHIDs = self::ATTACHABLE; private $watcherPHIDs = self::ATTACHABLE; private $sparseWatchers = self::ATTACHABLE; @@ -31,6 +36,7 @@ final class PhabricatorProject extends PhabricatorProjectDAO private $customFields = self::ATTACHABLE; private $profileImageFile = self::ATTACHABLE; private $slugs = self::ATTACHABLE; + private $parentProject = self::ATTACHABLE; const DEFAULT_ICON = 'fa-briefcase'; const DEFAULT_COLOR = 'blue'; @@ -59,7 +65,10 @@ final class PhabricatorProject extends PhabricatorProjectDAO ->setJoinPolicy($join_policy) ->setIsMembershipLocked(0) ->attachMemberPHIDs(array()) - ->attachSlugs(array()); + ->attachSlugs(array()) + ->setHasWorkboard(0) + ->setHasMilestones(0) + ->setHasSubprojects(0); } public function getCapabilities() { @@ -132,24 +141,21 @@ final class PhabricatorProject extends PhabricatorProjectDAO protected function getConfiguration() { return array( self::CONFIG_AUX_PHID => true, - self::CONFIG_SERIALIZATION => array( - 'subprojectPHIDs' => self::SERIALIZATION_JSON, - ), self::CONFIG_COLUMN_SCHEMA => array( 'name' => 'sort128', 'status' => 'text32', - 'phrictionSlug' => 'text128?', + 'primarySlug' => 'text128?', 'isMembershipLocked' => 'bool', 'profileImagePHID' => 'phid?', 'icon' => 'text32', 'color' => 'text32', 'mailKey' => 'bytes20', - - // T6203/NULLABILITY - // These are definitely wrong and should always exist. - 'editPolicy' => 'policy?', - 'viewPolicy' => 'policy?', - 'joinPolicy' => 'policy?', + 'joinPolicy' => 'policy', + 'parentProjectPHID' => 'phid?', + 'hasWorkboard' => 'bool', + 'hasMilestones' => 'bool', + 'hasSubprojects' => 'bool', + 'milestoneNumber' => 'uint32?', ), self::CONFIG_KEY_SCHEMA => array( 'key_phid' => null, @@ -163,14 +169,18 @@ final class PhabricatorProject extends PhabricatorProjectDAO 'key_color' => array( 'columns' => array('color'), ), - 'phrictionSlug' => array( - 'columns' => array('phrictionSlug'), - 'unique' => true, - ), 'name' => array( 'columns' => array('name'), 'unique' => true, ), + 'key_milestone' => array( + 'columns' => array('parentProjectPHID', 'milestoneNumber'), + 'unique' => true, + ), + 'key_primaryslug' => array( + 'columns' => array('primarySlug'), + 'unique' => true, + ), ), ) + parent::getConfiguration(); } @@ -189,19 +199,6 @@ final class PhabricatorProject extends PhabricatorProjectDAO return $this->assertAttached($this->memberPHIDs); } - public function setPrimarySlug($slug) { - $this->phrictionSlug = $slug.'/'; - return $this; - } - - // TODO - once we sever project => phriction automagicalness, - // migrate getPhrictionSlug to have no trailing slash and be called - // getPrimarySlug - public function getPrimarySlug() { - $slug = $this->getPhrictionSlug(); - return rtrim($slug, '/'); - } - public function isArchived() { return ($this->getStatus() == PhabricatorProjectStatus::STATUS_ARCHIVED); } @@ -313,6 +310,19 @@ final class PhabricatorProject extends PhabricatorProjectDAO $this->saveTransaction(); } + public function isMilestone() { + return ($this->getMilestoneNumber() !== null); + } + + public function getParentProject() { + return $this->assertAttached($this->parentProject); + } + + public function attachParentProject(PhabricatorProject $project) { + $this->parentProject = $project; + return $this; + } + /* -( PhabricatorSubscribableInterface )----------------------------------- */