2011-02-20 18:41:23 -08:00
|
|
|
<?php
|
|
|
|
|
|
2012-03-09 15:46:25 -08:00
|
|
|
final class PhabricatorProjectProfileController
|
2011-02-20 18:41:23 -08:00
|
|
|
extends PhabricatorProjectController {
|
|
|
|
|
|
Fix some file policy issues and add a "Query Workspace"
Summary:
Ref T603. Several issues here:
1. Currently, `FileQuery` does not actually respect object attachment edges when doing policy checks. Everything else works fine, but this was missing an `array_keys()`.
2. Once that's fixed, we hit a bunch of recursion issues. For example, when loading a User we load the profile picture, and then that loads the User, and that loads the profile picture, etc.
3. Introduce a "Query Workspace", which holds objects we know we've loaded and know we can see but haven't finished filtering and/or attaching data to. This allows subqueries to look up objects instead of querying for them.
- We can probably generalize this a bit to make a few other queries more efficient. Pholio currently has a similar (but less general) "mock cache". However, it's keyed by ID instead of PHID so it's not easy to reuse this right now.
This is a bit complex for the problem being solved, but I think it's the cleanest approach and I believe the primitive will be useful in the future.
Test Plan: Looked at pastes, macros, mocks and projects as a logged-in and logged-out user.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T603
Differential Revision: https://secure.phabricator.com/D7309
2013-10-14 14:36:06 -07:00
|
|
|
public function shouldAllowPublic() {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-12 10:04:01 -08:00
|
|
|
public function handleRequest(AphrontRequest $request) {
|
2015-12-27 02:04:37 -08:00
|
|
|
$response = $this->loadProject();
|
|
|
|
|
if ($response) {
|
|
|
|
|
return $response;
|
2014-05-22 15:16:16 -07:00
|
|
|
}
|
2012-08-15 10:44:58 -07:00
|
|
|
|
2016-01-19 17:02:29 -08:00
|
|
|
$viewer = $request->getUser();
|
2015-12-27 02:04:37 -08:00
|
|
|
$project = $this->getProject();
|
|
|
|
|
$id = $project->getID();
|
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 14:32:14 -08:00
|
|
|
$picture = $project->getProfileImageURI();
|
2014-02-13 14:36:49 -08:00
|
|
|
|
2013-09-17 09:12:37 -07:00
|
|
|
$header = id(new PHUIHeaderView())
|
2013-07-09 16:23:22 -07:00
|
|
|
->setHeader($project->getName())
|
2015-12-27 02:04:37 -08:00
|
|
|
->setUser($viewer)
|
2013-10-21 11:34:45 -07:00
|
|
|
->setPolicyObject($project)
|
2015-01-19 10:14:27 -08:00
|
|
|
->setImage($picture);
|
2011-02-20 18:41:23 -08:00
|
|
|
|
2013-10-21 11:34:45 -07:00
|
|
|
if ($project->getStatus() == PhabricatorProjectStatus::STATUS_ACTIVE) {
|
2014-05-18 16:10:54 -07:00
|
|
|
$header->setStatus('fa-check', 'bluegrey', pht('Active'));
|
2013-10-21 11:34:45 -07:00
|
|
|
} else {
|
2015-01-06 11:13:04 -08:00
|
|
|
$header->setStatus('fa-ban', 'red', pht('Archived'));
|
2013-10-21 11:34:45 -07:00
|
|
|
}
|
|
|
|
|
|
2016-01-23 16:02:29 -08:00
|
|
|
$properties = $this->buildPropertyListView($project);
|
|
|
|
|
$watch_action = $this->renderWatchAction($project);
|
|
|
|
|
$header->addActionLink($watch_action);
|
2013-09-28 15:55:38 -07:00
|
|
|
|
2016-01-19 17:02:29 -08:00
|
|
|
$member_list = id(new PhabricatorProjectMemberListView())
|
|
|
|
|
->setUser($viewer)
|
|
|
|
|
->setProject($project)
|
|
|
|
|
->setLimit(5)
|
|
|
|
|
->setUserPHIDs($project->getMemberPHIDs());
|
|
|
|
|
|
|
|
|
|
$watcher_list = id(new PhabricatorProjectWatcherListView())
|
|
|
|
|
->setUser($viewer)
|
|
|
|
|
->setProject($project)
|
|
|
|
|
->setLimit(5)
|
|
|
|
|
->setUserPHIDs($project->getWatcherPHIDs());
|
2015-01-19 10:14:27 -08:00
|
|
|
|
2016-01-12 14:15:59 -08:00
|
|
|
$nav = $this->getProfileMenu();
|
2016-01-12 10:27:39 -08:00
|
|
|
$nav->selectFilter(PhabricatorProject::PANEL_PROFILE);
|
2015-12-27 02:04:37 -08:00
|
|
|
|
2016-01-19 17:02:29 -08:00
|
|
|
$stories = id(new PhabricatorFeedQuery())
|
|
|
|
|
->setViewer($viewer)
|
|
|
|
|
->setFilterPHIDs(
|
|
|
|
|
array(
|
|
|
|
|
$project->getPHID(),
|
|
|
|
|
))
|
|
|
|
|
->setLimit(50)
|
|
|
|
|
->execute();
|
|
|
|
|
|
|
|
|
|
$feed = $this->renderStories($stories);
|
2016-01-23 16:02:29 -08:00
|
|
|
$feed = phutil_tag_div('project-view-feed', $feed);
|
2016-01-19 17:02:29 -08:00
|
|
|
|
2016-01-23 16:02:29 -08:00
|
|
|
$columns = id(new PHUITwoColumnView())
|
|
|
|
|
->setMainColumn(
|
|
|
|
|
array(
|
|
|
|
|
$properties,
|
|
|
|
|
$feed,
|
|
|
|
|
))
|
|
|
|
|
->setSideColumn(
|
2016-01-19 17:02:29 -08:00
|
|
|
array(
|
|
|
|
|
$member_list,
|
|
|
|
|
$watcher_list,
|
|
|
|
|
));
|
|
|
|
|
|
2015-12-27 04:10:59 -08:00
|
|
|
$crumbs = $this->buildApplicationCrumbs();
|
2016-01-23 16:02:29 -08:00
|
|
|
$crumbs->setBorder(true);
|
|
|
|
|
|
|
|
|
|
$header = phutil_tag(
|
|
|
|
|
'div',
|
|
|
|
|
array(
|
|
|
|
|
'class' => 'project-view-header',
|
|
|
|
|
),
|
|
|
|
|
$header);
|
|
|
|
|
|
|
|
|
|
require_celerity_resource('project-view-css');
|
|
|
|
|
$home = phutil_tag(
|
|
|
|
|
'div',
|
|
|
|
|
array(
|
|
|
|
|
'class' => 'project-view-home',
|
|
|
|
|
),
|
|
|
|
|
array(
|
|
|
|
|
$header,
|
|
|
|
|
$columns,
|
|
|
|
|
));
|
2015-12-27 04:10:59 -08:00
|
|
|
|
2015-12-27 02:04:37 -08:00
|
|
|
return $this->newPage()
|
|
|
|
|
->setNavigation($nav)
|
2015-12-27 04:10:59 -08:00
|
|
|
->setCrumbs($crumbs)
|
2015-12-27 02:04:37 -08:00
|
|
|
->setTitle($project->getName())
|
|
|
|
|
->setPageObjectPHIDs(array($project->getPHID()))
|
2016-01-19 17:02:29 -08:00
|
|
|
->appendChild(
|
|
|
|
|
array(
|
2016-01-23 16:02:29 -08:00
|
|
|
$home,
|
2016-01-19 17:02:29 -08:00
|
|
|
));
|
2011-02-20 18:41:23 -08:00
|
|
|
}
|
|
|
|
|
|
2013-10-11 07:53:56 -07:00
|
|
|
private function buildPropertyListView(
|
2016-01-23 16:02:29 -08:00
|
|
|
PhabricatorProject $project) {
|
2013-07-22 09:01:22 -07:00
|
|
|
$request = $this->getRequest();
|
|
|
|
|
$viewer = $request->getUser();
|
|
|
|
|
|
2013-10-11 07:53:56 -07:00
|
|
|
$view = id(new PHUIPropertyListView())
|
2013-07-22 09:01:22 -07:00
|
|
|
->setUser($viewer)
|
2016-01-23 16:02:29 -08:00
|
|
|
->setObject($project);
|
2015-01-19 10:14:27 -08:00
|
|
|
|
2014-02-10 14:31:34 -08:00
|
|
|
$field_list = PhabricatorCustomField::getObjectFields(
|
|
|
|
|
$project,
|
|
|
|
|
PhabricatorCustomField::ROLE_VIEW);
|
|
|
|
|
$field_list->appendFieldsToPropertyList($project, $viewer, $view);
|
|
|
|
|
|
2016-01-23 16:02:29 -08:00
|
|
|
$view = id(new PHUIBoxView())
|
|
|
|
|
->setColor(PHUIBoxView::GREY)
|
|
|
|
|
->appendChild($view)
|
|
|
|
|
->addClass('project-view-properties');
|
|
|
|
|
|
2013-07-22 09:01:22 -07:00
|
|
|
return $view;
|
2013-04-15 13:07:54 -07:00
|
|
|
}
|
|
|
|
|
|
2016-01-19 17:02:29 -08:00
|
|
|
private function renderStories(array $stories) {
|
|
|
|
|
assert_instances_of($stories, 'PhabricatorFeedStory');
|
|
|
|
|
|
|
|
|
|
$builder = new PhabricatorFeedBuilder($stories);
|
|
|
|
|
$builder->setUser($this->getRequest()->getUser());
|
|
|
|
|
$builder->setShowHovercards(true);
|
|
|
|
|
$view = $builder->buildView();
|
|
|
|
|
|
2016-01-23 16:02:29 -08:00
|
|
|
return $view;
|
2016-01-19 17:02:29 -08:00
|
|
|
}
|
2013-07-22 09:01:22 -07:00
|
|
|
|
2016-01-19 17:43:14 -08:00
|
|
|
private function renderWatchAction(PhabricatorProject $project) {
|
|
|
|
|
$viewer = $this->getViewer();
|
|
|
|
|
$viewer_phid = $viewer->getPHID();
|
|
|
|
|
$id = $project->getID();
|
|
|
|
|
|
|
|
|
|
$is_watcher = ($viewer_phid && $project->isUserWatcher($viewer_phid));
|
|
|
|
|
|
|
|
|
|
if (!$is_watcher) {
|
|
|
|
|
$watch_icon = 'fa-eye';
|
|
|
|
|
$watch_text = pht('Watch Project');
|
|
|
|
|
$watch_href = "/project/watch/{$id}/?via=profile";
|
|
|
|
|
} else {
|
|
|
|
|
$watch_icon = 'fa-eye-slash';
|
|
|
|
|
$watch_text = pht('Unwatch Project');
|
|
|
|
|
$watch_href = "/project/unwatch/{$id}/?via=profile";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$watch_icon = id(new PHUIIconView())
|
|
|
|
|
->setIconFont($watch_icon);
|
|
|
|
|
|
|
|
|
|
return id(new PHUIButtonView())
|
|
|
|
|
->setTag('a')
|
|
|
|
|
->setWorkflow(true)
|
|
|
|
|
->setIcon($watch_icon)
|
|
|
|
|
->setText($watch_text)
|
|
|
|
|
->setHref($watch_href);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-02-20 18:41:23 -08:00
|
|
|
}
|