Skip to content

Commit 7c5ad63

Browse files
author
epriestley
committed
Add very basic UI for creating milestones and subprojects
Summary: Ref T10010. This has a lot of UI/UX problems but I think it: - technically allows subproject creation; - technically allows milestone creation; - doesn't let users unwittingly destroy their installs (probably). Test Plan: - Created milestones. - Created subprojects. - Created and edited normal projects. - Observed some reasonable interactions (e.g., you can't create milestones for a milestone or edit a superproject's members). - Observed plenty of silly/confusing interactions that need additional work. {F1046657} {F1046658} {F1046655} {F1046656} {F1046654} Reviewers: chad Reviewed By: chad Maniphest Tasks: T10010 Differential Revision: https://secure.phabricator.com/D14904
1 parent 7732f9c commit 7c5ad63

14 files changed

+583
-84
lines changed

resources/celerity/map.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88
return array(
99
'names' => array(
10-
'core.pkg.css' => 'a419cf4b',
10+
'core.pkg.css' => '3ea6dc33',
1111
'core.pkg.js' => '57dff7df',
1212
'darkconsole.pkg.js' => 'e7393ebb',
1313
'differential.pkg.css' => '2de124c9',
@@ -114,7 +114,7 @@
114114
'rsrc/css/font/phui-font-icon-base.css' => 'ecbbb4c2',
115115
'rsrc/css/layout/phabricator-filetree-view.css' => 'fccf9f82',
116116
'rsrc/css/layout/phabricator-hovercard-view.css' => '1239cd52',
117-
'rsrc/css/layout/phabricator-side-menu-view.css' => 'bec2458e',
117+
'rsrc/css/layout/phabricator-side-menu-view.css' => '91b7a42c',
118118
'rsrc/css/layout/phabricator-source-code-view.css' => 'cbeef983',
119119
'rsrc/css/phui/calendar/phui-calendar-day.css' => 'd1cf6f93',
120120
'rsrc/css/phui/calendar/phui-calendar-list.css' => 'c1c7f338',
@@ -762,7 +762,7 @@
762762
'phabricator-remarkup-css' => '7afb543c',
763763
'phabricator-search-results-css' => '7dea472c',
764764
'phabricator-shaped-request' => '7cbe244b',
765-
'phabricator-side-menu-view-css' => 'bec2458e',
765+
'phabricator-side-menu-view-css' => '91b7a42c',
766766
'phabricator-slowvote-css' => 'da0afb1b',
767767
'phabricator-source-code-view-css' => 'cbeef983',
768768
'phabricator-standard-page-view' => '3c99cdf4',

src/__phutil_library_map__.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2855,6 +2855,7 @@
28552855
'PhabricatorProjectIconSet' => 'applications/project/icon/PhabricatorProjectIconSet.php',
28562856
'PhabricatorProjectInterface' => 'applications/project/interface/PhabricatorProjectInterface.php',
28572857
'PhabricatorProjectListController' => 'applications/project/controller/PhabricatorProjectListController.php',
2858+
'PhabricatorProjectListView' => 'applications/project/view/PhabricatorProjectListView.php',
28582859
'PhabricatorProjectLockController' => 'applications/project/controller/PhabricatorProjectLockController.php',
28592860
'PhabricatorProjectLogicalAndDatasource' => 'applications/project/typeahead/PhabricatorProjectLogicalAndDatasource.php',
28602861
'PhabricatorProjectLogicalDatasource' => 'applications/project/typeahead/PhabricatorProjectLogicalDatasource.php',
@@ -7206,6 +7207,7 @@
72067207
'PhabricatorProjectHeraldAction' => 'HeraldAction',
72077208
'PhabricatorProjectIconSet' => 'PhabricatorIconSet',
72087209
'PhabricatorProjectListController' => 'PhabricatorProjectController',
7210+
'PhabricatorProjectListView' => 'AphrontView',
72097211
'PhabricatorProjectLockController' => 'PhabricatorProjectController',
72107212
'PhabricatorProjectLogicalAndDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
72117213
'PhabricatorProjectLogicalDatasource' => 'PhabricatorTypeaheadCompositeDatasource',

src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -748,15 +748,15 @@ private function createProject(
748748
->setNewValue($name);
749749

750750
if ($parent) {
751-
$xactions[] = id(new PhabricatorProjectTransaction())
752-
->setTransactionType(PhabricatorProjectTransaction::TYPE_PARENT)
753-
->setNewValue($parent->getPHID());
754-
}
755-
756-
if ($is_milestone) {
757-
$xactions[] = id(new PhabricatorProjectTransaction())
758-
->setTransactionType(PhabricatorProjectTransaction::TYPE_MILESTONE)
759-
->setNewValue(true);
751+
if ($is_milestone) {
752+
$xactions[] = id(new PhabricatorProjectTransaction())
753+
->setTransactionType(PhabricatorProjectTransaction::TYPE_MILESTONE)
754+
->setNewValue($parent->getPHID());
755+
} else {
756+
$xactions[] = id(new PhabricatorProjectTransaction())
757+
->setTransactionType(PhabricatorProjectTransaction::TYPE_PARENT)
758+
->setNewValue($parent->getPHID());
759+
}
760760
}
761761

762762
$this->applyTransactions($project, $user, $xactions);

src/applications/project/controller/PhabricatorProjectController.php

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,6 @@ public function buildSideNavView($for_app = false) {
9999
$nav->addFilter("board/{$id}/", pht('Workboard'));
100100
$nav->addFilter("members/{$id}/", pht('Members'));
101101
$nav->addFilter("feed/{$id}/", pht('Feed'));
102-
$nav->addFilter("details/{$id}/", pht('Edit Details'));
103102
}
104103
$nav->addFilter('create', pht('Create Project'));
105104
}
@@ -149,11 +148,29 @@ public function buildIconNavView(PhabricatorProject $project) {
149148

150149
$nav->addIcon("feed/{$id}/", pht('Feed'), 'fa-newspaper-o');
151150
$nav->addIcon("members/{$id}/", pht('Members'), 'fa-group');
152-
$nav->addIcon("details/{$id}/", pht('Edit Details'), 'fa-pencil');
153151

154152
if (PhabricatorEnv::getEnvConfig('phabricator.show-prototypes')) {
155-
$nav->addIcon("subprojects/{$id}/", pht('Subprojects'), 'fa-sitemap');
156-
$nav->addIcon("milestones/{$id}/", pht('Milestones'), 'fa-map-marker');
153+
if ($project->supportsSubprojects()) {
154+
$subprojects_icon = 'fa-sitemap';
155+
} else {
156+
$subprojects_icon = 'fa-sitemap grey';
157+
}
158+
159+
if ($project->supportsMilestones()) {
160+
$milestones_icon = 'fa-map-marker';
161+
} else {
162+
$milestones_icon = 'fa-map-marker grey';
163+
}
164+
165+
$nav->addIcon(
166+
"subprojects/{$id}/",
167+
pht('Subprojects'),
168+
$subprojects_icon);
169+
170+
$nav->addIcon(
171+
"milestones/{$id}/",
172+
pht('Milestones'),
173+
$milestones_icon);
157174
}
158175

159176

@@ -170,8 +187,8 @@ protected function buildApplicationCrumbs() {
170187
$ancestors[] = $project;
171188
foreach ($ancestors as $ancestor) {
172189
$crumbs->addTextCrumb(
173-
$project->getName(),
174-
$project->getURI());
190+
$ancestor->getName(),
191+
$ancestor->getURI());
175192
}
176193
}
177194

src/applications/project/controller/PhabricatorProjectEditController.php

Lines changed: 104 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,111 @@
33
final class PhabricatorProjectEditController
44
extends PhabricatorProjectController {
55

6+
private $engine;
7+
8+
public function setEngine(PhabricatorProjectEditEngine $engine) {
9+
$this->engine = $engine;
10+
return $this;
11+
}
12+
13+
public function getEngine() {
14+
return $this->engine;
15+
}
16+
617
public function handleRequest(AphrontRequest $request) {
7-
return id(new PhabricatorProjectEditEngine())
8-
->setController($this)
9-
->buildResponse();
18+
$viewer = $this->getViewer();
19+
20+
$engine = id(new PhabricatorProjectEditEngine())
21+
->setController($this);
22+
23+
$this->setEngine($engine);
24+
25+
$id = $request->getURIData('id');
26+
if (!$id) {
27+
$parent_id = head($request->getArr('parent'));
28+
if (!$parent_id) {
29+
$parent_id = $request->getStr('parent');
30+
}
31+
32+
if ($parent_id) {
33+
$is_milestone = false;
34+
} else {
35+
$parent_id = head($request->getArr('milestone'));
36+
if (!$parent_id) {
37+
$parent_id = $request->getStr('milestone');
38+
}
39+
$is_milestone = true;
40+
}
41+
42+
if ($parent_id) {
43+
$query = id(new PhabricatorProjectQuery())
44+
->setViewer($viewer)
45+
->requireCapabilities(
46+
array(
47+
PhabricatorPolicyCapability::CAN_VIEW,
48+
PhabricatorPolicyCapability::CAN_EDIT,
49+
));
50+
51+
if (ctype_digit($parent_id)) {
52+
$query->withIDs(array($parent_id));
53+
} else {
54+
$query->withPHIDs(array($parent_id));
55+
}
56+
57+
$parent = $query->executeOne();
58+
59+
if ($is_milestone) {
60+
if (!$parent->supportsMilestones()) {
61+
$cancel_uri = "/project/milestones/{$parent_id}/";
62+
return $this->newDialog()
63+
->setTitle(pht('No Milestones'))
64+
->appendParagraph(
65+
pht('You can not add milestones to this project.'))
66+
->addCancelButton($cancel_uri);
67+
}
68+
$engine->setMilestoneProject($parent);
69+
} else {
70+
if (!$parent->supportsSubprojects()) {
71+
$cancel_uri = "/project/subprojects/{$parent_id}/";
72+
return $this->newDialog()
73+
->setTitle(pht('No Subprojects'))
74+
->appendParagraph(
75+
pht('You can not add subprojects to this project.'))
76+
->addCancelButton($cancel_uri);
77+
}
78+
$engine->setParentProject($parent);
79+
}
80+
81+
$this->setProject($parent);
82+
}
83+
}
84+
85+
return $engine->buildResponse();
86+
}
87+
88+
protected function buildApplicationCrumbs() {
89+
$crumbs = parent::buildApplicationCrumbs();
90+
91+
$engine = $this->getEngine();
92+
if ($engine) {
93+
$parent = $engine->getParentProject();
94+
if ($parent) {
95+
$id = $parent->getID();
96+
$crumbs->addTextCrumb(
97+
pht('Subprojects'),
98+
$this->getApplicationURI("subprojects/{$id}/"));
99+
}
100+
101+
$milestone = $engine->getMilestoneProject();
102+
if ($milestone) {
103+
$id = $milestone->getID();
104+
$crumbs->addTextCrumb(
105+
pht('Milestones'),
106+
$this->getApplicationURI("milestones/{$id}/"));
107+
}
108+
}
109+
110+
return $crumbs;
10111
}
11112

12113
}

src/applications/project/controller/PhabricatorProjectMembersEditController.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,11 @@ public function handleRequest(AphrontRequest $request) {
6868
$project,
6969
PhabricatorPolicyCapability::CAN_EDIT);
7070

71+
$supports_edit = $project->supportsEditMembers();
72+
7173
$form_box = null;
7274
$title = pht('Add Members');
73-
if ($can_edit) {
75+
if ($can_edit && $supports_edit) {
7476
$header_name = pht('Edit Members');
7577
$view_uri = $this->getApplicationURI('profile/'.$project->getID().'/');
7678

src/applications/project/controller/PhabricatorProjectMilestonesController.php

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,64 @@ public function handleRequest(AphrontRequest $request) {
1818
$project = $this->getProject();
1919
$id = $project->getID();
2020

21+
$can_edit = PhabricatorPolicyFilter::hasCapability(
22+
$viewer,
23+
$project,
24+
PhabricatorPolicyCapability::CAN_EDIT);
25+
26+
$has_support = $project->supportsMilestones();
27+
if ($has_support) {
28+
$milestones = id(new PhabricatorProjectQuery())
29+
->setViewer($viewer)
30+
->withParentProjectPHIDs(array($project->getPHID()))
31+
->needImages(true)
32+
->withIsMilestone(true)
33+
->setOrder('newest')
34+
->execute();
35+
} else {
36+
$milestones = array();
37+
}
38+
39+
$can_create = $can_edit && $has_support;
40+
41+
if ($project->getHasMilestones()) {
42+
$button_text = pht('Create Next Milestone');
43+
} else {
44+
$button_text = pht('Add Milestones');
45+
}
46+
47+
$header = id(new PHUIHeaderView())
48+
->setHeader(pht('Milestones'))
49+
->addActionLink(
50+
id(new PHUIButtonView())
51+
->setTag('a')
52+
->setHref("/project/edit/?milestone={$id}")
53+
->setIconFont('fa-plus')
54+
->setDisabled(!$can_create)
55+
->setWorkflow(!$can_create)
56+
->setText($button_text));
57+
58+
$box = id(new PHUIObjectBoxView())
59+
->setHeader($header);
60+
61+
if (!$has_support) {
62+
$no_support = pht(
63+
'This project is a milestone. Milestones can not have their own '.
64+
'milestones.');
65+
66+
$info_view = id(new PHUIInfoView())
67+
->setErrors(array($no_support))
68+
->setSeverity(PHUIInfoView::SEVERITY_WARNING);
69+
70+
$box->setInfoView($info_view);
71+
}
72+
73+
$box->setObjectList(
74+
id(new PhabricatorProjectListView())
75+
->setUser($viewer)
76+
->setProjects($milestones)
77+
->renderList());
78+
2179
$nav = $this->buildIconNavView($project);
2280
$nav->selectFilter("milestones/{$id}/");
2381

@@ -27,7 +85,8 @@ public function handleRequest(AphrontRequest $request) {
2785
return $this->newPage()
2886
->setNavigation($nav)
2987
->setCrumbs($crumbs)
30-
->setTitle(array($project->getName(), pht('Milestones')));
88+
->setTitle(array($project->getName(), pht('Milestones')))
89+
->appendChild($box);
3190
}
3291

3392
}

src/applications/project/controller/PhabricatorProjectSubprojectsController.php

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,63 @@ public function handleRequest(AphrontRequest $request) {
1818
$project = $this->getProject();
1919
$id = $project->getID();
2020

21+
$can_edit = PhabricatorPolicyFilter::hasCapability(
22+
$viewer,
23+
$project,
24+
PhabricatorPolicyCapability::CAN_EDIT);
25+
26+
$has_support = $project->supportsSubprojects();
27+
28+
if ($has_support) {
29+
$subprojects = id(new PhabricatorProjectQuery())
30+
->setViewer($viewer)
31+
->withParentProjectPHIDs(array($project->getPHID()))
32+
->needImages(true)
33+
->withIsMilestone(false)
34+
->execute();
35+
} else {
36+
$subprojects = array();
37+
}
38+
39+
$can_create = $can_edit && $has_support;
40+
41+
if ($project->getHasSubprojects()) {
42+
$button_text = pht('Create Subproject');
43+
} else {
44+
$button_text = pht('Add Subprojects');
45+
}
46+
47+
$header = id(new PHUIHeaderView())
48+
->setHeader(pht('Subprojects'))
49+
->addActionLink(
50+
id(new PHUIButtonView())
51+
->setTag('a')
52+
->setHref("/project/edit/?parent={$id}")
53+
->setIconFont('fa-plus')
54+
->setDisabled(!$can_create)
55+
->setWorkflow(!$can_create)
56+
->setText($button_text));
57+
58+
$box = id(new PHUIObjectBoxView())
59+
->setHeader($header);
60+
61+
if (!$has_support) {
62+
$no_support = pht(
63+
'This project is a milestone. Milestones can not have subprojects.');
64+
65+
$info_view = id(new PHUIInfoView())
66+
->setErrors(array($no_support))
67+
->setSeverity(PHUIInfoView::SEVERITY_WARNING);
68+
69+
$box->setInfoView($info_view);
70+
}
71+
72+
$box->setObjectList(
73+
id(new PhabricatorProjectListView())
74+
->setUser($viewer)
75+
->setProjects($subprojects)
76+
->renderList());
77+
2178
$nav = $this->buildIconNavView($project);
2279
$nav->selectFilter("subprojects/{$id}/");
2380

@@ -27,7 +84,8 @@ public function handleRequest(AphrontRequest $request) {
2784
return $this->newPage()
2885
->setNavigation($nav)
2986
->setCrumbs($crumbs)
30-
->setTitle(array($project->getName(), pht('Subprojects')));
87+
->setTitle(array($project->getName(), pht('Subprojects')))
88+
->appendChild($box);
3189
}
3290

3391
}

0 commit comments

Comments
 (0)