1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-01-27 15:08:20 +01:00

Revamp Projects with new navigation

Summary:
A refresh of Projects including a new navigations UI.

 - New Navigation UI.
 - Auto switch default page if Workboard has been initialized
 - Move Feed to it's own page
 - Increase 'tasks' on Project Home to 50 over 10
 - Fix various display bugs on Workboards
 - Remove 'crumbs' from Project portal (unneeded).

Test Plan:
- clicked a link for a project with no workboard and saw the profile
- clicked a link for a project with a workboard and saw the workboard
- navigated around the various edit pages, inspecting links and making sure things linked back to the new profile uri

{F266460}

{F266461}

{F266462}

{F266463}

{F266464}

Reviewers: epriestley, btrahan

Reviewed By: epriestley, btrahan

Subscribers: Korvin, epriestley

Differential Revision: https://secure.phabricator.com/D11272
This commit is contained in:
Chad Little 2015-01-12 10:04:01 -08:00
parent 1f6c91a7ba
commit 953f281dc0
26 changed files with 466 additions and 246 deletions

View file

@ -7,7 +7,7 @@
*/
return array(
'names' => array(
'core.pkg.css' => 'b99369cc',
'core.pkg.css' => '60a6d241',
'core.pkg.js' => '61af8961',
'darkconsole.pkg.js' => '8ab24e01',
'differential.pkg.css' => '8af45893',
@ -23,7 +23,7 @@ return array(
'rsrc/css/aphront/error-view.css' => '3462dbee',
'rsrc/css/aphront/lightbox-attachment.css' => '7acac05d',
'rsrc/css/aphront/list-filter-view.css' => '2ae43867',
'rsrc/css/aphront/multi-column.css' => '6d72e772',
'rsrc/css/aphront/multi-column.css' => '41a848c0',
'rsrc/css/aphront/notification.css' => '9c279160',
'rsrc/css/aphront/pager-view.css' => '2e3539af',
'rsrc/css/aphront/panel-view.css' => '5846dfa2',
@ -94,7 +94,7 @@ return array(
'rsrc/css/application/ponder/feed.css' => 'e62615b6',
'rsrc/css/application/ponder/post.css' => 'ebab8a70',
'rsrc/css/application/ponder/vote.css' => '8ed6ed8b',
'rsrc/css/application/profile/profile-view.css' => '28f433ef',
'rsrc/css/application/profile/profile-view.css' => 'fddedfa1',
'rsrc/css/application/projects/project-icon.css' => 'c2ecb7f1',
'rsrc/css/application/releeph/releeph-core.css' => '9b3c5733',
'rsrc/css/application/releeph/releeph-preview-branch.css' => 'b7a6f4a5',
@ -115,7 +115,7 @@ return array(
'rsrc/css/layout/phabricator-crumbs-view.css' => 'd5aa87e4',
'rsrc/css/layout/phabricator-filetree-view.css' => 'fccf9f82',
'rsrc/css/layout/phabricator-hovercard-view.css' => '893f4783',
'rsrc/css/layout/phabricator-side-menu-view.css' => '90eafc85',
'rsrc/css/layout/phabricator-side-menu-view.css' => '7e8c6341',
'rsrc/css/layout/phabricator-source-code-view.css' => '7d346aa4',
'rsrc/css/phui/calendar/phui-calendar-day.css' => 'de035c8a',
'rsrc/css/phui/calendar/phui-calendar-list.css' => 'c1d0ca59',
@ -136,7 +136,7 @@ return array(
'rsrc/css/phui/phui-info-panel.css' => '27ea50a1',
'rsrc/css/phui/phui-list.css' => '53deb25c',
'rsrc/css/phui/phui-object-box.css' => 'dd19785f',
'rsrc/css/phui/phui-object-item-list-view.css' => '5b2ad99d',
'rsrc/css/phui/phui-object-item-list-view.css' => '8279b873',
'rsrc/css/phui/phui-pinboard-view.css' => '3dd4a269',
'rsrc/css/phui/phui-property-list-view.css' => '51480060',
'rsrc/css/phui/phui-remarkup-preview.css' => '19ad512b',
@ -145,7 +145,7 @@ return array(
'rsrc/css/phui/phui-tag-view.css' => '6b74282b',
'rsrc/css/phui/phui-text.css' => 'cf019f54',
'rsrc/css/phui/phui-timeline-view.css' => '415bf348',
'rsrc/css/phui/phui-workboard-view.css' => '2bf82d00',
'rsrc/css/phui/phui-workboard-view.css' => '8896938c',
'rsrc/css/phui/phui-workpanel-view.css' => 'e495a5cc',
'rsrc/css/sprite-apps-large.css' => '20ec0cc0',
'rsrc/css/sprite-apps.css' => 'd5baed0f',
@ -498,7 +498,7 @@ return array(
'aphront-dialog-view-css' => '4dbbe3bb',
'aphront-error-view-css' => '3462dbee',
'aphront-list-filter-view-css' => '2ae43867',
'aphront-multi-column-view-css' => '6d72e772',
'aphront-multi-column-view-css' => '41a848c0',
'aphront-pager-view-css' => '2e3539af',
'aphront-panel-view-css' => '5846dfa2',
'aphront-table-view-css' => 'b22b7216',
@ -728,11 +728,11 @@ return array(
'phabricator-object-selector-css' => '029a133d',
'phabricator-phtize' => 'd254d646',
'phabricator-prefab' => '72da38cc',
'phabricator-profile-css' => '28f433ef',
'phabricator-profile-css' => 'fddedfa1',
'phabricator-remarkup-css' => '7604f12e',
'phabricator-search-results-css' => 'f240504c',
'phabricator-shaped-request' => '7cbe244b',
'phabricator-side-menu-view-css' => '90eafc85',
'phabricator-side-menu-view-css' => '7e8c6341',
'phabricator-slowvote-css' => '266df6a1',
'phabricator-source-code-view-css' => '7d346aa4',
'phabricator-standard-page-view' => '2c96cfb5',
@ -780,7 +780,7 @@ return array(
'phui-info-panel-css' => '27ea50a1',
'phui-list-view-css' => '53deb25c',
'phui-object-box-css' => 'dd19785f',
'phui-object-item-list-view-css' => '5b2ad99d',
'phui-object-item-list-view-css' => '8279b873',
'phui-pinboard-view-css' => '3dd4a269',
'phui-property-list-view-css' => '51480060',
'phui-remarkup-preview-css' => '19ad512b',
@ -789,7 +789,7 @@ return array(
'phui-tag-view-css' => '6b74282b',
'phui-text-css' => 'cf019f54',
'phui-timeline-view-css' => '415bf348',
'phui-workboard-view-css' => '2bf82d00',
'phui-workboard-view-css' => '8896938c',
'phui-workpanel-view-css' => 'e495a5cc',
'phuix-action-list-view' => 'b5c256b8',
'phuix-action-view' => '6e8cefa4',

View file

@ -2199,6 +2199,7 @@ phutil_register_library_map(array(
'PhabricatorProjectEditMainController' => 'applications/project/controller/PhabricatorProjectEditMainController.php',
'PhabricatorProjectEditPictureController' => 'applications/project/controller/PhabricatorProjectEditPictureController.php',
'PhabricatorProjectEditorTestCase' => 'applications/project/editor/__tests__/PhabricatorProjectEditorTestCase.php',
'PhabricatorProjectFeedController' => 'applications/project/controller/PhabricatorProjectFeedController.php',
'PhabricatorProjectIcon' => 'applications/project/icon/PhabricatorProjectIcon.php',
'PhabricatorProjectInterface' => 'applications/project/interface/PhabricatorProjectInterface.php',
'PhabricatorProjectListController' => 'applications/project/controller/PhabricatorProjectListController.php',
@ -2225,6 +2226,7 @@ phutil_register_library_map(array(
'PhabricatorProjectTransactionQuery' => 'applications/project/query/PhabricatorProjectTransactionQuery.php',
'PhabricatorProjectUIEventListener' => 'applications/project/events/PhabricatorProjectUIEventListener.php',
'PhabricatorProjectUpdateController' => 'applications/project/controller/PhabricatorProjectUpdateController.php',
'PhabricatorProjectViewController' => 'applications/project/controller/PhabricatorProjectViewController.php',
'PhabricatorProjectWatchController' => 'applications/project/controller/PhabricatorProjectWatchController.php',
'PhabricatorProjectWikiExplainController' => 'applications/project/controller/PhabricatorProjectWikiExplainController.php',
'PhabricatorProjectsPolicyRule' => 'applications/policy/rule/PhabricatorProjectsPolicyRule.php',
@ -5418,6 +5420,7 @@ phutil_register_library_map(array(
'PhabricatorProjectEditMainController' => 'PhabricatorProjectController',
'PhabricatorProjectEditPictureController' => 'PhabricatorProjectController',
'PhabricatorProjectEditorTestCase' => 'PhabricatorTestCase',
'PhabricatorProjectFeedController' => 'PhabricatorProjectController',
'PhabricatorProjectIcon' => 'Phobject',
'PhabricatorProjectListController' => 'PhabricatorProjectController',
'PhabricatorProjectMemberOfProjectEdgeType' => 'PhabricatorEdgeType',
@ -5445,6 +5448,7 @@ phutil_register_library_map(array(
'PhabricatorProjectTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhabricatorProjectUIEventListener' => 'PhabricatorEventListener',
'PhabricatorProjectUpdateController' => 'PhabricatorProjectController',
'PhabricatorProjectViewController' => 'PhabricatorProjectController',
'PhabricatorProjectWatchController' => 'PhabricatorProjectController',
'PhabricatorProjectWikiExplainController' => 'PhabricatorProjectController',
'PhabricatorProjectsPolicyRule' => 'PhabricatorPolicyRule',

View file

@ -52,8 +52,12 @@ final class PhabricatorProjectApplication extends PhabricatorApplication {
=> 'PhabricatorProjectMembersEditController',
'members/(?P<id>[1-9]\d*)/remove/'
=> 'PhabricatorProjectMembersRemoveController',
'view/(?P<id>[1-9]\d*)/'
'profile/(?P<id>[1-9]\d*)/'
=> 'PhabricatorProjectProfileController',
'feed/(?P<id>[1-9]\d*)/'
=> 'PhabricatorProjectFeedController',
'view/(?P<id>[1-9]\d*)/'
=> 'PhabricatorProjectViewController',
'picture/(?P<id>[1-9]\d*)/'
=> 'PhabricatorProjectEditPictureController',
'icon/(?P<id>[1-9]\d*)/'
@ -86,7 +90,7 @@ final class PhabricatorProjectApplication extends PhabricatorApplication {
'wiki/' => 'PhabricatorProjectWikiExplainController',
),
'/tag/' => array(
'(?P<slug>[^/]+)/' => 'PhabricatorProjectProfileController',
'(?P<slug>[^/]+)/' => 'PhabricatorProjectViewController',
'(?P<slug>[^/]+)/board/' => 'PhabricatorProjectBoardViewController',
),
);

View file

@ -13,12 +13,10 @@ abstract class PhabricatorProjectBoardController
return $this->project;
}
protected function buildApplicationCrumbs() {
$project = $this->getProject();
$crumbs = parent::buildApplicationCrumbs();
$crumbs->addTextCrumb(
$project->getName(),
$this->getApplicationURI('view/'.$project->getID().'/'));
return $crumbs;
public function buildIconNavView(PhabricatorProject $project) {
$id = $project->getID();
$nav = parent::buildIconNavView($project);
$nav->selectFilter("board/{$id}/");
return $nav;
}
}

View file

@ -15,16 +15,9 @@ final class PhabricatorProjectBoardViewController
return true;
}
public function willProcessRequest(array $data) {
$this->id = idx($data, 'id');
$this->slug = idx($data, 'slug');
$this->queryKey = idx($data, 'queryKey');
$this->filter = (bool)idx($data, 'filter');
}
public function processRequest() {
$request = $this->getRequest();
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getUser();
$id = $request->getURIData('id');
$show_hidden = $request->getBool('hidden');
$this->showHidden = $show_hidden;
@ -32,10 +25,12 @@ final class PhabricatorProjectBoardViewController
$project = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->needImages(true);
if ($this->slug) {
$project->withSlugs(array($this->slug));
$id = $request->getURIData('id');
$slug = $request->getURIData('slug');
if ($slug) {
$project->withSlugs(array($slug));
} else {
$project->withIDs(array($this->id));
$project->withIDs(array($id));
}
$project = $project->executeOne();
if (!$project) {
@ -111,7 +106,7 @@ final class PhabricatorProjectBoardViewController
$engine->getQueryResultsPageURI($saved->getQueryKey())));
}
$query_key = $this->queryKey;
$query_key = $request->getURIData('queryKey');
if (!$query_key) {
$query_key = 'open';
}
@ -133,7 +128,7 @@ final class PhabricatorProjectBoardViewController
$custom_query = $saved;
}
if ($this->filter) {
if ($request->getURIData('filter')) {
$filter_form = id(new AphrontFormView())
->setUser($viewer);
$engine->buildSearchForm($filter_form, $saved);
@ -303,7 +298,7 @@ final class PhabricatorProjectBoardViewController
$header_link = phutil_tag(
'a',
array(
'href' => $this->getApplicationURI('view/'.$project->getID().'/'),
'href' => $this->getApplicationURI('profile/'.$project->getID().'/'),
),
$project->getName());
@ -312,7 +307,7 @@ final class PhabricatorProjectBoardViewController
->setUser($viewer)
->setNoBackground(true)
->setImage($project->getProfileImageURI())
->setImageURL($this->getApplicationURI('view/'.$project->getID().'/'))
->setImageURL($this->getApplicationURI('profile/'.$project->getID().'/'))
->addActionLink($sort_menu)
->addActionLink($filter_menu)
->addActionLink($manage_menu)
@ -322,10 +317,13 @@ final class PhabricatorProjectBoardViewController
->appendChild($board)
->addClass('project-board-wrapper');
$nav = $this->buildIconNavView($project);
$nav->appendChild($header);
$nav->appendChild($board_box);
return $this->buildApplicationPage(
array(
$header,
$board_box,
$nav,
),
array(
'title' => pht('%s Board', $project->getName()),

View file

@ -22,6 +22,7 @@ final class PhabricatorProjectColumnDetailController
PhabricatorPolicyCapability::CAN_VIEW,
))
->withIDs(array($this->projectID))
->needImages(true)
->executeOne();
if (!$project) {
@ -47,11 +48,6 @@ final class PhabricatorProjectColumnDetailController
$timeline->setShouldTerminate(true);
$title = pht('%s', $column->getDisplayName());
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(
pht('Board'),
$this->getApplicationURI('board/'.$project->getID().'/'));
$crumbs->addTextCrumb($title);
$header = $this->buildHeaderView($column);
$actions = $this->buildActionView($column);
@ -61,11 +57,13 @@ final class PhabricatorProjectColumnDetailController
->setHeader($header)
->addPropertyList($properties);
$nav = $this->buildIconNavView($project);
$nav->appendChild($box);
$nav->appendChild($timeline);
return $this->buildApplicationPage(
array(
$crumbs,
$box,
$timeline,
$nav,
),
array(
'title' => $title,

View file

@ -23,6 +23,7 @@ final class PhabricatorProjectColumnEditController
PhabricatorPolicyCapability::CAN_EDIT,
))
->withIDs(array($this->projectID))
->needImages(true)
->executeOne();
if (!$project) {
@ -135,11 +136,9 @@ final class PhabricatorProjectColumnEditController
if ($is_new) {
$title = pht('Create Column');
$submit = pht('Create Column');
$crumb_text = pht('Create Column');
} else {
$title = pht('Edit %s', $column->getDisplayName());
$submit = pht('Save Column');
$crumb_text = pht('Edit');
}
$form->appendChild(
@ -147,28 +146,17 @@ final class PhabricatorProjectColumnEditController
->setValue($submit)
->addCancelButton($view_uri));
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(
pht('Board'),
$this->getApplicationURI('board/'.$project->getID().'/'));
if (!$is_new) {
$crumbs->addTextCrumb(
$column->getDisplayName(),
$view_uri);
}
$crumbs->addTextCrumb($crumb_text);
$form_box = id(new PHUIObjectBoxView())
->setHeaderText($title)
->setValidationException($validation_exception)
->setForm($form);
$nav = $this->buildIconNavView($project);
$nav->appendChild($form_box);
return $this->buildApplicationPage(
array(
$crumbs,
$form_box,
$nav,
),
array(
'title' => $title,

View file

@ -2,23 +2,56 @@
abstract class PhabricatorProjectController extends PhabricatorController {
public function buildApplicationMenu() {
return $this->buildSideNavView(true)->getMenu();
}
public function buildSideNavView($for_app = false) {
$user = $this->getRequest()->getUser();
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
$id = null;
if ($for_app) {
$user = $this->getRequest()->getUser();
$id = $this->getRequest()->getURIData('id');
if ($id) {
$nav->addFilter("profile/{$id}/", pht('Profile'));
$nav->addFilter("board/{$id}/", pht('Workboard'));
$nav->addFilter("members/{$id}/", pht('Members'));
$nav->addFilter("feed/{$id}/", pht('Feed'));
$nav->addFilter("edit/{$id}/", pht('Edit'));
}
$nav->addFilter('create', pht('Create Project'));
}
if (!$id) {
id(new PhabricatorProjectSearchEngine())
->setViewer($user)
->addNavigationItems($nav->getMenu());
}
$nav->selectFilter(null);
return $nav;
}
public function buildIconNavView(PhabricatorProject $project) {
$id = $project->getID();
$picture = $project->getProfileImageURI();
$name = $project->getName();
$nav = new AphrontSideNavFilterView();
$nav->setIconNav(true);
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
$nav->addIcon("profile/{$id}/", $name, null, $picture);
$nav->addIcon("board/{$id}/", pht('Workboard'), 'fa-columns');
$nav->addIcon("feed/{$id}/", pht('Feed'), 'fa-newspaper-o');
$nav->addIcon("members/{$id}/", pht('Members'), 'fa-group');
$nav->addIcon("edit/{$id}/", pht('Edit'), 'fa-pencil');
return $nav;
}
}

View file

@ -14,12 +14,14 @@ final class PhabricatorProjectEditDetailsController
$viewer = $request->getUser();
if ($this->id) {
$id = $request->getURIData('id');
$is_new = false;
$project = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->withIDs(array($this->id))
->needSlugs(true)
->needImages(true)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
@ -149,7 +151,7 @@ final class PhabricatorProjectEditDetailsController
if ($is_new) {
$redirect_uri =
$this->getApplicationURI('view/'.$project->getID().'/');
$this->getApplicationURI('profile/'.$project->getID().'/');
} else {
$redirect_uri =
$this->getApplicationURI('edit/'.$project->getID().'/');
@ -300,22 +302,17 @@ final class PhabricatorProjectEditDetailsController
->setValidationException($validation_exception)
->setForm($form);
$crumbs = $this->buildApplicationCrumbs($this->buildSideNavView());
if ($is_new) {
$crumbs->addTextCrumb($title);
if (!$is_new) {
$nav = $this->buildIconNavView($project);
$nav->selectFilter("edit/{$id}/");
$nav->appendChild($form_box);
} else {
$crumbs
->addTextCrumb($project->getName(),
$this->getApplicationURI('view/'.$project->getID().'/'))
->addTextCrumb(pht('Edit'),
$this->getApplicationURI('edit/'.$project->getID().'/'))
->addTextCrumb(pht('Details'));
$nav = array($form_box);
}
return $this->buildApplicationPage(
array(
$crumbs,
$form_box,
$nav,
),
array(
'title' => $title,

View file

@ -18,6 +18,7 @@ final class PhabricatorProjectEditMainController
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$id = $request->getURIData('id');
$project = id(new PhabricatorProjectQuery())
->setViewer($viewer)
@ -43,12 +44,6 @@ final class PhabricatorProjectEditMainController
$actions = $this->buildActionListView($project);
$properties = $this->buildPropertyListView($project, $actions);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(
$project->getName(),
$this->getApplicationURI('view/'.$project->getID().'/'));
$crumbs->addTextCrumb(pht('Edit'));
$object_box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
@ -58,11 +53,16 @@ final class PhabricatorProjectEditMainController
new PhabricatorProjectTransactionQuery());
$timeline->setShouldTerminate(true);
$nav = $this->buildIconNavView($project);
$nav->selectFilter("edit/{$id}/");
$nav->appendChild($object_box);
$nav->appendChild($timeline);
$mnav = $this->buildSideNavView();
return $this->buildApplicationPage(
array(
$crumbs,
$object_box,
$timeline,
$nav,
),
array(
'title' => $project->getName(),

View file

@ -12,10 +12,12 @@ final class PhabricatorProjectEditPictureController
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$id = $request->getURIData('id');
$project = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->withIDs(array($this->id))
->needImages(true)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
@ -27,7 +29,7 @@ final class PhabricatorProjectEditPictureController
}
$edit_uri = $this->getApplicationURI('edit/'.$project->getID().'/');
$view_uri = $this->getApplicationURI('view/'.$project->getID().'/');
$view_uri = $this->getApplicationURI('profile/'.$project->getID().'/');
$supported_formats = PhabricatorFile::getTransformableImageFormats();
$e_file = true;
@ -100,10 +102,6 @@ final class PhabricatorProjectEditPictureController
}
$title = pht('Edit Project Picture');
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($project->getName(), $view_uri);
$crumbs->addTextCrumb(pht('Edit'), $edit_uri);
$crumbs->addTextCrumb(pht('Picture'));
$form = id(new PHUIFormLayoutView())
->setUser($viewer);
@ -292,11 +290,14 @@ final class PhabricatorProjectEditPictureController
->setHeaderText(pht('Upload New Picture'))
->setForm($upload_form);
$nav = $this->buildIconNavView($project);
$nav->selectFilter("edit/{$id}/");
$nav->appendChild($form_box);
$nav->appendChild($upload_box);
return $this->buildApplicationPage(
array(
$crumbs,
$form_box,
$upload_box,
$nav,
),
array(
'title' => $title,

View file

@ -0,0 +1,91 @@
<?php
final class PhabricatorProjectFeedController
extends PhabricatorProjectController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$user = $request->getUser();
$query = id(new PhabricatorProjectQuery())
->setViewer($user)
->needMembers(true)
->needWatchers(true)
->needImages(true)
->needSlugs(true);
$id = $request->getURIData('id');
$slug = $request->getURIData('slug');
if ($slug) {
$query->withSlugs(array($slug));
} else {
$query->withIDs(array($id));
}
$project = $query->executeOne();
if (!$project) {
return new Aphront404Response();
}
if ($slug && $slug != $project->getPrimarySlug()) {
return id(new AphrontRedirectResponse())
->setURI('/tag/'.$project->getPrimarySlug().'/');
}
require_celerity_resource('phabricator-profile-css');
$query = new PhabricatorFeedQuery();
$query->setFilterPHIDs(
array(
$project->getPHID(),
));
$query->setLimit(50);
$query->setViewer($request->getUser());
$stories = $query->execute();
$feed = $this->renderStories($stories);
$content = phutil_tag_div('phabricator-project-feed', $feed);
$nav = $this->buildIconNavView($project);
$nav->selectFilter("feed/{$id}/");
$nav->appendChild($content);
return $this->buildApplicationPage(
array(
$nav,
),
array(
'title' => $project->getName(),
));
}
private function renderFeedPage(PhabricatorProject $project) {
$query = new PhabricatorFeedQuery();
$query->setFilterPHIDs(array($project->getPHID()));
$query->setViewer($this->getRequest()->getUser());
$query->setLimit(100);
$stories = $query->execute();
if (!$stories) {
return pht('There are no stories about this project.');
}
return $this->renderStories($stories);
}
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();
return phutil_tag_div(
'profile-feed',
$view->render());
}
}

View file

@ -22,7 +22,7 @@ final class PhabricatorProjectListController
return $this->delegateToController($controller);
}
protected function buildApplicationMenu() {
public function buildApplicationMenu() {
return $this->buildSideNavView(true)->getMenu();
}

View file

@ -12,15 +12,16 @@ final class PhabricatorProjectMembersEditController
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$id = $request->getURIData('id');
$project = id(new PhabricatorProjectQuery())
->setViewer($user)
->withIDs(array($this->id))
->needMembers(true)
->needImages(true)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$project) {
@ -73,8 +74,16 @@ final class PhabricatorProjectMembersEditController
);
}
$can_edit = PhabricatorPolicyFilter::hasCapability(
$user,
$project,
PhabricatorPolicyCapability::CAN_EDIT);
$form_box = null;
$title = pht('Add Members');
if ($can_edit) {
$header_name = pht('Edit Members');
$title = pht('Edit Members');
$view_uri = $this->getApplicationURI('profile/'.$project->getID().'/');
$form = new AphrontFormView();
$form
@ -86,26 +95,23 @@ final class PhabricatorProjectMembersEditController
->setDatasource(new PhabricatorPeopleDatasource()))
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton('/project/view/'.$project->getID().'/')
->addCancelButton($view_uri)
->setValue(pht('Add Members')));
$member_list = $this->renderMemberList($project, $handles);
$form_box = id(new PHUIObjectBoxView())
->setHeaderText($title)
->setForm($form);
}
$crumbs = $this->buildApplicationCrumbs($this->buildSideNavView())
->addTextCrumb(
$project->getName(),
'/project/view/'.$project->getID().'/')
->addTextCrumb(pht('Edit Members'), $this->getApplicationURI());
$member_list = $this->renderMemberList($project, $handles);
$nav = $this->buildIconNavView($project);
$nav->selectFilter("members/{$id}/");
$nav->appendChild($form_box);
$nav->appendChild($member_list);
return $this->buildApplicationPage(
array(
$crumbs,
$form_box,
$member_list,
$nav,
),
array(
'title' => $title,
@ -119,8 +125,14 @@ final class PhabricatorProjectMembersEditController
$request = $this->getRequest();
$viewer = $request->getUser();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$project,
PhabricatorPolicyCapability::CAN_EDIT);
$list = id(new PHUIObjectItemListView())
->setNoDataString(pht('This project does not have any members.'));
->setNoDataString(pht('This project does not have any members.'))
->setStackable(true);
foreach ($handles as $handle) {
$remove_uri = $this->getApplicationURI(
@ -131,16 +143,22 @@ final class PhabricatorProjectMembersEditController
->setHref($handle->getURI())
->setImageURI($handle->getImageURI());
if ($can_edit) {
$item->addAction(
id(new PHUIListItemView())
->setIcon('fa-times')
->setName(pht('Remove'))
->setHref($remove_uri)
->setWorkflow(true));
}
$list->addItem($item);
}
return $list;
$box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Members'))
->appendChild($list);
return $box;
}
}

View file

@ -3,22 +3,11 @@
final class PhabricatorProjectProfileController
extends PhabricatorProjectController {
private $id;
private $slug;
public function shouldAllowPublic() {
return true;
}
public function willProcessRequest(array $data) {
// via /project/view/$id/
$this->id = idx($data, 'id');
// via /tag/$slug/
$this->slug = idx($data, 'slug');
}
public function processRequest() {
$request = $this->getRequest();
public function handleRequest(AphrontRequest $request) {
$user = $request->getUser();
$query = id(new PhabricatorProjectQuery())
@ -27,55 +16,43 @@ final class PhabricatorProjectProfileController
->needWatchers(true)
->needImages(true)
->needSlugs(true);
if ($this->slug) {
$query->withSlugs(array($this->slug));
$id = $request->getURIData('id');
$slug = $request->getURIData('slug');
if ($slug) {
$query->withSlugs(array($slug));
} else {
$query->withIDs(array($this->id));
$query->withIDs(array($id));
}
$project = $query->executeOne();
if (!$project) {
return new Aphront404Response();
}
if ($this->slug && $this->slug != $project->getPrimarySlug()) {
if ($slug && $slug != $project->getPrimarySlug()) {
return id(new AphrontRedirectResponse())
->setURI('/tag/'.$project->getPrimarySlug().'/');
}
$picture = $project->getProfileImageURI();
require_celerity_resource('phabricator-profile-css');
$tasks = $this->renderTasksPage($project);
$content = phutil_tag_div('phabricator-project-layout', $tasks);
$query = new PhabricatorFeedQuery();
$query->setFilterPHIDs(
array(
$project->getPHID(),
));
$query->setLimit(50);
$query->setViewer($this->getRequest()->getUser());
$stories = $query->execute();
$feed = $this->renderStories($stories);
$content = phutil_tag_div(
'phabricator-project-layout',
array($tasks, $feed));
$id = $project->getID();
$icon = id(new PHUIIconView())
->setIconFont('fa-columns');
$board_btn = id(new PHUIButtonView())
$phid = $project->getPHID();
$create_uri = '/maniphest/task/create/?projects='.$phid;
$icon_new = id(new PHUIIconView())
->setIconFont('fa-plus');
$button_add = id(new PHUIButtonView())
->setTag('a')
->setText(pht('Workboard'))
->setHref($this->getApplicationURI("board/{$id}/"))
->setIcon($icon);
->setText(pht('New Task'))
->setHref($create_uri)
->setIcon($icon_new);
$header = id(new PHUIHeaderView())
->setHeader($project->getName())
->setUser($user)
->setPolicyObject($project)
->setImage($picture)
->addActionLink($board_btn);
->addActionLink($button_add);
if ($project->getStatus() == PhabricatorProjectStatus::STATUS_ACTIVE) {
$header->setStatus('fa-check', 'bluegrey', pht('Active'));
@ -86,57 +63,28 @@ final class PhabricatorProjectProfileController
$actions = $this->buildActionListView($project);
$properties = $this->buildPropertyListView($project, $actions);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($project->getName());
$object_box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
$nav = $this->buildIconNavView($project);
$nav->selectFilter("profile/{$id}/");
$nav->appendChild($object_box);
$nav->appendChild($content);
return $this->buildApplicationPage(
array(
$crumbs,
$object_box,
$content,
$nav,
),
array(
'title' => $project->getName(),
));
}
private function renderFeedPage(PhabricatorProject $project) {
$query = new PhabricatorFeedQuery();
$query->setFilterPHIDs(array($project->getPHID()));
$query->setViewer($this->getRequest()->getUser());
$query->setLimit(100);
$stories = $query->execute();
if (!$stories) {
return pht('There are no stories about this project.');
}
return $this->renderStories($stories);
}
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();
return phutil_tag_div(
'profile-feed',
$view->render());
}
private function renderTasksPage(PhabricatorProject $project) {
$user = $this->getRequest()->getUser();
$limit = 10;
$limit = 50;
$query = id(new ManiphestTaskQuery())
->setViewer($user)
@ -168,24 +116,15 @@ final class PhabricatorProjectProfileController
'/maniphest/?statuses=%s&allProjects=%s#R',
implode(',', ManiphestTaskStatus::getOpenStatusConstants()),
$phid);
$create_uri = '/maniphest/task/create/?projects='.$phid;
$icon = id(new PHUIIconView())
->setIconFont('fa-list');
->setIconFont('fa-search');
$button_view = id(new PHUIButtonView())
->setTag('a')
->setText(pht('View All'))
->setText(pht('View Query'))
->setHref($view_uri)
->setIcon($icon);
$icon_new = id(new PHUIIconView())
->setIconFont('fa-plus');
$button_add = id(new PHUIButtonView())
->setTag('a')
->setText(pht('New Task'))
->setHref($create_uri)
->setIcon($icon_new);
$header = id(new PHUIHeaderView())
->addActionLink($button_add)
->addActionLink($button_view);
if ($count > $limit) {
@ -217,20 +156,6 @@ final class PhabricatorProjectProfileController
$project,
PhabricatorPolicyCapability::CAN_EDIT);
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Project'))
->setIcon('fa-pencil')
->setHref($this->getApplicationURI("edit/{$id}/")));
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Members'))
->setIcon('fa-users')
->setHref($this->getApplicationURI("members/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$action = null;
if (!$project->isUserMember($viewer->getPHID())) {
$can_join = PhabricatorPolicyFilter::hasCapability(

View file

@ -42,7 +42,7 @@ final class PhabricatorProjectUpdateController
return new Aphront404Response();
}
$project_uri = '/project/view/'.$project->getID().'/';
$project_uri = $this->getApplicationURI('profile/'.$project->getID().'/');
if ($process_action) {

View file

@ -0,0 +1,56 @@
<?php
final class PhabricatorProjectViewController
extends PhabricatorProjectController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$request = $this->getRequest();
$user = $request->getUser();
$query = id(new PhabricatorProjectQuery())
->setViewer($user)
->needMembers(true)
->needWatchers(true)
->needImages(true)
->needSlugs(true);
$id = $request->getURIData('id');
$slug = $request->getURIData('slug');
if ($slug) {
$query->withSlugs(array($slug));
} else {
$query->withIDs(array($id));
}
$project = $query->executeOne();
if (!$project) {
return new Aphront404Response();
}
$columns = id(new PhabricatorProjectColumnQuery())
->setViewer($user)
->withProjectPHIDs(array($project->getPHID()))
->execute();
if ($columns) {
$controller = 'board';
} else {
$controller = 'profile';
}
switch ($controller) {
case 'board':
$controller_object = new PhabricatorProjectBoardViewController();
break;
case 'profile':
default:
$controller_object = new PhabricatorProjectProfileController();
break;
}
return $this->delegateToController($controller_object);
}
}

View file

@ -25,7 +25,7 @@ final class PhabricatorProjectWatchController
return new Aphront404Response();
}
$project_uri = '/project/view/'.$project->getID().'/';
$project_uri = $this->getApplicationURI('profile/'.$project->getID().'/');
// You must be a member of a project to
if (!$project->isUserMember($viewer->getPHID())) {

View file

@ -219,8 +219,15 @@ final class PhabricatorProjectSearchEngine
foreach ($projects as $key => $project) {
$id = $project->getID();
$profile_uri = $this->getApplicationURI("profile/{$id}/");
$workboards_uri = $this->getApplicationURI("board/{$id}/");
$members_uri = $this->getApplicationURI("members/{$id}/");
$profile_url = phutil_tag(
'a',
array(
'href' => $profile_uri,
),
pht('Profile'));
$workboards_url = phutil_tag(
'a',
array(
@ -252,6 +259,7 @@ final class PhabricatorProjectSearchEngine
->setHref($this->getApplicationURI("view/{$id}/"))
->setImageURI($project->getProfileImageURI())
->addAttribute($tag_list)
->addAttribute($profile_url)
->addAttribute($workboards_url)
->addAttribute($members_url);

View file

@ -27,6 +27,7 @@ final class AphrontSideNavFilterView extends AphrontView {
private $crumbs;
private $classes = array();
private $menuID;
private $iconNav;
public function setMenuID($menu_id) {
$this->menuID = $menu_id;
@ -61,6 +62,11 @@ final class AphrontSideNavFilterView extends AphrontView {
return $this->crumbs;
}
public function setIconNav($nav) {
$this->iconNav = $nav;
return $this;
}
public function setActive($active) {
$this->active = $active;
return $this;
@ -94,6 +100,22 @@ final class AphrontSideNavFilterView extends AphrontView {
$key, $name, $uri, PHUIListItemView::TYPE_LINK);
}
public function addIcon($key, $name, $icon, $image = null) {
$href = clone $this->baseURI;
$href->setPath(rtrim($href->getPath().$key, '/').'/');
$href = (string)$href;
$item = id(new PHUIListItemView())
->setKey($key)
->setRenderNameAsTooltip(true)
->setType(PHUIListItemView::TYPE_ICON_NAV)
->setIcon($icon)
->setHref($href)
->setName($name)
->setProfileImage($image);
return $this->addMenuItem($item);
}
public function addButton($key, $name, $uri = null) {
return $this->addThing(
$key, $name, $uri, PHUIListItemView::TYPE_BUTTON);
@ -109,6 +131,7 @@ final class AphrontSideNavFilterView extends AphrontView {
->setName($name)
->setType($type);
if (strlen($key)) {
$item->setKey($key);
}
@ -192,6 +215,9 @@ final class AphrontSideNavFilterView extends AphrontView {
$nav_classes = array();
$nav_classes[] = 'phabricator-nav';
if ($this->iconNav) {
$nav_classes[] = 'phabricator-icon-nav';
}
$nav_id = null;
$drag_id = null;

View file

@ -9,6 +9,7 @@ final class PHUIListItemView extends AphrontTagView {
const TYPE_CUSTOM = 'type-custom';
const TYPE_DIVIDER = 'type-divider';
const TYPE_ICON = 'type-icon';
const TYPE_ICON_NAV = 'type-icon-nav';
const STATUS_WARN = 'phui-list-item-warn';
const STATUS_FAIL = 'phui-list-item-fail';
@ -26,6 +27,7 @@ final class PHUIListItemView extends AphrontTagView {
private $statusColor;
private $order;
private $aural;
private $profileImage;
public function setAural($aural) {
$this->aural = $aural;
@ -73,6 +75,11 @@ final class PHUIListItemView extends AphrontTagView {
return $this;
}
public function setProfileImage($image) {
$this->profileImage = $image;
return $this;
}
public function getIcon() {
return $this->icon;
}
@ -170,9 +177,11 @@ final class PHUIListItemView extends AphrontTagView {
if ($this->name) {
if ($this->getRenderNameAsTooltip()) {
Javelin::initBehavior('phabricator-tooltips');
$sigil = 'has-tooltip';
$meta = array(
'tip' => $this->name,
'align' => 'E',
);
} else {
$external = null;
@ -224,6 +233,12 @@ final class PHUIListItemView extends AphrontTagView {
->setIconFont($icon_name);
}
if ($this->profileImage) {
$icon = id(new PHUIIconView())
->setHeadSize(PHUIIconView::HEAD_SMALL)
->setImage($this->profileImage);
}
if ($this->appIcon) {
$icon = id(new PHUIIconView())
->addClass('phui-list-item-icon')

View file

@ -159,25 +159,25 @@
display: block;
width: auto;
}
.device-desktop .aphront-multi-column-column-outer {
.device-desktop .dashboard-view .aphront-multi-column-column-outer {
display: block;
border: none;
}
.device-desktop .aphront-multi-column-column.mlr {
.device-desktop .dashboard-view .aphront-multi-column-column.mlr {
margin: 0;
}
.device-desktop .aphront-multi-column-fluid .aphront-multi-column-2-up
.aphront-multi-column-column-outer.half {
.device-desktop .dashboard-view .aphront-multi-column-fluid
.aphront-multi-column-2-up .aphront-multi-column-column-outer.half {
width: auto;
margin: 0 0 16px;
}
.device-desktop .aphront-multi-column-fluid .aphront-multi-column-2-up
.aphront-multi-column-column-outer.thirds {
.device-desktop .dashboard-view .aphront-multi-column-fluid
.aphront-multi-column-2-up .aphront-multi-column-column-outer.thirds {
width: auto;
margin: 0 0 16px;
}
.device-desktop .aphront-multi-column-fluid .aphront-multi-column-2-up
.aphront-multi-column-column-outer.third {
.device-desktop .dashboard-view .aphront-multi-column-fluid
.aphront-multi-column-2-up .aphront-multi-column-column-outer.third {
width: auto;
margin: 0 0 16px;
}

View file

@ -51,3 +51,13 @@
.device-tablet .phabricator-project-layout .profile-feed {
padding: 16px;
}
.phabricator-project-feed {
padding: 4px 0 12px 12px;
max-width: 640px;
}
.device-phone .phabricator-project-feed {
padding: 0 12px;
width: auto;
}

View file

@ -43,3 +43,44 @@
text-decoration: none;
background-color: {$hovergrey};
}
.device-desktop .phabricator-icon-nav .phabricator-nav-column-background,
.device-desktop .phabricator-icon-nav .phabricator-nav-local {
width: 40px;
}
.device-desktop .phabricator-icon-nav .phabricator-nav-content {
margin-left: 41px;
}
.phabricator-icon-nav .phabricator-side-menu .phui-list-item-href {
height: 40px;
width: 40px;
padding: 0;
}
.phabricator-icon-nav .phabricator-side-menu .phui-list-item-icon {
font-size: 20px;
width: 40px;
line-height: 40px;
text-align: center;
vertical-align: bottom;
}
.phabricator-icon-nav .phabricator-side-menu .phui-list-item-selected {
border: none;
}
.phabricator-icon-nav .phabricator-side-menu .phui-list-item-selected
.phui-icon-view {
color: {$sky};
}
.phabricator-icon-nav .phui-icon-view.phuihead-small {
height: 24px;
width: 24px;
border: 1px solid #fff;
background-size: 24px;
display: inline-block;
margin: 7px;
}

View file

@ -19,7 +19,7 @@
}
.phui-object-box .phui-object-item-list-view.phui-object-list-flush {
padding: 8px 12px 4px 12px;
padding: 8px 8px 4px 8px;
background-color: #E5E8EE;
}
@ -38,7 +38,7 @@
border-color: {$lightgreyborder};
margin: 5px 0;
overflow: hidden;
border-left-width: 6px;
border-left-width: 4px;
background: #fff;
margin-bottom: 4px;
}

View file

@ -73,6 +73,15 @@
margin: 0 3px;
}
.device-desktop .project-board-wrapper .phui-workboard-view-shadow {
left: 53px;
}
.device-desktop .phui-workboard-view .aphront-multi-column-fixed
.aphront-multi-column-inner {
margin-left: 0;
}
.device-tablet .project-board-wrapper {
margin-left: 8px;
margin-right: 8px;