2011-02-21 03:41:23 +01:00
|
|
|
<?php
|
|
|
|
|
2012-08-09 02:10:10 +02:00
|
|
|
final class PhabricatorProject extends PhabricatorProjectDAO
|
2013-10-25 21:52:00 +02:00
|
|
|
implements
|
|
|
|
PhabricatorFlaggableInterface,
|
Make Projects a PhabricatorSubscribableInterface, but with restricted defaults
Summary:
Ref T4379. I want project subscriptions to work like this (yell if this seems whacky, since it makes subscriptions mean somethign a little different for projects than they do for other objects):
- You can only subscribe to a project if you're a project member.
- When you're added as a member, you're added as a subscriber.
- When you're removed as a member, you're removed as a subscriber.
- While you're a member, you can optionally unsubscribe.
From a UI perspective:
- We don't show the subscriber list, since it's going to be some uninteresting subset of the member list.
- We don't show CC transactions in history, since they're an uninteresting near-approximation of the membership transactions.
- You only see the subscription controls if you're a member.
To do this, I've augmented `PhabricatorSubscribableInterface` with two new methods. It would be nice if we were on PHP 5.4+ and could just use traits for this, but we should get data about version usage before we think about this. For now, copy/paste the default implementations into every implementing class.
Then, I implemented the interface in `PhabricatorProject` but with alternate defaults.
Test Plan:
- Used the normal interaction on existing objects.
- This has no actual effect on projects, verified no subscription stuff mysteriously appeared.
- Hit the new error case by fiddling with the UI.
Reviewers: btrahan
Reviewed By: btrahan
CC: chad, aran
Maniphest Tasks: T4379
Differential Revision: https://secure.phabricator.com/D8165
2014-02-10 23:29:17 +01:00
|
|
|
PhabricatorPolicyInterface,
|
2014-02-10 23:31:34 +01:00
|
|
|
PhabricatorSubscribableInterface,
|
|
|
|
PhabricatorCustomFieldInterface {
|
2011-02-21 03:41:23 +01:00
|
|
|
|
|
|
|
protected $name;
|
2012-02-07 23:59:38 +01:00
|
|
|
protected $status = PhabricatorProjectStatus::STATUS_ACTIVE;
|
2011-02-21 03:41:23 +01:00
|
|
|
protected $authorPHID;
|
2011-07-19 20:50:15 +02:00
|
|
|
protected $subprojectPHIDs = array();
|
Provide wiki pages for projects
Summary:
Provide tighter integration between Projects and Phriction. Partly, I have most
of a rewrite for the Projects homepage ready but it's not currently possible to
publish feed stories about a project so all the feeds are empty/boring. This
partly makes them more useful and partly just provides a tool integration point.
- When you create a project, all the wiki pages in projects/<project_name>/*
are associated with it.
- Publish updates to those pages as being related to the project so they'll
show up in project feeds.
- Show a project link on those pages.
This is very "convention over configuration" but I think it's the right
approach. We could provide some sort of, like, "@project=derp" tag to let you
associated arbitrary pages to projects later, but just letting you move pages is
probably far better.
Test Plan:
- Ran upgrade scripts against stupidly named projects ("der", " der", " der
", "der (2)", " der (2) (2)", etc). Ended up with uniquely named projects.
- Ran unit tests.
- Created /projects/ wiki documents and made sure they displayed correctly.
- Verified feed stories publish as project-related.
- Edited projects, including perfomring a name-colliding edit.
- Created projects, including performing a name-colliding create.
Reviewers: btrahan, jungejason
Reviewed By: btrahan
CC: aran, epriestley, btrahan
Maniphest Tasks: T681
Differential Revision: 1231
2011-12-17 20:58:55 +01:00
|
|
|
protected $phrictionSlug;
|
Migrate project profiles onto projects, and remove ProjectProfile object
Summary:
Ref T4379. Long ago, the "Project" vs "ProjectProfile" split was intended to allow a bunch of special fields on projects without burdening the simple use cases, but CustomField handles that far better and far more generally, and doing this makes using ApplicationTransactions a pain to get right, so get rid of it.
The only remaining field is `profileImagePHID`, which we can just move to the main Project object. This is custom enough that I think it's reasonable not to express it as a custom field.
Test Plan: Created a project, set profile, edited project, viewed in typeahead, ran migration, verified database results.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T4379
Differential Revision: https://secure.phabricator.com/D8183
2014-02-10 23:32:14 +01:00
|
|
|
protected $profileImagePHID;
|
2014-05-23 19:41:24 +02:00
|
|
|
protected $icon;
|
2011-07-19 20:50:15 +02:00
|
|
|
|
2012-08-11 16:05:01 +02:00
|
|
|
protected $viewPolicy;
|
|
|
|
protected $editPolicy;
|
|
|
|
protected $joinPolicy;
|
|
|
|
|
2013-09-03 15:02:14 +02:00
|
|
|
private $memberPHIDs = self::ATTACHABLE;
|
2014-05-19 21:40:57 +02:00
|
|
|
private $watcherPHIDs = self::ATTACHABLE;
|
|
|
|
private $sparseWatchers = self::ATTACHABLE;
|
2013-09-03 15:02:14 +02:00
|
|
|
private $sparseMembers = self::ATTACHABLE;
|
2014-02-10 23:31:34 +01:00
|
|
|
private $customFields = self::ATTACHABLE;
|
Migrate project profiles onto projects, and remove ProjectProfile object
Summary:
Ref T4379. Long ago, the "Project" vs "ProjectProfile" split was intended to allow a bunch of special fields on projects without burdening the simple use cases, but CustomField handles that far better and far more generally, and doing this makes using ApplicationTransactions a pain to get right, so get rid of it.
The only remaining field is `profileImagePHID`, which we can just move to the main Project object. This is custom enough that I think it's reasonable not to express it as a custom field.
Test Plan: Created a project, set profile, edited project, viewed in typeahead, ran migration, verified database results.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T4379
Differential Revision: https://secure.phabricator.com/D8183
2014-02-10 23:32:14 +01:00
|
|
|
private $profileImageFile = self::ATTACHABLE;
|
2014-05-22 20:19:03 +02:00
|
|
|
private $slugs = self::ATTACHABLE;
|
2011-02-21 03:41:23 +01:00
|
|
|
|
2014-05-23 19:41:24 +02:00
|
|
|
const DEFAULT_ICON = 'fa-briefcase';
|
|
|
|
|
2014-02-10 23:30:17 +01:00
|
|
|
public static function initializeNewProject(PhabricatorUser $actor) {
|
|
|
|
return id(new PhabricatorProject())
|
|
|
|
->setName('')
|
|
|
|
->setAuthorPHID($actor->getPHID())
|
2014-05-23 19:41:24 +02:00
|
|
|
->setIcon(self::DEFAULT_ICON)
|
2014-02-10 23:30:17 +01:00
|
|
|
->setViewPolicy(PhabricatorPolicies::POLICY_USER)
|
|
|
|
->setEditPolicy(PhabricatorPolicies::POLICY_USER)
|
|
|
|
->setJoinPolicy(PhabricatorPolicies::POLICY_USER)
|
|
|
|
->attachMemberPHIDs(array());
|
|
|
|
}
|
|
|
|
|
2012-08-09 02:10:10 +02:00
|
|
|
public function getCapabilities() {
|
|
|
|
return array(
|
|
|
|
PhabricatorPolicyCapability::CAN_VIEW,
|
2012-08-11 16:05:01 +02:00
|
|
|
PhabricatorPolicyCapability::CAN_EDIT,
|
|
|
|
PhabricatorPolicyCapability::CAN_JOIN,
|
2012-08-09 02:10:10 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getPolicy($capability) {
|
2012-08-11 16:05:01 +02:00
|
|
|
switch ($capability) {
|
|
|
|
case PhabricatorPolicyCapability::CAN_VIEW:
|
|
|
|
return $this->getViewPolicy();
|
|
|
|
case PhabricatorPolicyCapability::CAN_EDIT:
|
|
|
|
return $this->getEditPolicy();
|
|
|
|
case PhabricatorPolicyCapability::CAN_JOIN:
|
|
|
|
return $this->getJoinPolicy();
|
|
|
|
}
|
2012-08-09 02:10:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
2012-08-11 16:05:01 +02:00
|
|
|
|
|
|
|
switch ($capability) {
|
|
|
|
case PhabricatorPolicyCapability::CAN_VIEW:
|
|
|
|
if ($this->isUserMember($viewer->getPHID())) {
|
|
|
|
// Project members can always view a project.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case PhabricatorPolicyCapability::CAN_EDIT:
|
|
|
|
break;
|
|
|
|
case PhabricatorPolicyCapability::CAN_JOIN:
|
|
|
|
$can_edit = PhabricatorPolicyCapability::CAN_EDIT;
|
|
|
|
if (PhabricatorPolicyFilter::hasCapability($viewer, $this, $can_edit)) {
|
|
|
|
// Project editors can always join a project.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-08-09 02:10:10 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-09-27 17:43:41 +02:00
|
|
|
public function describeAutomaticCapability($capability) {
|
|
|
|
switch ($capability) {
|
|
|
|
case PhabricatorPolicyCapability::CAN_VIEW:
|
|
|
|
return pht("Members of a project can always view it.");
|
|
|
|
case PhabricatorPolicyCapability::CAN_JOIN:
|
|
|
|
return pht("Users who can edit a project can always join it.");
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2012-08-11 16:05:01 +02:00
|
|
|
public function isUserMember($user_phid) {
|
2014-02-10 23:30:17 +01:00
|
|
|
if ($this->memberPHIDs !== self::ATTACHABLE) {
|
|
|
|
return in_array($user_phid, $this->memberPHIDs);
|
|
|
|
}
|
2013-09-03 15:02:14 +02:00
|
|
|
return $this->assertAttachedKey($this->sparseMembers, $user_phid);
|
2012-08-11 16:05:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public function setIsUserMember($user_phid, $is_member) {
|
2013-09-03 15:02:14 +02:00
|
|
|
if ($this->sparseMembers === self::ATTACHABLE) {
|
|
|
|
$this->sparseMembers = array();
|
|
|
|
}
|
2012-08-11 16:05:01 +02:00
|
|
|
$this->sparseMembers[$user_phid] = $is_member;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2011-02-21 03:41:23 +01:00
|
|
|
public function getConfiguration() {
|
|
|
|
return array(
|
|
|
|
self::CONFIG_AUX_PHID => true,
|
2011-07-19 20:50:15 +02:00
|
|
|
self::CONFIG_SERIALIZATION => array(
|
|
|
|
'subprojectPHIDs' => self::SERIALIZATION_JSON,
|
|
|
|
),
|
2011-02-21 03:41:23 +01:00
|
|
|
) + parent::getConfiguration();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function generatePHID() {
|
2011-03-03 03:58:21 +01:00
|
|
|
return PhabricatorPHID::generateNewPHID(
|
2013-07-22 18:26:26 +02:00
|
|
|
PhabricatorProjectPHIDTypeProject::TYPECONST);
|
2011-02-21 03:41:23 +01:00
|
|
|
}
|
|
|
|
|
2012-08-08 03:02:05 +02:00
|
|
|
public function attachMemberPHIDs(array $phids) {
|
|
|
|
$this->memberPHIDs = $phids;
|
|
|
|
return $this;
|
2012-08-07 20:55:00 +02:00
|
|
|
}
|
|
|
|
|
2012-08-08 03:02:05 +02:00
|
|
|
public function getMemberPHIDs() {
|
2013-09-03 15:02:14 +02:00
|
|
|
return $this->assertAttached($this->memberPHIDs);
|
2012-01-18 01:29:35 +01:00
|
|
|
}
|
|
|
|
|
Provide wiki pages for projects
Summary:
Provide tighter integration between Projects and Phriction. Partly, I have most
of a rewrite for the Projects homepage ready but it's not currently possible to
publish feed stories about a project so all the feeds are empty/boring. This
partly makes them more useful and partly just provides a tool integration point.
- When you create a project, all the wiki pages in projects/<project_name>/*
are associated with it.
- Publish updates to those pages as being related to the project so they'll
show up in project feeds.
- Show a project link on those pages.
This is very "convention over configuration" but I think it's the right
approach. We could provide some sort of, like, "@project=derp" tag to let you
associated arbitrary pages to projects later, but just letting you move pages is
probably far better.
Test Plan:
- Ran upgrade scripts against stupidly named projects ("der", " der", " der
", "der (2)", " der (2) (2)", etc). Ended up with uniquely named projects.
- Ran unit tests.
- Created /projects/ wiki documents and made sure they displayed correctly.
- Verified feed stories publish as project-related.
- Edited projects, including perfomring a name-colliding edit.
- Created projects, including performing a name-colliding create.
Reviewers: btrahan, jungejason
Reviewed By: btrahan
CC: aran, epriestley, btrahan
Maniphest Tasks: T681
Differential Revision: 1231
2011-12-17 20:58:55 +01:00
|
|
|
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);
|
2012-04-10 23:18:20 +02:00
|
|
|
$slug = PhabricatorSlug::normalize($slug);
|
Provide wiki pages for projects
Summary:
Provide tighter integration between Projects and Phriction. Partly, I have most
of a rewrite for the Projects homepage ready but it's not currently possible to
publish feed stories about a project so all the feeds are empty/boring. This
partly makes them more useful and partly just provides a tool integration point.
- When you create a project, all the wiki pages in projects/<project_name>/*
are associated with it.
- Publish updates to those pages as being related to the project so they'll
show up in project feeds.
- Show a project link on those pages.
This is very "convention over configuration" but I think it's the right
approach. We could provide some sort of, like, "@project=derp" tag to let you
associated arbitrary pages to projects later, but just letting you move pages is
probably far better.
Test Plan:
- Ran upgrade scripts against stupidly named projects ("der", " der", " der
", "der (2)", " der (2) (2)", etc). Ended up with uniquely named projects.
- Ran unit tests.
- Created /projects/ wiki documents and made sure they displayed correctly.
- Verified feed stories publish as project-related.
- Edited projects, including perfomring a name-colliding edit.
- Created projects, including performing a name-colliding create.
Reviewers: btrahan, jungejason
Reviewed By: btrahan
CC: aran, epriestley, btrahan
Maniphest Tasks: T681
Differential Revision: 1231
2011-12-17 20:58:55 +01:00
|
|
|
$this->phrictionSlug = $slug;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2013-07-18 01:43:37 +02:00
|
|
|
public function getFullPhrictionSlug() {
|
|
|
|
$slug = $this->getPhrictionSlug();
|
|
|
|
return 'projects/'.$slug;
|
|
|
|
}
|
|
|
|
|
2014-05-22 20:19:03 +02:00
|
|
|
// 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, '/');
|
|
|
|
}
|
|
|
|
|
2014-02-10 23:30:47 +01:00
|
|
|
public function isArchived() {
|
|
|
|
return ($this->getStatus() == PhabricatorProjectStatus::STATUS_ARCHIVED);
|
|
|
|
}
|
|
|
|
|
Migrate project profiles onto projects, and remove ProjectProfile object
Summary:
Ref T4379. Long ago, the "Project" vs "ProjectProfile" split was intended to allow a bunch of special fields on projects without burdening the simple use cases, but CustomField handles that far better and far more generally, and doing this makes using ApplicationTransactions a pain to get right, so get rid of it.
The only remaining field is `profileImagePHID`, which we can just move to the main Project object. This is custom enough that I think it's reasonable not to express it as a custom field.
Test Plan: Created a project, set profile, edited project, viewed in typeahead, ran migration, verified database results.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T4379
Differential Revision: https://secure.phabricator.com/D8183
2014-02-10 23:32:14 +01:00
|
|
|
public function getProfileImageURI() {
|
|
|
|
return $this->getProfileImageFile()->getBestURI();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function attachProfileImageFile(PhabricatorFile $file) {
|
|
|
|
$this->profileImageFile = $file;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getProfileImageFile() {
|
|
|
|
return $this->assertAttached($this->profileImageFile);
|
|
|
|
}
|
|
|
|
|
Make Projects a PhabricatorSubscribableInterface, but with restricted defaults
Summary:
Ref T4379. I want project subscriptions to work like this (yell if this seems whacky, since it makes subscriptions mean somethign a little different for projects than they do for other objects):
- You can only subscribe to a project if you're a project member.
- When you're added as a member, you're added as a subscriber.
- When you're removed as a member, you're removed as a subscriber.
- While you're a member, you can optionally unsubscribe.
From a UI perspective:
- We don't show the subscriber list, since it's going to be some uninteresting subset of the member list.
- We don't show CC transactions in history, since they're an uninteresting near-approximation of the membership transactions.
- You only see the subscription controls if you're a member.
To do this, I've augmented `PhabricatorSubscribableInterface` with two new methods. It would be nice if we were on PHP 5.4+ and could just use traits for this, but we should get data about version usage before we think about this. For now, copy/paste the default implementations into every implementing class.
Then, I implemented the interface in `PhabricatorProject` but with alternate defaults.
Test Plan:
- Used the normal interaction on existing objects.
- This has no actual effect on projects, verified no subscription stuff mysteriously appeared.
- Hit the new error case by fiddling with the UI.
Reviewers: btrahan
Reviewed By: btrahan
CC: chad, aran
Maniphest Tasks: T4379
Differential Revision: https://secure.phabricator.com/D8165
2014-02-10 23:29:17 +01:00
|
|
|
|
2014-05-19 21:40:57 +02:00
|
|
|
public function isUserWatcher($user_phid) {
|
|
|
|
if ($this->watcherPHIDs !== self::ATTACHABLE) {
|
|
|
|
return in_array($user_phid, $this->watcherPHIDs);
|
|
|
|
}
|
|
|
|
return $this->assertAttachedKey($this->sparseWatchers, $user_phid);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setIsUserWatcher($user_phid, $is_watcher) {
|
|
|
|
if ($this->sparseWatchers === self::ATTACHABLE) {
|
|
|
|
$this->sparseWatchers = array();
|
|
|
|
}
|
|
|
|
$this->sparseWatchers[$user_phid] = $is_watcher;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function attachWatcherPHIDs(array $phids) {
|
|
|
|
$this->watcherPHIDs = $phids;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getWatcherPHIDs() {
|
|
|
|
return $this->assertAttached($this->watcherPHIDs);
|
|
|
|
}
|
|
|
|
|
2014-05-22 20:19:03 +02:00
|
|
|
public function attachSlugs(array $slugs) {
|
|
|
|
$this->slugs = $slugs;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getSlugs() {
|
|
|
|
return $this->assertAttached($this->slugs);
|
|
|
|
}
|
|
|
|
|
2014-05-19 21:40:57 +02:00
|
|
|
|
|
|
|
|
Make Projects a PhabricatorSubscribableInterface, but with restricted defaults
Summary:
Ref T4379. I want project subscriptions to work like this (yell if this seems whacky, since it makes subscriptions mean somethign a little different for projects than they do for other objects):
- You can only subscribe to a project if you're a project member.
- When you're added as a member, you're added as a subscriber.
- When you're removed as a member, you're removed as a subscriber.
- While you're a member, you can optionally unsubscribe.
From a UI perspective:
- We don't show the subscriber list, since it's going to be some uninteresting subset of the member list.
- We don't show CC transactions in history, since they're an uninteresting near-approximation of the membership transactions.
- You only see the subscription controls if you're a member.
To do this, I've augmented `PhabricatorSubscribableInterface` with two new methods. It would be nice if we were on PHP 5.4+ and could just use traits for this, but we should get data about version usage before we think about this. For now, copy/paste the default implementations into every implementing class.
Then, I implemented the interface in `PhabricatorProject` but with alternate defaults.
Test Plan:
- Used the normal interaction on existing objects.
- This has no actual effect on projects, verified no subscription stuff mysteriously appeared.
- Hit the new error case by fiddling with the UI.
Reviewers: btrahan
Reviewed By: btrahan
CC: chad, aran
Maniphest Tasks: T4379
Differential Revision: https://secure.phabricator.com/D8165
2014-02-10 23:29:17 +01:00
|
|
|
/* -( PhabricatorSubscribableInterface )----------------------------------- */
|
|
|
|
|
|
|
|
|
|
|
|
public function isAutomaticallySubscribed($phid) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function shouldShowSubscribersProperty() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function shouldAllowSubscription($phid) {
|
2014-05-19 21:40:57 +02:00
|
|
|
return $this->isUserMember($phid) &&
|
|
|
|
!$this->isUserWatcher($phid);
|
Make Projects a PhabricatorSubscribableInterface, but with restricted defaults
Summary:
Ref T4379. I want project subscriptions to work like this (yell if this seems whacky, since it makes subscriptions mean somethign a little different for projects than they do for other objects):
- You can only subscribe to a project if you're a project member.
- When you're added as a member, you're added as a subscriber.
- When you're removed as a member, you're removed as a subscriber.
- While you're a member, you can optionally unsubscribe.
From a UI perspective:
- We don't show the subscriber list, since it's going to be some uninteresting subset of the member list.
- We don't show CC transactions in history, since they're an uninteresting near-approximation of the membership transactions.
- You only see the subscription controls if you're a member.
To do this, I've augmented `PhabricatorSubscribableInterface` with two new methods. It would be nice if we were on PHP 5.4+ and could just use traits for this, but we should get data about version usage before we think about this. For now, copy/paste the default implementations into every implementing class.
Then, I implemented the interface in `PhabricatorProject` but with alternate defaults.
Test Plan:
- Used the normal interaction on existing objects.
- This has no actual effect on projects, verified no subscription stuff mysteriously appeared.
- Hit the new error case by fiddling with the UI.
Reviewers: btrahan
Reviewed By: btrahan
CC: chad, aran
Maniphest Tasks: T4379
Differential Revision: https://secure.phabricator.com/D8165
2014-02-10 23:29:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-02-10 23:31:34 +01:00
|
|
|
/* -( PhabricatorCustomFieldInterface )------------------------------------ */
|
|
|
|
|
|
|
|
|
|
|
|
public function getCustomFieldSpecificationForRole($role) {
|
|
|
|
return PhabricatorEnv::getEnvConfig('projects.fields');
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getCustomFieldBaseClass() {
|
|
|
|
return 'PhabricatorProjectCustomField';
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getCustomFields() {
|
|
|
|
return $this->assertAttached($this->customFields);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function attachCustomFields(PhabricatorCustomFieldAttachment $fields) {
|
|
|
|
$this->customFields = $fields;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-02-21 03:41:23 +01:00
|
|
|
}
|