1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-25 16:22:43 +01:00

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
This commit is contained in:
Bob Trahan 2014-05-23 10:41:24 -07:00
parent 217e0e7fe1
commit 102befdede
16 changed files with 229 additions and 13 deletions

View file

@ -94,6 +94,7 @@ return array(
'rsrc/css/application/ponder/post.css' => 'ebab8a70',
'rsrc/css/application/ponder/vote.css' => '8ed6ed8b',
'rsrc/css/application/profile/profile-view.css' => '33e6f703',
'rsrc/css/application/projects/project-icon.css' => 'd80f48b0',
'rsrc/css/application/projects/project-tag.css' => '095c9404',
'rsrc/css/application/releeph/releeph-core.css' => '9b3c5733',
'rsrc/css/application/releeph/releeph-preview-branch.css' => 'b7a6f4a5',
@ -788,6 +789,7 @@ return array(
'ponder-feed-view-css' => 'e62615b6',
'ponder-post-css' => 'ebab8a70',
'ponder-vote-css' => '8ed6ed8b',
'project-icon-css' => 'd80f48b0',
'raphael-core' => '51ee6b43',
'raphael-g' => '40dde778',
'raphael-g-line' => '40da039e',

View file

@ -0,0 +1,5 @@
ALTER TABLE {$NAMESPACE}_project.project
ADD COLUMN icon VARCHAR(32) NOT NULL COLLATE utf8_bin;
UPDATE {$NAMESPACE}_project.project
SET icon = "fa-briefcase" WHERE icon = "";

View file

@ -1950,9 +1950,11 @@ phutil_register_library_map(array(
'PhabricatorProjectDAO' => 'applications/project/storage/PhabricatorProjectDAO.php',
'PhabricatorProjectDescriptionField' => 'applications/project/customfield/PhabricatorProjectDescriptionField.php',
'PhabricatorProjectEditDetailsController' => 'applications/project/controller/PhabricatorProjectEditDetailsController.php',
'PhabricatorProjectEditIconController' => 'applications/project/controller/PhabricatorProjectEditIconController.php',
'PhabricatorProjectEditMainController' => 'applications/project/controller/PhabricatorProjectEditMainController.php',
'PhabricatorProjectEditPictureController' => 'applications/project/controller/PhabricatorProjectEditPictureController.php',
'PhabricatorProjectEditorTestCase' => 'applications/project/editor/__tests__/PhabricatorProjectEditorTestCase.php',
'PhabricatorProjectIcon' => 'applications/project/icon/PhabricatorProjectIcon.php',
'PhabricatorProjectListController' => 'applications/project/controller/PhabricatorProjectListController.php',
'PhabricatorProjectMembersEditController' => 'applications/project/controller/PhabricatorProjectMembersEditController.php',
'PhabricatorProjectMembersRemoveController' => 'applications/project/controller/PhabricatorProjectMembersRemoveController.php',
@ -4776,9 +4778,11 @@ phutil_register_library_map(array(
'PhabricatorProjectDAO' => 'PhabricatorLiskDAO',
'PhabricatorProjectDescriptionField' => 'PhabricatorProjectStandardCustomField',
'PhabricatorProjectEditDetailsController' => 'PhabricatorProjectController',
'PhabricatorProjectEditIconController' => 'PhabricatorProjectController',
'PhabricatorProjectEditMainController' => 'PhabricatorProjectController',
'PhabricatorProjectEditPictureController' => 'PhabricatorProjectController',
'PhabricatorProjectEditorTestCase' => 'PhabricatorTestCase',
'PhabricatorProjectIcon' => 'Phobject',
'PhabricatorProjectListController' => 'PhabricatorProjectController',
'PhabricatorProjectMembersEditController' => 'PhabricatorProjectController',
'PhabricatorProjectMembersRemoveController' => 'PhabricatorProjectController',

View file

@ -10,6 +10,7 @@ final class PhabricatorObjectHandle
private $fullName;
private $title;
private $imageURI;
private $icon;
private $timestamp;
private $status = PhabricatorObjectHandleStatus::STATUS_OPEN;
private $complete;
@ -17,6 +18,18 @@ final class PhabricatorObjectHandle
private $objectName;
private $policyFiltered;
public function setIcon($icon) {
$this->icon = $icon;
return $this;
}
public function getIcon() {
if ($this->icon) {
return $this->icon;
}
return $this->getTypeIcon();
}
public function getTypeIcon() {
if ($this->getPHIDType()) {
return $this->getPHIDType()->getTypeIcon();

View file

@ -50,6 +50,8 @@ final class PhabricatorApplicationProject extends PhabricatorApplication {
=> 'PhabricatorProjectProfileController',
'picture/(?P<id>[1-9]\d*)/' =>
'PhabricatorProjectEditPictureController',
'icon/(?P<id>[1-9]\d*)/' =>
'PhabricatorProjectEditIconController',
'create/' => 'PhabricatorProjectCreateController',
'board/(?P<id>[1-9]\d*)/'.
'(?P<filter>filter/)?'.

View file

@ -0,0 +1,111 @@
<?php
final class PhabricatorProjectEditIconController
extends PhabricatorProjectController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$project = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->withIDs(array($this->id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$project) {
return new Aphront404Response();
}
$view_uri = '/tag/'.$project->getPrimarySlug().'/';
$edit_uri = $this->getApplicationURI('edit/'.$project->getID().'/');
if ($request->isFormPost()) {
$v_icon = $request->getStr('icon');
$type_icon = PhabricatorProjectTransaction::TYPE_ICON;
$xactions = array(id(new PhabricatorProjectTransaction())
->setTransactionType($type_icon)
->setNewValue($v_icon));
$editor = id(new PhabricatorProjectTransactionEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnMissingFields(true)
->setContinueOnNoEffect(true);
$editor->applyTransactions($project, $xactions);
return id(new AphrontReloadResponse())->setURI($edit_uri);
}
require_celerity_resource('project-icon-css');
Javelin::initBehavior('phabricator-tooltips');
$project_icons = PhabricatorProjectIcon::getIconMap();
$ii = 0;
$buttons = array();
foreach ($project_icons as $icon => $label) {
$view = id(new PHUIIconView())
->setIconFont($icon.' bluegrey');
$aural = javelin_tag(
'span',
array(
'aural' => true,
),
pht('Choose "%s" Icon', $label));
if ($icon == $project->getIcon()) {
$class_extra = ' selected';
$tip = $label . pht(' - selected');
} else {
$class_extra = null;
$tip = $label;
}
$buttons[] = javelin_tag(
'button',
array(
'class' => 'icon-button'.$class_extra,
'name' => 'icon',
'value' => $icon,
'type' => 'submit',
'sigil' => 'has-tooltip',
'meta' => array(
'tip' => $tip,
)
),
array(
$aural,
$view,
));
if ((++$ii % 4) == 0) {
$buttons[] = phutil_tag('br');
}
}
$buttons = phutil_tag(
'div',
array(
'class' => 'icon-grid',
),
$buttons);
$dialog = id(new AphrontDialogView())
->setUser($viewer)
->setTitle(pht('Choose Project Icon'))
->appendChild($buttons)
->addCancelButton($edit_uri);
return id(new AphrontDialogResponse())->setDialog($dialog);
}
}

View file

@ -94,6 +94,14 @@ final class PhabricatorProjectEditMainController
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Icon'))
->setIcon($project->getIcon())
->setHref($this->getApplicationURI("icon/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(true));
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Picture'))

View file

@ -15,6 +15,7 @@ final class PhabricatorProjectTransactionEditor
$types[] = PhabricatorProjectTransaction::TYPE_SLUGS;
$types[] = PhabricatorProjectTransaction::TYPE_STATUS;
$types[] = PhabricatorProjectTransaction::TYPE_IMAGE;
$types[] = PhabricatorProjectTransaction::TYPE_ICON;
return $types;
}
@ -35,6 +36,8 @@ final class PhabricatorProjectTransactionEditor
return $object->getStatus();
case PhabricatorProjectTransaction::TYPE_IMAGE:
return $object->getProfileImagePHID();
case PhabricatorProjectTransaction::TYPE_ICON:
return $object->getIcon();
}
return parent::getCustomTransactionOldValue($object, $xaction);
@ -49,6 +52,7 @@ final class PhabricatorProjectTransactionEditor
case PhabricatorProjectTransaction::TYPE_SLUGS:
case PhabricatorProjectTransaction::TYPE_STATUS:
case PhabricatorProjectTransaction::TYPE_IMAGE:
case PhabricatorProjectTransaction::TYPE_ICON:
return $xaction->getNewValue();
}
@ -72,6 +76,9 @@ final class PhabricatorProjectTransactionEditor
case PhabricatorProjectTransaction::TYPE_IMAGE:
$object->setProfileImagePHID($xaction->getNewValue());
return;
case PhabricatorProjectTransaction::TYPE_ICON:
$object->setIcon($xaction->getNewValue());
return;
case PhabricatorTransactions::TYPE_EDGE:
return;
case PhabricatorTransactions::TYPE_VIEW_POLICY:
@ -173,6 +180,7 @@ final class PhabricatorProjectTransactionEditor
case PhabricatorTransactions::TYPE_JOIN_POLICY:
case PhabricatorProjectTransaction::TYPE_STATUS:
case PhabricatorProjectTransaction::TYPE_IMAGE:
case PhabricatorProjectTransaction::TYPE_ICON:
return;
case PhabricatorTransactions::TYPE_EDGE:
$edge_type = $xaction->getMetadataValue('edge:type');
@ -342,6 +350,7 @@ final class PhabricatorProjectTransactionEditor
case PhabricatorProjectTransaction::TYPE_NAME:
case PhabricatorProjectTransaction::TYPE_STATUS:
case PhabricatorProjectTransaction::TYPE_IMAGE:
case PhabricatorProjectTransaction::TYPE_ICON:
PhabricatorPolicyFilter::requireCapability(
$this->requireActor(),
$object,

View file

@ -15,9 +15,7 @@ final class PhabricatorProjectEditorTestCase extends PhabricatorTestCase {
$user2 = $this->createUser();
$user2->save();
$proj = $this->createProject();
$proj->setAuthorPHID($user->getPHID());
$proj->save();
$proj = $this->createProject($user);
$proj = $this->refreshProject($proj, $user, true);
@ -48,9 +46,7 @@ final class PhabricatorProjectEditorTestCase extends PhabricatorTestCase {
$user2 = $this->createUser();
$user2->save();
$proj = $this->createProject();
$proj->setAuthorPHID($user->getPHID());
$proj->save();
$proj = $this->createProject($user);
// When edit and view policies are set to "user", anyone can edit.
@ -100,7 +96,6 @@ final class PhabricatorProjectEditorTestCase extends PhabricatorTestCase {
$user->save();
$proj = $this->createProjectWithNewAuthor();
$proj->save();
$proj = $this->refreshProject($proj, $user, true);
$this->assertTrue(
@ -228,9 +223,10 @@ final class PhabricatorProjectEditorTestCase extends PhabricatorTestCase {
}
}
private function createProject() {
$project = new PhabricatorProject();
private function createProject(PhabricatorUser $user) {
$project = PhabricatorProject::initializeNewProject($user);
$project->setName('Test Project '.mt_rand());
$project->save();
return $project;
}
@ -239,8 +235,7 @@ final class PhabricatorProjectEditorTestCase extends PhabricatorTestCase {
$author = $this->createUser();
$author->save();
$project = $this->createProject();
$project->setAuthorPHID($author->getPHID());
$project = $this->createProject($author);
return $project;
}

View file

@ -0,0 +1,27 @@
<?php
final class PhabricatorProjectIcon extends Phobject {
public static function getIconMap() {
return
array(
'fa-briefcase' => pht('Briefcase'),
'fa-tags' => pht('Tag'),
'fa-folder' => pht('Folder'),
'fa-users' => pht('Team'),
'fa-bug' => pht('Bug'),
'fa-trash-o' => pht('Garbage'),
'fa-calendar' => pht('Deadline'),
'fa-flag-checkered' => pht('Goal'),
'fa-envelope' => pht('Communication'),
'fa-truck' => pht('Release'),
'fa-lock' => pht('Policy'),
'fa-umbrella' => pht('An Umbrella'),
);
}
public static function getLabel($key) {
$map = self::getIconMap();
return $map[$key];
}
}

View file

@ -49,6 +49,7 @@ final class PhabricatorProjectPHIDTypeProject extends PhabricatorPHIDType {
$handle->setObjectName('#'.$slug);
$handle->setURI("/tag/{$slug}/");
$handle->setImageURI($project->getProfileImageURI());
$handle->setIcon($project->getIcon());
if ($project->isArchived()) {
$handle->setStatus(PhabricatorObjectHandleStatus::STATUS_CLOSED);

View file

@ -13,6 +13,7 @@ final class PhabricatorProject extends PhabricatorProjectDAO
protected $subprojectPHIDs = array();
protected $phrictionSlug;
protected $profileImagePHID;
protected $icon;
protected $viewPolicy;
protected $editPolicy;
@ -26,10 +27,13 @@ final class PhabricatorProject extends PhabricatorProjectDAO
private $profileImageFile = self::ATTACHABLE;
private $slugs = self::ATTACHABLE;
const DEFAULT_ICON = 'fa-briefcase';
public static function initializeNewProject(PhabricatorUser $actor) {
return id(new PhabricatorProject())
->setName('')
->setAuthorPHID($actor->getPHID())
->setIcon(self::DEFAULT_ICON)
->setViewPolicy(PhabricatorPolicies::POLICY_USER)
->setEditPolicy(PhabricatorPolicies::POLICY_USER)
->setJoinPolicy(PhabricatorPolicies::POLICY_USER)

View file

@ -7,6 +7,7 @@ final class PhabricatorProjectTransaction
const TYPE_SLUGS = 'project:slugs';
const TYPE_STATUS = 'project:status';
const TYPE_IMAGE = 'project:image';
const TYPE_ICON = 'project:icon';
// NOTE: This is deprecated, members are just a normal edge now.
const TYPE_MEMBERS = 'project:members';
@ -86,6 +87,12 @@ final class PhabricatorProjectTransaction
$this->renderHandleLink($new));
}
case PhabricatorProjectTransaction::TYPE_ICON:
return pht(
'%s set this project\'s icon to %s.',
$author_handle,
PhabricatorProjectIcon::getLabel($new));
case PhabricatorProjectTransaction::TYPE_SLUGS:
$add = array_diff($new, $old);
$rem = array_diff($old, $new);

View file

@ -288,7 +288,7 @@ final class PhabricatorTypeaheadCommonDatasourceController
->setDisplayType("Project")
->setURI('/project/view/'.$proj->getID().'/')
->setPHID($proj->getPHID())
->setIcon('fa-briefcase bluegrey')
->setIcon($proj->getIcon().' bluegrey')
->setClosed($closed);
$proj_result->setImageURI($proj->getProfileImageURI());

View file

@ -62,7 +62,7 @@ final class AphrontFormTokenizerControl extends AphrontFormControl {
'id' => $id,
'src' => $this->datasource,
'value' => mpull($values, 'getFullName', 'getPHID'),
'icons' => mpull($values, 'getTypeIcon', 'getPHID'),
'icons' => mpull($values, 'getIcon', 'getPHID'),
'limit' => $this->limit,
'username' => $username,
'placeholder' => $this->placeholder,

View file

@ -0,0 +1,28 @@
/**
* @provides project-icon-css
*/
button.icon-button {
background: #f7f7f7;
border: 1px solid {$lightblueborder};
position: relative;
width: 16px;
height: 16px;
padding: 12px;
margin: 4px;
text-shadow: none;
box-shadow: none;
box-sizing: content-box;
}
.icon-grid {
text-align: center;
}
.icon-icon + .icon-icon {
margin-left: 4px;
}
button.icon-button.selected {
background: {$bluebackground};
}