true, self::CONFIG_SERIALIZATION => array( 'subprojectPHIDs' => self::SERIALIZATION_JSON, ), ) + parent::getConfiguration(); } public function generatePHID() { return PhabricatorPHID::generateNewPHID( PhabricatorPHIDConstants::PHID_TYPE_PROJ); } public function setSubprojectPHIDs(array $phids) { $this->subprojectPHIDs = $phids; $this->subprojectsNeedUpdate = true; return $this; } public function loadProfile() { $profile = id(new PhabricatorProjectProfile())->loadOneWhere( 'projectPHID = %s', $this->getPHID()); return $profile; } public function getAffiliations() { if ($this->affiliations === null) { throw new Exception('Attach affiliations first!'); } return $this->affiliations; } public function attachAffiliations(array $affiliations) { assert_instances_of($affiliations, 'PhabricatorProjectAffiliation'); $this->affiliations = $affiliations; return $this; } public function loadAffiliations() { $affils = PhabricatorProjectAffiliation::loadAllForProjectPHIDs( array($this->getPHID())); return $affils[$this->getPHID()]; } public function setPhrictionSlug($slug) { // NOTE: We're doing a little magic here and stripping out '/' so that // project pages always appear at top level under projects/ even if the // display name is "Hack / Slash" or similar (it will become // 'hack_slash' instead of 'hack/slash'). $slug = str_replace('/', ' ', $slug); $slug = PhabricatorSlug::normalize($slug); $this->phrictionSlug = $slug; return $this; } public function save() { $result = parent::save(); if ($this->subprojectsNeedUpdate) { // If we've changed the project PHIDs for this task, update the link // table. PhabricatorProjectSubproject::updateProjectSubproject($this); $this->subprojectsNeedUpdate = false; } return $result; } }