mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-09 16:32:39 +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:
parent
217e0e7fe1
commit
102befdede
16 changed files with 229 additions and 13 deletions
|
@ -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',
|
||||
|
|
5
resources/sql/autopatches/20140522.projecticon.sql
Normal file
5
resources/sql/autopatches/20140522.projecticon.sql
Normal 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 = "";
|
|
@ -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',
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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/)?'.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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'))
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
27
src/applications/project/icon/PhabricatorProjectIcon.php
Normal file
27
src/applications/project/icon/PhabricatorProjectIcon.php
Normal 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];
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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,
|
||||
|
|
28
webroot/rsrc/css/application/projects/project-icon.css
Normal file
28
webroot/rsrc/css/application/projects/project-icon.css
Normal 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};
|
||||
}
|
Loading…
Reference in a new issue