Skip to content

Commit 102befd

Browse files
committedMay 23, 2014
Project - add ability to select an icon for typeaheads and such
Summary: Fixes T5090. Introduced getIcon into Handle stack which allows you to specify a per handle icon. getIcon falls back ot getTypeIcon. Test Plan: changed the icon on a project a bunch. verified transactions showed up. verified icon showed up in typeahead. verified icon showed up in tokens that were pre-generated (not typed in). units test passed. Reviewers: chad, epriestley Reviewed By: epriestley Subscribers: epriestley, Korvin Maniphest Tasks: T5090 Differential Revision: https://secure.phabricator.com/D9264
1 parent 217e0e7 commit 102befd

16 files changed

+229
-13
lines changed
 

‎resources/celerity/map.php

+2
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
'rsrc/css/application/ponder/post.css' => 'ebab8a70',
9595
'rsrc/css/application/ponder/vote.css' => '8ed6ed8b',
9696
'rsrc/css/application/profile/profile-view.css' => '33e6f703',
97+
'rsrc/css/application/projects/project-icon.css' => 'd80f48b0',
9798
'rsrc/css/application/projects/project-tag.css' => '095c9404',
9899
'rsrc/css/application/releeph/releeph-core.css' => '9b3c5733',
99100
'rsrc/css/application/releeph/releeph-preview-branch.css' => 'b7a6f4a5',
@@ -788,6 +789,7 @@
788789
'ponder-feed-view-css' => 'e62615b6',
789790
'ponder-post-css' => 'ebab8a70',
790791
'ponder-vote-css' => '8ed6ed8b',
792+
'project-icon-css' => 'd80f48b0',
791793
'raphael-core' => '51ee6b43',
792794
'raphael-g' => '40dde778',
793795
'raphael-g-line' => '40da039e',
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
ALTER TABLE {$NAMESPACE}_project.project
2+
ADD COLUMN icon VARCHAR(32) NOT NULL COLLATE utf8_bin;
3+
4+
UPDATE {$NAMESPACE}_project.project
5+
SET icon = "fa-briefcase" WHERE icon = "";

‎src/__phutil_library_map__.php

+4
Original file line numberDiff line numberDiff line change
@@ -1950,9 +1950,11 @@
19501950
'PhabricatorProjectDAO' => 'applications/project/storage/PhabricatorProjectDAO.php',
19511951
'PhabricatorProjectDescriptionField' => 'applications/project/customfield/PhabricatorProjectDescriptionField.php',
19521952
'PhabricatorProjectEditDetailsController' => 'applications/project/controller/PhabricatorProjectEditDetailsController.php',
1953+
'PhabricatorProjectEditIconController' => 'applications/project/controller/PhabricatorProjectEditIconController.php',
19531954
'PhabricatorProjectEditMainController' => 'applications/project/controller/PhabricatorProjectEditMainController.php',
19541955
'PhabricatorProjectEditPictureController' => 'applications/project/controller/PhabricatorProjectEditPictureController.php',
19551956
'PhabricatorProjectEditorTestCase' => 'applications/project/editor/__tests__/PhabricatorProjectEditorTestCase.php',
1957+
'PhabricatorProjectIcon' => 'applications/project/icon/PhabricatorProjectIcon.php',
19561958
'PhabricatorProjectListController' => 'applications/project/controller/PhabricatorProjectListController.php',
19571959
'PhabricatorProjectMembersEditController' => 'applications/project/controller/PhabricatorProjectMembersEditController.php',
19581960
'PhabricatorProjectMembersRemoveController' => 'applications/project/controller/PhabricatorProjectMembersRemoveController.php',
@@ -4776,9 +4778,11 @@
47764778
'PhabricatorProjectDAO' => 'PhabricatorLiskDAO',
47774779
'PhabricatorProjectDescriptionField' => 'PhabricatorProjectStandardCustomField',
47784780
'PhabricatorProjectEditDetailsController' => 'PhabricatorProjectController',
4781+
'PhabricatorProjectEditIconController' => 'PhabricatorProjectController',
47794782
'PhabricatorProjectEditMainController' => 'PhabricatorProjectController',
47804783
'PhabricatorProjectEditPictureController' => 'PhabricatorProjectController',
47814784
'PhabricatorProjectEditorTestCase' => 'PhabricatorTestCase',
4785+
'PhabricatorProjectIcon' => 'Phobject',
47824786
'PhabricatorProjectListController' => 'PhabricatorProjectController',
47834787
'PhabricatorProjectMembersEditController' => 'PhabricatorProjectController',
47844788
'PhabricatorProjectMembersRemoveController' => 'PhabricatorProjectController',

‎src/applications/phid/PhabricatorObjectHandle.php

+13
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,26 @@ final class PhabricatorObjectHandle
1010
private $fullName;
1111
private $title;
1212
private $imageURI;
13+
private $icon;
1314
private $timestamp;
1415
private $status = PhabricatorObjectHandleStatus::STATUS_OPEN;
1516
private $complete;
1617
private $disabled;
1718
private $objectName;
1819
private $policyFiltered;
1920

21+
public function setIcon($icon) {
22+
$this->icon = $icon;
23+
return $this;
24+
}
25+
26+
public function getIcon() {
27+
if ($this->icon) {
28+
return $this->icon;
29+
}
30+
return $this->getTypeIcon();
31+
}
32+
2033
public function getTypeIcon() {
2134
if ($this->getPHIDType()) {
2235
return $this->getPHIDType()->getTypeIcon();

‎src/applications/project/application/PhabricatorApplicationProject.php

+2
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ public function getRoutes() {
5050
=> 'PhabricatorProjectProfileController',
5151
'picture/(?P<id>[1-9]\d*)/' =>
5252
'PhabricatorProjectEditPictureController',
53+
'icon/(?P<id>[1-9]\d*)/' =>
54+
'PhabricatorProjectEditIconController',
5355
'create/' => 'PhabricatorProjectCreateController',
5456
'board/(?P<id>[1-9]\d*)/'.
5557
'(?P<filter>filter/)?'.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
<?php
2+
3+
final class PhabricatorProjectEditIconController
4+
extends PhabricatorProjectController {
5+
6+
private $id;
7+
8+
public function willProcessRequest(array $data) {
9+
$this->id = $data['id'];
10+
}
11+
12+
public function processRequest() {
13+
$request = $this->getRequest();
14+
$viewer = $request->getUser();
15+
16+
$project = id(new PhabricatorProjectQuery())
17+
->setViewer($viewer)
18+
->withIDs(array($this->id))
19+
->requireCapabilities(
20+
array(
21+
PhabricatorPolicyCapability::CAN_VIEW,
22+
PhabricatorPolicyCapability::CAN_EDIT,
23+
))
24+
->executeOne();
25+
if (!$project) {
26+
return new Aphront404Response();
27+
}
28+
29+
$view_uri = '/tag/'.$project->getPrimarySlug().'/';
30+
$edit_uri = $this->getApplicationURI('edit/'.$project->getID().'/');
31+
32+
if ($request->isFormPost()) {
33+
$v_icon = $request->getStr('icon');
34+
$type_icon = PhabricatorProjectTransaction::TYPE_ICON;
35+
$xactions = array(id(new PhabricatorProjectTransaction())
36+
->setTransactionType($type_icon)
37+
->setNewValue($v_icon));
38+
39+
$editor = id(new PhabricatorProjectTransactionEditor())
40+
->setActor($viewer)
41+
->setContentSourceFromRequest($request)
42+
->setContinueOnMissingFields(true)
43+
->setContinueOnNoEffect(true);
44+
45+
$editor->applyTransactions($project, $xactions);
46+
47+
return id(new AphrontReloadResponse())->setURI($edit_uri);
48+
}
49+
50+
require_celerity_resource('project-icon-css');
51+
Javelin::initBehavior('phabricator-tooltips');
52+
53+
$project_icons = PhabricatorProjectIcon::getIconMap();
54+
$ii = 0;
55+
$buttons = array();
56+
foreach ($project_icons as $icon => $label) {
57+
$view = id(new PHUIIconView())
58+
->setIconFont($icon.' bluegrey');
59+
60+
$aural = javelin_tag(
61+
'span',
62+
array(
63+
'aural' => true,
64+
),
65+
pht('Choose "%s" Icon', $label));
66+
67+
if ($icon == $project->getIcon()) {
68+
$class_extra = ' selected';
69+
$tip = $label . pht(' - selected');
70+
} else {
71+
$class_extra = null;
72+
$tip = $label;
73+
}
74+
75+
$buttons[] = javelin_tag(
76+
'button',
77+
array(
78+
'class' => 'icon-button'.$class_extra,
79+
'name' => 'icon',
80+
'value' => $icon,
81+
'type' => 'submit',
82+
'sigil' => 'has-tooltip',
83+
'meta' => array(
84+
'tip' => $tip,
85+
)
86+
),
87+
array(
88+
$aural,
89+
$view,
90+
));
91+
if ((++$ii % 4) == 0) {
92+
$buttons[] = phutil_tag('br');
93+
}
94+
}
95+
96+
$buttons = phutil_tag(
97+
'div',
98+
array(
99+
'class' => 'icon-grid',
100+
),
101+
$buttons);
102+
103+
$dialog = id(new AphrontDialogView())
104+
->setUser($viewer)
105+
->setTitle(pht('Choose Project Icon'))
106+
->appendChild($buttons)
107+
->addCancelButton($edit_uri);
108+
109+
return id(new AphrontDialogResponse())->setDialog($dialog);
110+
}
111+
}

‎src/applications/project/controller/PhabricatorProjectEditMainController.php

+8
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,14 @@ private function buildActionListView(PhabricatorProject $project) {
9494
->setDisabled(!$can_edit)
9595
->setWorkflow(!$can_edit));
9696

97+
$view->addAction(
98+
id(new PhabricatorActionView())
99+
->setName(pht('Edit Icon'))
100+
->setIcon($project->getIcon())
101+
->setHref($this->getApplicationURI("icon/{$id}/"))
102+
->setDisabled(!$can_edit)
103+
->setWorkflow(true));
104+
97105
$view->addAction(
98106
id(new PhabricatorActionView())
99107
->setName(pht('Edit Picture'))

‎src/applications/project/editor/PhabricatorProjectTransactionEditor.php

+9
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public function getTransactionTypes() {
1515
$types[] = PhabricatorProjectTransaction::TYPE_SLUGS;
1616
$types[] = PhabricatorProjectTransaction::TYPE_STATUS;
1717
$types[] = PhabricatorProjectTransaction::TYPE_IMAGE;
18+
$types[] = PhabricatorProjectTransaction::TYPE_ICON;
1819

1920
return $types;
2021
}
@@ -35,6 +36,8 @@ protected function getCustomTransactionOldValue(
3536
return $object->getStatus();
3637
case PhabricatorProjectTransaction::TYPE_IMAGE:
3738
return $object->getProfileImagePHID();
39+
case PhabricatorProjectTransaction::TYPE_ICON:
40+
return $object->getIcon();
3841
}
3942

4043
return parent::getCustomTransactionOldValue($object, $xaction);
@@ -49,6 +52,7 @@ protected function getCustomTransactionNewValue(
4952
case PhabricatorProjectTransaction::TYPE_SLUGS:
5053
case PhabricatorProjectTransaction::TYPE_STATUS:
5154
case PhabricatorProjectTransaction::TYPE_IMAGE:
55+
case PhabricatorProjectTransaction::TYPE_ICON:
5256
return $xaction->getNewValue();
5357
}
5458

@@ -72,6 +76,9 @@ protected function applyCustomInternalTransaction(
7276
case PhabricatorProjectTransaction::TYPE_IMAGE:
7377
$object->setProfileImagePHID($xaction->getNewValue());
7478
return;
79+
case PhabricatorProjectTransaction::TYPE_ICON:
80+
$object->setIcon($xaction->getNewValue());
81+
return;
7582
case PhabricatorTransactions::TYPE_EDGE:
7683
return;
7784
case PhabricatorTransactions::TYPE_VIEW_POLICY:
@@ -173,6 +180,7 @@ protected function applyCustomExternalTransaction(
173180
case PhabricatorTransactions::TYPE_JOIN_POLICY:
174181
case PhabricatorProjectTransaction::TYPE_STATUS:
175182
case PhabricatorProjectTransaction::TYPE_IMAGE:
183+
case PhabricatorProjectTransaction::TYPE_ICON:
176184
return;
177185
case PhabricatorTransactions::TYPE_EDGE:
178186
$edge_type = $xaction->getMetadataValue('edge:type');
@@ -342,6 +350,7 @@ protected function requireCapabilities(
342350
case PhabricatorProjectTransaction::TYPE_NAME:
343351
case PhabricatorProjectTransaction::TYPE_STATUS:
344352
case PhabricatorProjectTransaction::TYPE_IMAGE:
353+
case PhabricatorProjectTransaction::TYPE_ICON:
345354
PhabricatorPolicyFilter::requireCapability(
346355
$this->requireActor(),
347356
$object,

‎src/applications/project/editor/__tests__/PhabricatorProjectEditorTestCase.php

+6-11
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@ public function testViewProject() {
1515
$user2 = $this->createUser();
1616
$user2->save();
1717

18-
$proj = $this->createProject();
19-
$proj->setAuthorPHID($user->getPHID());
20-
$proj->save();
18+
$proj = $this->createProject($user);
2119

2220
$proj = $this->refreshProject($proj, $user, true);
2321

@@ -48,9 +46,7 @@ public function testEditProject() {
4846
$user2 = $this->createUser();
4947
$user2->save();
5048

51-
$proj = $this->createProject();
52-
$proj->setAuthorPHID($user->getPHID());
53-
$proj->save();
49+
$proj = $this->createProject($user);
5450

5551

5652
// When edit and view policies are set to "user", anyone can edit.
@@ -100,7 +96,6 @@ public function testJoinLeaveProject() {
10096
$user->save();
10197

10298
$proj = $this->createProjectWithNewAuthor();
103-
$proj->save();
10499

105100
$proj = $this->refreshProject($proj, $user, true);
106101
$this->assertTrue(
@@ -228,9 +223,10 @@ private function refreshProject(
228223
}
229224
}
230225

231-
private function createProject() {
232-
$project = new PhabricatorProject();
226+
private function createProject(PhabricatorUser $user) {
227+
$project = PhabricatorProject::initializeNewProject($user);
233228
$project->setName('Test Project '.mt_rand());
229+
$project->save();
234230

235231
return $project;
236232
}
@@ -239,8 +235,7 @@ private function createProjectWithNewAuthor() {
239235
$author = $this->createUser();
240236
$author->save();
241237

242-
$project = $this->createProject();
243-
$project->setAuthorPHID($author->getPHID());
238+
$project = $this->createProject($author);
244239

245240
return $project;
246241
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
final class PhabricatorProjectIcon extends Phobject {
4+
5+
public static function getIconMap() {
6+
return
7+
array(
8+
'fa-briefcase' => pht('Briefcase'),
9+
'fa-tags' => pht('Tag'),
10+
'fa-folder' => pht('Folder'),
11+
'fa-users' => pht('Team'),
12+
'fa-bug' => pht('Bug'),
13+
'fa-trash-o' => pht('Garbage'),
14+
'fa-calendar' => pht('Deadline'),
15+
'fa-flag-checkered' => pht('Goal'),
16+
'fa-envelope' => pht('Communication'),
17+
'fa-truck' => pht('Release'),
18+
'fa-lock' => pht('Policy'),
19+
'fa-umbrella' => pht('An Umbrella'),
20+
);
21+
}
22+
23+
public static function getLabel($key) {
24+
$map = self::getIconMap();
25+
return $map[$key];
26+
}
27+
}

‎src/applications/project/phid/PhabricatorProjectPHIDTypeProject.php

+1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ public function loadHandles(
4949
$handle->setObjectName('#'.$slug);
5050
$handle->setURI("/tag/{$slug}/");
5151
$handle->setImageURI($project->getProfileImageURI());
52+
$handle->setIcon($project->getIcon());
5253

5354
if ($project->isArchived()) {
5455
$handle->setStatus(PhabricatorObjectHandleStatus::STATUS_CLOSED);

‎src/applications/project/storage/PhabricatorProject.php

+4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ final class PhabricatorProject extends PhabricatorProjectDAO
1313
protected $subprojectPHIDs = array();
1414
protected $phrictionSlug;
1515
protected $profileImagePHID;
16+
protected $icon;
1617

1718
protected $viewPolicy;
1819
protected $editPolicy;
@@ -26,10 +27,13 @@ final class PhabricatorProject extends PhabricatorProjectDAO
2627
private $profileImageFile = self::ATTACHABLE;
2728
private $slugs = self::ATTACHABLE;
2829

30+
const DEFAULT_ICON = 'fa-briefcase';
31+
2932
public static function initializeNewProject(PhabricatorUser $actor) {
3033
return id(new PhabricatorProject())
3134
->setName('')
3235
->setAuthorPHID($actor->getPHID())
36+
->setIcon(self::DEFAULT_ICON)
3337
->setViewPolicy(PhabricatorPolicies::POLICY_USER)
3438
->setEditPolicy(PhabricatorPolicies::POLICY_USER)
3539
->setJoinPolicy(PhabricatorPolicies::POLICY_USER)

‎src/applications/project/storage/PhabricatorProjectTransaction.php

+7
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ final class PhabricatorProjectTransaction
77
const TYPE_SLUGS = 'project:slugs';
88
const TYPE_STATUS = 'project:status';
99
const TYPE_IMAGE = 'project:image';
10+
const TYPE_ICON = 'project:icon';
1011

1112
// NOTE: This is deprecated, members are just a normal edge now.
1213
const TYPE_MEMBERS = 'project:members';
@@ -86,6 +87,12 @@ public function getTitle() {
8687
$this->renderHandleLink($new));
8788
}
8889

90+
case PhabricatorProjectTransaction::TYPE_ICON:
91+
return pht(
92+
'%s set this project\'s icon to %s.',
93+
$author_handle,
94+
PhabricatorProjectIcon::getLabel($new));
95+
8996
case PhabricatorProjectTransaction::TYPE_SLUGS:
9097
$add = array_diff($new, $old);
9198
$rem = array_diff($old, $new);

0 commit comments

Comments
 (0)
Failed to load comments.