2011-02-21 03:41:23 +01:00
|
|
|
<?php
|
|
|
|
|
2012-08-09 02:10:10 +02:00
|
|
|
final class PhabricatorProject extends PhabricatorProjectDAO
|
|
|
|
implements PhabricatorPolicyInterface {
|
2011-02-21 03:41:23 +01:00
|
|
|
|
|
|
|
protected $name;
|
|
|
|
protected $phid;
|
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;
|
2011-07-19 20:50:15 +02:00
|
|
|
|
2012-08-11 16:05:01 +02:00
|
|
|
protected $viewPolicy;
|
|
|
|
protected $editPolicy;
|
|
|
|
protected $joinPolicy;
|
|
|
|
|
2012-08-08 03:02:05 +02:00
|
|
|
private $subprojectsNeedUpdate;
|
|
|
|
private $memberPHIDs;
|
2012-08-11 16:05:01 +02:00
|
|
|
private $sparseMembers = array();
|
2011-02-21 03:41:23 +01:00
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-08-11 16:05:01 +02:00
|
|
|
public function isUserMember($user_phid) {
|
|
|
|
if (!isset($this->sparseMembers[$user_phid])) {
|
|
|
|
throw new Exception(
|
|
|
|
"Call setIsUserMember() before isUserMember()!");
|
|
|
|
}
|
|
|
|
return $this->sparseMembers[$user_phid];
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setIsUserMember($user_phid, $is_member) {
|
|
|
|
$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
|
|
|
}
|
|
|
|
|
Improve performance of project list view
Summary:
D477 added functionality to the project list view but had a couple of
performance issues that I missed in review, because it took the query count for
the page from around 3 to as many as 300, including up to 100 heavyweight search
index queries.
This fixes the two simple N+1 query problems. This general pattern of data
access often occurs:
COUNTEREXAMPLE
$cats = load_cats();
foreach ($cats as $cat) {
$cats_hats = load_hats_for_cat($cat);
// ...
}
But this issues "N+1" queries, i.e. if you load 100 cats you issue 101 queries.
It is faster to group the queries instead:
$cats = load_cats();
$hats = load_all_hats_for_these_cats($cats);
foreach ($cats as $cat) {
$cats_hats = $hats[$cat->getID()];
}
MySQL can execute one query which returns all the results much faster than 100
queries which return one result, especially if the database is not local (i.e.,
over the network).
However, this doesn't save a ton of time. The bigger issue is that I didn't have
the right keys on the relationship tables in the search engine. This adds them,
and reduces the search engine lookup cost from 25-80ms (for
secure.phabricator.com) down to 1-3ms.
I still probably want to get this out of the loop at some point but it's okay
for now and the page loads in a few ms rather than taking more than a second.
Test Plan:
Used "services" tab, "xhprof" and "EXPLAIN" to analyze page performance. I
measured these changes:
- Query count: 1 + (3 * N projects) -> 3 + (N projects) (e.g., 301 -> 103)
- Total time spent querying, ignoring search indexes: 40ms (local.aprhont.com)
-> 20ms (local.aphront.com)
- Cost for search index query: 25-80ms (secure.phabricator.com) -> 1-3ms
Reviewed By: cadamo
Reviewers: cadamo, aran, jungejason, tuomaspelkonen
CC: aran, cadamo, epriestley
Differential Revision: 485
2011-06-21 01:32:44 +02:00
|
|
|
public function loadProfile() {
|
2011-06-18 10:13:56 +02:00
|
|
|
$profile = id(new PhabricatorProjectProfile())->loadOneWhere(
|
|
|
|
'projectPHID = %s',
|
|
|
|
$this->getPHID());
|
|
|
|
return $profile;
|
|
|
|
}
|
|
|
|
|
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() {
|
|
|
|
if ($this->memberPHIDs === null) {
|
|
|
|
throw new Exception("Call attachMemberPHIDs() first!");
|
2012-01-18 01:29:35 +01:00
|
|
|
}
|
2012-08-08 03:02:05 +02:00
|
|
|
return $this->memberPHIDs;
|
2012-01-18 01:29:35 +01:00
|
|
|
}
|
|
|
|
|
2012-08-08 03:02:05 +02:00
|
|
|
public function loadMemberPHIDs() {
|
|
|
|
if (!$this->getPHID()) {
|
|
|
|
return array();
|
|
|
|
}
|
|
|
|
return PhabricatorEdgeQuery::loadDestinationPHIDs(
|
|
|
|
$this->getPHID(),
|
|
|
|
PhabricatorEdgeConfig::TYPE_PROJ_MEMBER);
|
2011-06-18 10:13:56 +02:00
|
|
|
}
|
2011-07-19 20:50:15 +02: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;
|
|
|
|
}
|
|
|
|
|
2011-02-21 03:41:23 +01:00
|
|
|
}
|