mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-09 16:32:39 +01:00
Update project profile image composer for new IconSet code
Summary: Fixes T6856. Fixes T10164. - Make the profile image composer code use the underlying icon name instead of the top-level icon key, so it works instead of 404'ing. - Change the button to show a preview of the profile icon instead of the text "Use Icon and Color". - When creating a new non-milestone project, automatically set the profile image to the icon + color image. Test Plan: - Created several new projects, saw appropriate default icons. - Edited projects, saw icon previews. - Clicked icon buttons to set icons. - Poked around other applications which use builtins (Pholio, user profiles) to look for anything I broke, but everything seemed fine. Reviewers: chad Reviewed By: chad Maniphest Tasks: T6856, T10164 Differential Revision: https://secure.phabricator.com/D15050
This commit is contained in:
parent
155cb1d2c5
commit
a9a5991f01
10 changed files with 496 additions and 226 deletions
|
@ -2314,6 +2314,8 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFileinfoSetupCheck' => 'applications/config/check/PhabricatorFileinfoSetupCheck.php',
|
||||
'PhabricatorFilesApplication' => 'applications/files/application/PhabricatorFilesApplication.php',
|
||||
'PhabricatorFilesApplicationStorageEnginePanel' => 'applications/files/applicationpanel/PhabricatorFilesApplicationStorageEnginePanel.php',
|
||||
'PhabricatorFilesBuiltinFile' => 'applications/files/builtin/PhabricatorFilesBuiltinFile.php',
|
||||
'PhabricatorFilesComposeIconBuiltinFile' => 'applications/files/builtin/PhabricatorFilesComposeIconBuiltinFile.php',
|
||||
'PhabricatorFilesConfigOptions' => 'applications/files/config/PhabricatorFilesConfigOptions.php',
|
||||
'PhabricatorFilesManagementCatWorkflow' => 'applications/files/management/PhabricatorFilesManagementCatWorkflow.php',
|
||||
'PhabricatorFilesManagementCompactWorkflow' => 'applications/files/management/PhabricatorFilesManagementCompactWorkflow.php',
|
||||
|
@ -2322,6 +2324,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFilesManagementPurgeWorkflow' => 'applications/files/management/PhabricatorFilesManagementPurgeWorkflow.php',
|
||||
'PhabricatorFilesManagementRebuildWorkflow' => 'applications/files/management/PhabricatorFilesManagementRebuildWorkflow.php',
|
||||
'PhabricatorFilesManagementWorkflow' => 'applications/files/management/PhabricatorFilesManagementWorkflow.php',
|
||||
'PhabricatorFilesOnDiskBuiltinFile' => 'applications/files/builtin/PhabricatorFilesOnDiskBuiltinFile.php',
|
||||
'PhabricatorFilesOutboundRequestAction' => 'applications/files/action/PhabricatorFilesOutboundRequestAction.php',
|
||||
'PhabricatorFlag' => 'applications/flag/storage/PhabricatorFlag.php',
|
||||
'PhabricatorFlagAddFlagHeraldAction' => 'applications/flag/herald/PhabricatorFlagAddFlagHeraldAction.php',
|
||||
|
@ -6610,6 +6613,8 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFileinfoSetupCheck' => 'PhabricatorSetupCheck',
|
||||
'PhabricatorFilesApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorFilesApplicationStorageEnginePanel' => 'PhabricatorApplicationConfigurationPanel',
|
||||
'PhabricatorFilesBuiltinFile' => 'Phobject',
|
||||
'PhabricatorFilesComposeIconBuiltinFile' => 'PhabricatorFilesBuiltinFile',
|
||||
'PhabricatorFilesConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
'PhabricatorFilesManagementCatWorkflow' => 'PhabricatorFilesManagementWorkflow',
|
||||
'PhabricatorFilesManagementCompactWorkflow' => 'PhabricatorFilesManagementWorkflow',
|
||||
|
@ -6618,6 +6623,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFilesManagementPurgeWorkflow' => 'PhabricatorFilesManagementWorkflow',
|
||||
'PhabricatorFilesManagementRebuildWorkflow' => 'PhabricatorFilesManagementWorkflow',
|
||||
'PhabricatorFilesManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
||||
'PhabricatorFilesOnDiskBuiltinFile' => 'PhabricatorFilesBuiltinFile',
|
||||
'PhabricatorFilesOutboundRequestAction' => 'PhabricatorSystemAction',
|
||||
'PhabricatorFlag' => array(
|
||||
'PhabricatorFlagDAO',
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorFilesBuiltinFile extends Phobject {
|
||||
|
||||
abstract public function getBuiltinFileKey();
|
||||
abstract public function getBuiltinDisplayName();
|
||||
abstract public function loadBuiltinFileData();
|
||||
|
||||
}
|
|
@ -0,0 +1,235 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorFilesComposeIconBuiltinFile
|
||||
extends PhabricatorFilesBuiltinFile {
|
||||
|
||||
private $icon;
|
||||
private $color;
|
||||
|
||||
public function setIcon($icon) {
|
||||
$this->icon = $icon;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIcon() {
|
||||
return $this->icon;
|
||||
}
|
||||
|
||||
public function setColor($color) {
|
||||
$this->color = $color;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getColor() {
|
||||
return $this->color;
|
||||
}
|
||||
|
||||
public function getBuiltinFileKey() {
|
||||
$icon = $this->getIcon();
|
||||
$color = $this->getColor();
|
||||
$desc = "compose(icon={$icon}, color={$color})";
|
||||
$hash = PhabricatorHash::digestToLength($desc, 40);
|
||||
return "builtin:{$hash}";
|
||||
}
|
||||
|
||||
public function getBuiltinDisplayName() {
|
||||
$icon = $this->getIcon();
|
||||
$color = $this->getColor();
|
||||
return "{$icon}-{$color}.png";
|
||||
}
|
||||
|
||||
public function loadBuiltinFileData() {
|
||||
return $this->composeImage($this->getColor(), $this->getIcon());
|
||||
}
|
||||
|
||||
public static function getAllIcons() {
|
||||
$root = dirname(phutil_get_library_root('phabricator'));
|
||||
$root = $root.'/resources/sprite/projects_2x/';
|
||||
|
||||
$quips = self::getIconQuips();
|
||||
|
||||
$map = array();
|
||||
$list = Filesystem::listDirectory($root, $include_hidden = false);
|
||||
foreach ($list as $file) {
|
||||
$short = preg_replace('/\.png$/', '', $file);
|
||||
|
||||
$map[$short] = array(
|
||||
'path' => $root.$file,
|
||||
'quip' => idx($quips, $short, $short),
|
||||
);
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
public static function getAllColors() {
|
||||
$colors = id(new CelerityResourceTransformer())
|
||||
->getCSSVariableMap();
|
||||
|
||||
$colors = array_select_keys(
|
||||
$colors,
|
||||
array(
|
||||
'red',
|
||||
'orange',
|
||||
'yellow',
|
||||
'green',
|
||||
'blue',
|
||||
'sky',
|
||||
'indigo',
|
||||
'violet',
|
||||
'pink',
|
||||
'charcoal',
|
||||
'backdrop',
|
||||
));
|
||||
|
||||
$quips = self::getColorQuips();
|
||||
|
||||
$map = array();
|
||||
foreach ($colors as $name => $color) {
|
||||
$map[$name] = array(
|
||||
'color' => $color,
|
||||
'quip' => idx($quips, $name, $name),
|
||||
);
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
private function composeImage($color, $icon) {
|
||||
$color_map = self::getAllColors();
|
||||
$color = idx($color_map, $color);
|
||||
if (!$color) {
|
||||
$fallback = 'backdrop';
|
||||
$color = idx($color_map, $fallback);
|
||||
if (!$color) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Fallback compose color ("%s") does not exist!',
|
||||
$fallback));
|
||||
}
|
||||
}
|
||||
|
||||
$color_hex = idx($color, 'color');
|
||||
$color_const = hexdec(trim($color_hex, '#'));
|
||||
|
||||
$icon_map = self::getAllIcons();
|
||||
$icon = idx($icon_map, $icon);
|
||||
if (!$icon) {
|
||||
$fallback = 'fa-umbrella';
|
||||
$icon = idx($icon_map, $fallback);
|
||||
if (!$icon) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Fallback compose icon ("%s") does not exist!',
|
||||
$fallback));
|
||||
}
|
||||
}
|
||||
|
||||
$path = idx($icon, 'path');
|
||||
$data = Filesystem::readFile($path);
|
||||
|
||||
$icon_img = imagecreatefromstring($data);
|
||||
|
||||
$canvas = imagecreatetruecolor(100, 100);
|
||||
imagefill($canvas, 0, 0, $color_const);
|
||||
imagecopy($canvas, $icon_img, 0, 0, 0, 0, 100, 100);
|
||||
|
||||
return PhabricatorImageTransformer::saveImageDataInAnyFormat(
|
||||
$canvas,
|
||||
'image/png');
|
||||
}
|
||||
|
||||
private static function getIconQuips() {
|
||||
return array(
|
||||
'8ball' => pht('Take a Risk'),
|
||||
'alien' => pht('Foreign Interface'),
|
||||
'announce' => pht('Louder is Better'),
|
||||
'art' => pht('Unique Snowflake'),
|
||||
'award' => pht('Shooting Star'),
|
||||
'bacon' => pht('Healthy Vegetables'),
|
||||
'bandaid' => pht('Durable Infrastructure'),
|
||||
'beer' => pht('Healthy Vegetable Juice'),
|
||||
'bomb' => pht('Imminent Success'),
|
||||
'briefcase' => pht('Adventure Pack'),
|
||||
'bug' => pht('Costumed Egg'),
|
||||
'calendar' => pht('Everyone Loves Meetings'),
|
||||
'cloud' => pht('Water Cycle'),
|
||||
'coffee' => pht('Half-Whip Nonfat Soy Latte'),
|
||||
'creditcard' => pht('Expense It'),
|
||||
'death' => pht('Calcium Promotes Bone Health'),
|
||||
'desktop' => pht('Magical Portal'),
|
||||
'dropbox' => pht('Cardboard Box'),
|
||||
'education' => pht('Debt'),
|
||||
'experimental' => pht('CAUTION: Dangerous Chemicals'),
|
||||
'facebook' => pht('Popular Social Network'),
|
||||
'facility' => pht('Pollution Solves Problems'),
|
||||
'film' => pht('Actual Physical Film'),
|
||||
'forked' => pht('You Can\'t Eat Soup'),
|
||||
'games' => pht('Serious Business'),
|
||||
'ghost' => pht('Haunted'),
|
||||
'gift' => pht('Surprise!'),
|
||||
'globe' => pht('Scanner Sweep'),
|
||||
'golf' => pht('Business Meeting'),
|
||||
'heart' => pht('Undergoing a Major Surgery'),
|
||||
'intergalactic' => pht('Jupiter'),
|
||||
'lock' => pht('Extremely Secret'),
|
||||
'mail' => pht('Oragami'),
|
||||
'martini' => pht('Healthy Olive Drink'),
|
||||
'medical' => pht('Medic!'),
|
||||
'mobile' => pht('Cellular Telephone'),
|
||||
'music' => pht("\xE2\x99\xAB"),
|
||||
'news' => pht('Actual Physical Newspaper'),
|
||||
'orgchart' => pht('It\'s Good to be King'),
|
||||
'peoples' => pht('Angel and Devil'),
|
||||
'piechart' => pht('Actual Physical Pie'),
|
||||
'poison' => pht('Healthy Bone Juice'),
|
||||
'putabirdonit' => pht('Put a Bird On It'),
|
||||
'radiate' => pht('Radiant Beauty'),
|
||||
'savings' => pht('Oink Oink'),
|
||||
'search' => pht('Sleuthing'),
|
||||
'shield' => pht('Royal Crest'),
|
||||
'speed' => pht('Slow and Steady'),
|
||||
'sprint' => pht('Fire Exit'),
|
||||
'star' => pht('The More You Know'),
|
||||
'storage' => pht('Stack of Pancakes'),
|
||||
'tablet' => pht('Cellular Telephone For Giants'),
|
||||
'travel' => pht('Pretty Clearly an Airplane'),
|
||||
'twitter' => pht('Bird Stencil'),
|
||||
'warning' => pht('No Caution Required, Everything Looks Safe'),
|
||||
'whale' => pht('Friendly Walrus'),
|
||||
'fa-flask' => pht('Experimental'),
|
||||
'fa-briefcase' => pht('Briefcase'),
|
||||
'fa-bug' => pht('Bug'),
|
||||
'fa-building' => pht('Company'),
|
||||
'fa-calendar' => pht('Deadline'),
|
||||
'fa-cloud' => pht('The Cloud'),
|
||||
'fa-credit-card' => pht('Accounting'),
|
||||
'fa-envelope' => pht('Communication'),
|
||||
'fa-flag-checkered' => pht('Goal'),
|
||||
'fa-folder' => pht('Folder'),
|
||||
'fa-group' => pht('Team'),
|
||||
'fa-lock' => pht('Policy'),
|
||||
'fa-tags' => pht('Tag'),
|
||||
'fa-trash-o' => pht('Garbage'),
|
||||
'fa-truck' => pht('Release'),
|
||||
'fa-umbrella' => pht('An Umbrella'),
|
||||
);
|
||||
}
|
||||
|
||||
private static function getColorQuips() {
|
||||
return array(
|
||||
'red' => pht('Verbillion'),
|
||||
'orange' => pht('Navel Orange'),
|
||||
'yellow' => pht('Prim Goldenrod'),
|
||||
'green' => pht('Lustrous Verdant'),
|
||||
'blue' => pht('Tropical Deep'),
|
||||
'sky' => pht('Wide Open Sky'),
|
||||
'indigo' => pht('Pleated Khaki'),
|
||||
'violet' => pht('Aged Merlot'),
|
||||
'pink' => pht('Easter Bunny'),
|
||||
'charcoal' => pht('Gemstone'),
|
||||
'backdrop' => pht('Driven Snow'),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorFilesOnDiskBuiltinFile
|
||||
extends PhabricatorFilesBuiltinFile {
|
||||
|
||||
private $name;
|
||||
|
||||
public function setName($name) {
|
||||
$this->name = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getName() {
|
||||
if ($this->name === null) {
|
||||
throw new PhutilInvalidStateException('setName');
|
||||
}
|
||||
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getBuiltinDisplayName() {
|
||||
return $this->getName();
|
||||
}
|
||||
|
||||
public function getBuiltinFileKey() {
|
||||
$name = $this->getName();
|
||||
$desc = "disk(name={$name})";
|
||||
$hash = PhabricatorHash::digestToLength($desc, 40);
|
||||
return "builtin:{$hash}";
|
||||
}
|
||||
|
||||
public function loadBuiltinFileData() {
|
||||
$name = $this->getName();
|
||||
|
||||
$available = $this->getAllBuiltinFiles();
|
||||
if (empty($available[$name])) {
|
||||
throw new Exception(pht('Builtin "%s" does not exist!', $name));
|
||||
}
|
||||
|
||||
return Filesystem::readFile($available[$name]);
|
||||
}
|
||||
|
||||
private function getAllBuiltinFiles() {
|
||||
$root = dirname(phutil_get_library_root('phabricator'));
|
||||
$root = $root.'/resources/builtin/';
|
||||
|
||||
$map = array();
|
||||
$list = Filesystem::listDirectory($root, $include_hidden = false);
|
||||
foreach ($list as $file) {
|
||||
$map[$file] = $root.$file;
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
}
|
|
@ -6,21 +6,8 @@ final class PhabricatorFileComposeController
|
|||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
|
||||
$colors = array(
|
||||
'red' => pht('Verbillion'),
|
||||
'orange' => pht('Navel Orange'),
|
||||
'yellow' => pht('Prim Goldenrod'),
|
||||
'green' => pht('Lustrous Verdant'),
|
||||
'blue' => pht('Tropical Deep'),
|
||||
'sky' => pht('Wide Open Sky'),
|
||||
'indigo' => pht('Pleated Khaki'),
|
||||
'violet' => pht('Aged Merlot'),
|
||||
'pink' => pht('Easter Bunny'),
|
||||
'charcoal' => pht('Gemstone'),
|
||||
'backdrop' => pht('Driven Snow'),
|
||||
);
|
||||
|
||||
$manifest = PHUIIconView::getSheetManifest(PHUIIconView::SPRITE_PROJECTS);
|
||||
$color_map = PhabricatorFilesComposeIconBuiltinFile::getAllColors();
|
||||
$icon_map = $this->getIconMap();
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
$project_phid = $request->getStr('projectPHID');
|
||||
|
@ -37,36 +24,21 @@ final class PhabricatorFileComposeController
|
|||
if (!$project) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
$icon = $project->getIcon();
|
||||
$color = $project->getColor();
|
||||
switch ($color) {
|
||||
case 'grey':
|
||||
$color = 'charcoal';
|
||||
break;
|
||||
case 'checkered':
|
||||
$color = 'backdrop';
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
$icon = $request->getStr('icon');
|
||||
$color = $request->getStr('color');
|
||||
}
|
||||
|
||||
if (!isset($colors[$color]) || !isset($manifest['projects-'.$icon])) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
$icon = $request->getStr('icon');
|
||||
$color = $request->getStr('color');
|
||||
|
||||
$root = dirname(phutil_get_library_root('phabricator'));
|
||||
$icon_file = $root.'/resources/sprite/projects_2x/'.$icon.'.png';
|
||||
$icon_data = Filesystem::readFile($icon_file);
|
||||
$composer = id(new PhabricatorFilesComposeIconBuiltinFile())
|
||||
->setIcon($icon)
|
||||
->setColor($color);
|
||||
|
||||
|
||||
$data = $this->composeImage($color, $icon_data);
|
||||
$data = $composer->loadBuiltinFileData();
|
||||
|
||||
$file = PhabricatorFile::buildFromFileDataOrHash(
|
||||
$data,
|
||||
array(
|
||||
'name' => 'project.png',
|
||||
'name' => $composer->getBuiltinDisplayName(),
|
||||
'profile' => true,
|
||||
'canCDN' => true,
|
||||
));
|
||||
|
@ -97,14 +69,15 @@ final class PhabricatorFileComposeController
|
|||
}
|
||||
}
|
||||
|
||||
$value_color = head_key($colors);
|
||||
$value_icon = head_key($manifest);
|
||||
$value_icon = substr($value_icon, strlen('projects-'));
|
||||
$value_color = head_key($color_map);
|
||||
$value_icon = head_key($icon_map);
|
||||
|
||||
require_celerity_resource('people-profile-css');
|
||||
|
||||
$buttons = array();
|
||||
foreach ($colors as $color => $name) {
|
||||
foreach ($color_map as $color => $info) {
|
||||
$quip = idx($info, 'quip');
|
||||
|
||||
$buttons[] = javelin_tag(
|
||||
'button',
|
||||
array(
|
||||
|
@ -113,116 +86,17 @@ final class PhabricatorFileComposeController
|
|||
'style' => 'margin: 0 8px 8px 0',
|
||||
'meta' => array(
|
||||
'color' => $color,
|
||||
'tip' => $name,
|
||||
'tip' => $quip,
|
||||
),
|
||||
),
|
||||
id(new PHUIIconView())
|
||||
->addClass('compose-background-'.$color));
|
||||
}
|
||||
|
||||
$sort_these_first = array(
|
||||
'projects-fa-briefcase',
|
||||
'projects-fa-tags',
|
||||
'projects-fa-folder',
|
||||
'projects-fa-group',
|
||||
'projects-fa-bug',
|
||||
'projects-fa-trash-o',
|
||||
'projects-fa-calendar',
|
||||
'projects-fa-flag-checkered',
|
||||
'projects-fa-envelope',
|
||||
'projects-fa-truck',
|
||||
'projects-fa-lock',
|
||||
'projects-fa-umbrella',
|
||||
'projects-fa-cloud',
|
||||
'projects-fa-building',
|
||||
'projects-fa-credit-card',
|
||||
'projects-fa-flask',
|
||||
);
|
||||
|
||||
$manifest = array_select_keys(
|
||||
$manifest,
|
||||
$sort_these_first)
|
||||
+ $manifest;
|
||||
|
||||
$icons = array();
|
||||
|
||||
$icon_quips = array(
|
||||
'8ball' => pht('Take a Risk'),
|
||||
'alien' => pht('Foreign Interface'),
|
||||
'announce' => pht('Louder is Better'),
|
||||
'art' => pht('Unique Snowflake'),
|
||||
'award' => pht('Shooting Star'),
|
||||
'bacon' => pht('Healthy Vegetables'),
|
||||
'bandaid' => pht('Durable Infrastructure'),
|
||||
'beer' => pht('Healthy Vegetable Juice'),
|
||||
'bomb' => pht('Imminent Success'),
|
||||
'briefcase' => pht('Adventure Pack'),
|
||||
'bug' => pht('Costumed Egg'),
|
||||
'calendar' => pht('Everyone Loves Meetings'),
|
||||
'cloud' => pht('Water Cycle'),
|
||||
'coffee' => pht('Half-Whip Nonfat Soy Latte'),
|
||||
'creditcard' => pht('Expense It'),
|
||||
'death' => pht('Calcium Promotes Bone Health'),
|
||||
'desktop' => pht('Magical Portal'),
|
||||
'dropbox' => pht('Cardboard Box'),
|
||||
'education' => pht('Debt'),
|
||||
'experimental' => pht('CAUTION: Dangerous Chemicals'),
|
||||
'facebook' => pht('Popular Social Network'),
|
||||
'facility' => pht('Pollution Solves Problems'),
|
||||
'film' => pht('Actual Physical Film'),
|
||||
'forked' => pht('You Can\'t Eat Soup'),
|
||||
'games' => pht('Serious Business'),
|
||||
'ghost' => pht('Haunted'),
|
||||
'gift' => pht('Surprise!'),
|
||||
'globe' => pht('Scanner Sweep'),
|
||||
'golf' => pht('Business Meeting'),
|
||||
'heart' => pht('Undergoing a Major Surgery'),
|
||||
'intergalactic' => pht('Jupiter'),
|
||||
'lock' => pht('Extremely Secret'),
|
||||
'mail' => pht('Oragami'),
|
||||
'martini' => pht('Healthy Olive Drink'),
|
||||
'medical' => pht('Medic!'),
|
||||
'mobile' => pht('Cellular Telephone'),
|
||||
'music' => pht("\xE2\x99\xAB"),
|
||||
'news' => pht('Actual Physical Newspaper'),
|
||||
'orgchart' => pht('It\'s Good to be King'),
|
||||
'peoples' => pht('Angel and Devil'),
|
||||
'piechart' => pht('Actual Physical Pie'),
|
||||
'poison' => pht('Healthy Bone Juice'),
|
||||
'putabirdonit' => pht('Put a Bird On It'),
|
||||
'radiate' => pht('Radiant Beauty'),
|
||||
'savings' => pht('Oink Oink'),
|
||||
'search' => pht('Sleuthing'),
|
||||
'shield' => pht('Royal Crest'),
|
||||
'speed' => pht('Slow and Steady'),
|
||||
'sprint' => pht('Fire Exit'),
|
||||
'star' => pht('The More You Know'),
|
||||
'storage' => pht('Stack of Pancakes'),
|
||||
'tablet' => pht('Cellular Telephone For Giants'),
|
||||
'travel' => pht('Pretty Clearly an Airplane'),
|
||||
'twitter' => pht('Bird Stencil'),
|
||||
'warning' => pht('No Caution Required, Everything Looks Safe'),
|
||||
'whale' => pht('Friendly Walrus'),
|
||||
'fa-flask' => pht('Experimental'),
|
||||
'fa-briefcase' => pht('Briefcase'),
|
||||
'fa-bug' => pht('Bug'),
|
||||
'fa-building' => pht('Company'),
|
||||
'fa-calendar' => pht('Deadline'),
|
||||
'fa-cloud' => pht('The Cloud'),
|
||||
'fa-credit-card' => pht('Accounting'),
|
||||
'fa-envelope' => pht('Communication'),
|
||||
'fa-flag-checkered' => pht('Goal'),
|
||||
'fa-folder' => pht('Folder'),
|
||||
'fa-group' => pht('Team'),
|
||||
'fa-lock' => pht('Policy'),
|
||||
'fa-tags' => pht('Tag'),
|
||||
'fa-trash-o' => pht('Garbage'),
|
||||
'fa-truck' => pht('Release'),
|
||||
'fa-umbrella' => pht('An Umbrella'),
|
||||
);
|
||||
|
||||
foreach ($manifest as $icon => $spec) {
|
||||
$icon = substr($icon, strlen('projects-'));
|
||||
foreach ($icon_map as $icon => $spec) {
|
||||
$quip = idx($spec, 'quip');
|
||||
|
||||
$icons[] = javelin_tag(
|
||||
'button',
|
||||
|
@ -232,7 +106,7 @@ final class PhabricatorFileComposeController
|
|||
'style' => 'margin: 0 8px 8px 0',
|
||||
'meta' => array(
|
||||
'icon' => $icon,
|
||||
'tip' => idx($icon_quips, $icon, $icon),
|
||||
'tip' => $quip,
|
||||
),
|
||||
),
|
||||
id(new PHUIIconView())
|
||||
|
@ -318,23 +192,31 @@ final class PhabricatorFileComposeController
|
|||
return id(new AphrontDialogResponse())->setDialog($dialog);
|
||||
}
|
||||
|
||||
private function composeImage($color, $icon_data) {
|
||||
$icon_img = imagecreatefromstring($icon_data);
|
||||
private function getIconMap() {
|
||||
$icon_map = PhabricatorFilesComposeIconBuiltinFile::getAllIcons();
|
||||
|
||||
$map = id(new CelerityResourceTransformer())
|
||||
->getCSSVariableMap();
|
||||
$first = array(
|
||||
'fa-briefcase',
|
||||
'fa-tags',
|
||||
'fa-folder',
|
||||
'fa-group',
|
||||
'fa-bug',
|
||||
'fa-trash-o',
|
||||
'fa-calendar',
|
||||
'fa-flag-checkered',
|
||||
'fa-envelope',
|
||||
'fa-truck',
|
||||
'fa-lock',
|
||||
'fa-umbrella',
|
||||
'fa-cloud',
|
||||
'fa-building',
|
||||
'fa-credit-card',
|
||||
'fa-flask',
|
||||
);
|
||||
|
||||
$color_string = idx($map, $color, '#ff00ff');
|
||||
$color_const = hexdec(trim($color_string, '#'));
|
||||
$icon_map = array_select_keys($icon_map, $first) + $icon_map;
|
||||
|
||||
$canvas = imagecreatetruecolor(100, 100);
|
||||
imagefill($canvas, 0, 0, $color_const);
|
||||
|
||||
imagecopy($canvas, $icon_img, 0, 0, 0, 0, 100, 100);
|
||||
|
||||
return PhabricatorImageTransformer::saveImageDataInAnyFormat(
|
||||
$canvas,
|
||||
'image/png');
|
||||
return $icon_map;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -961,16 +961,18 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||
* Builtins are located in `resources/builtin/` and identified by their
|
||||
* name.
|
||||
*
|
||||
* @param PhabricatorUser Viewing user.
|
||||
* @param list<string> List of builtin file names.
|
||||
* @return dict<string, PhabricatorFile> Dictionary of named builtins.
|
||||
* @param PhabricatorUser Viewing user.
|
||||
* @param list<PhabricatorFilesBuiltinFile> List of builtin file specs.
|
||||
* @return dict<string, PhabricatorFile> Dictionary of named builtins.
|
||||
*/
|
||||
public static function loadBuiltins(PhabricatorUser $user, array $names) {
|
||||
public static function loadBuiltins(PhabricatorUser $user, array $builtins) {
|
||||
$builtins = mpull($builtins, null, 'getBuiltinFileKey');
|
||||
|
||||
$specs = array();
|
||||
foreach ($names as $name) {
|
||||
foreach ($builtins as $key => $buitin) {
|
||||
$specs[] = array(
|
||||
'originalPHID' => PhabricatorPHIDConstants::PHID_VOID,
|
||||
'transform' => 'builtin:'.$name,
|
||||
'transform' => $key,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -981,41 +983,34 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||
->withTransforms($specs)
|
||||
->execute();
|
||||
|
||||
$files = mpull($files, null, 'getName');
|
||||
|
||||
$root = dirname(phutil_get_library_root('phabricator'));
|
||||
$root = $root.'/resources/builtin/';
|
||||
$results = array();
|
||||
foreach ($files as $file) {
|
||||
$builtin_key = $file->getBuiltinName();
|
||||
if ($builtin_key !== null) {
|
||||
$results[$builtin_key] = $file;
|
||||
}
|
||||
}
|
||||
|
||||
$build = array();
|
||||
foreach ($names as $name) {
|
||||
if (isset($files[$name])) {
|
||||
foreach ($builtins as $key => $builtin) {
|
||||
if (isset($results[$key])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// This is just a sanity check to prevent loading arbitrary files.
|
||||
if (basename($name) != $name) {
|
||||
throw new Exception(pht("Invalid builtin name '%s'!", $name));
|
||||
}
|
||||
$data = $builtin->loadBuiltinFileData();
|
||||
|
||||
$path = $root.$name;
|
||||
|
||||
if (!Filesystem::pathExists($path)) {
|
||||
throw new Exception(pht("Builtin '%s' does not exist!", $path));
|
||||
}
|
||||
|
||||
$data = Filesystem::readFile($path);
|
||||
$params = array(
|
||||
'name' => $name,
|
||||
'name' => $builtin->getBuiltinDisplayName(),
|
||||
'ttl' => time() + (60 * 60 * 24 * 7),
|
||||
'canCDN' => true,
|
||||
'builtin' => $name,
|
||||
'builtin' => $key,
|
||||
);
|
||||
|
||||
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
||||
$file = self::newFromFileData($data, $params);
|
||||
$xform = id(new PhabricatorTransformedFile())
|
||||
->setOriginalPHID(PhabricatorPHIDConstants::PHID_VOID)
|
||||
->setTransform('builtin:'.$name)
|
||||
->setTransform($key)
|
||||
->setTransformedPHID($file->getPHID())
|
||||
->save();
|
||||
unset($unguarded);
|
||||
|
@ -1023,10 +1018,10 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||
$file->attachObjectPHIDs(array());
|
||||
$file->attachObjects(array());
|
||||
|
||||
$files[$name] = $file;
|
||||
$results[$key] = $file;
|
||||
}
|
||||
|
||||
return $files;
|
||||
return $results;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1038,7 +1033,12 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||
* @return PhabricatorFile Corresponding builtin file.
|
||||
*/
|
||||
public static function loadBuiltin(PhabricatorUser $user, $name) {
|
||||
return idx(self::loadBuiltins($user, array($name)), $name);
|
||||
$builtin = id(new PhabricatorFilesOnDiskBuiltinFile())
|
||||
->setName($name);
|
||||
|
||||
$key = $builtin->getBuiltinFileKey();
|
||||
|
||||
return idx(self::loadBuiltins($user, array($builtin)), $key);
|
||||
}
|
||||
|
||||
public function getObjects() {
|
||||
|
|
|
@ -68,11 +68,9 @@ final class PholioMockImagesView extends AphrontView {
|
|||
|
||||
// TODO: We could maybe do a better job with tailoring this, which is the
|
||||
// image shown on the review stage.
|
||||
$default_name = 'image-100x100.png';
|
||||
$builtins = PhabricatorFile::loadBuiltins(
|
||||
$this->getUser(),
|
||||
array($default_name));
|
||||
$default = $builtins[$default_name];
|
||||
$viewer = $this->getUser();
|
||||
|
||||
$default = PhabricatorFile::loadBuiltin($viewer, 'image-100x100.png');
|
||||
|
||||
$engine = id(new PhabricatorMarkupEngine())
|
||||
->setViewer($this->getUser());
|
||||
|
|
|
@ -123,7 +123,7 @@ final class PhabricatorProjectEditPictureController
|
|||
|
||||
$images[PhabricatorPHIDConstants::PHID_VOID] = array(
|
||||
'uri' => $default_image->getBestURI(),
|
||||
'tip' => pht('Default Picture'),
|
||||
'tip' => pht('No Picture'),
|
||||
);
|
||||
|
||||
require_celerity_resource('people-profile-css');
|
||||
|
@ -181,7 +181,11 @@ final class PhabricatorProjectEditPictureController
|
|||
$form->appendChild(
|
||||
id(new AphrontFormMarkupControl())
|
||||
->setLabel(pht('Use Picture'))
|
||||
->setValue($buttons));
|
||||
->setValue(
|
||||
array(
|
||||
$this->renderDefaultForm($project),
|
||||
$buttons,
|
||||
)));
|
||||
|
||||
$launch_id = celerity_generate_unique_node_id();
|
||||
$input_id = celerity_generate_unique_node_id();
|
||||
|
@ -226,38 +230,6 @@ final class PhabricatorProjectEditPictureController
|
|||
->setLabel(pht('Quick Create'))
|
||||
->setValue($compose_form));
|
||||
|
||||
$default_button = javelin_tag(
|
||||
'button',
|
||||
array(
|
||||
'class' => 'grey',
|
||||
),
|
||||
pht('Use Project Icon'));
|
||||
|
||||
$default_input = javelin_tag(
|
||||
'input',
|
||||
array(
|
||||
'type' => 'hidden',
|
||||
'name' => 'projectPHID',
|
||||
'value' => $project->getPHID(),
|
||||
));
|
||||
|
||||
$default_form = phabricator_form(
|
||||
$viewer,
|
||||
array(
|
||||
'class' => 'profile-image-form',
|
||||
'method' => 'POST',
|
||||
'action' => '/file/compose/',
|
||||
),
|
||||
array(
|
||||
$default_input,
|
||||
$default_button,
|
||||
));
|
||||
|
||||
$form->appendChild(
|
||||
id(new AphrontFormMarkupControl())
|
||||
->setLabel(pht('Use Default'))
|
||||
->setValue($default_form));
|
||||
|
||||
$upload_form = id(new AphrontFormView())
|
||||
->setUser($viewer)
|
||||
->setEncType('multipart/form-data')
|
||||
|
@ -294,4 +266,69 @@ final class PhabricatorProjectEditPictureController
|
|||
$upload_box,
|
||||
));
|
||||
}
|
||||
|
||||
private function renderDefaultForm(PhabricatorProject $project) {
|
||||
$viewer = $this->getViewer();
|
||||
$compose_color = $project->getDisplayIconComposeColor();
|
||||
$compose_icon = $project->getDisplayIconComposeIcon();
|
||||
|
||||
$default_builtin = id(new PhabricatorFilesComposeIconBuiltinFile())
|
||||
->setColor($compose_color)
|
||||
->setIcon($compose_icon);
|
||||
|
||||
$file_builtins = PhabricatorFile::loadBuiltins(
|
||||
$viewer,
|
||||
array($default_builtin));
|
||||
|
||||
$file_builtin = head($file_builtins);
|
||||
|
||||
$default_button = javelin_tag(
|
||||
'button',
|
||||
array(
|
||||
'class' => 'grey profile-image-button',
|
||||
'sigil' => 'has-tooltip',
|
||||
'meta' => array(
|
||||
'tip' => pht('Use Icon and Color'),
|
||||
'size' => 300,
|
||||
),
|
||||
),
|
||||
phutil_tag(
|
||||
'img',
|
||||
array(
|
||||
'height' => 50,
|
||||
'width' => 50,
|
||||
'src' => $file_builtin->getBestURI(),
|
||||
)));
|
||||
|
||||
$inputs = array(
|
||||
'projectPHID' => $project->getPHID(),
|
||||
'icon' => $compose_icon,
|
||||
'color' => $compose_color,
|
||||
);
|
||||
|
||||
foreach ($inputs as $key => $value) {
|
||||
$inputs[$key] = javelin_tag(
|
||||
'input',
|
||||
array(
|
||||
'type' => 'hidden',
|
||||
'name' => $key,
|
||||
'value' => $value,
|
||||
));
|
||||
}
|
||||
|
||||
$default_form = phabricator_form(
|
||||
$viewer,
|
||||
array(
|
||||
'class' => 'profile-image-form',
|
||||
'method' => 'POST',
|
||||
'action' => '/file/compose/',
|
||||
),
|
||||
array(
|
||||
$inputs,
|
||||
$default_button,
|
||||
));
|
||||
|
||||
return $default_form;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -713,6 +713,10 @@ final class PhabricatorProjectTransactionEditor
|
|||
}
|
||||
}
|
||||
|
||||
if ($this->getIsNewObject()) {
|
||||
$this->setDefaultProfilePicture($object);
|
||||
}
|
||||
|
||||
// TODO: We should dump an informational transaction onto the parent
|
||||
// project to show that we created the sub-thing.
|
||||
|
||||
|
@ -886,5 +890,31 @@ final class PhabricatorProjectTransactionEditor
|
|||
return $results;
|
||||
}
|
||||
|
||||
private function setDefaultProfilePicture(PhabricatorProject $project) {
|
||||
if ($project->isMilestone()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$compose_color = $project->getDisplayIconComposeColor();
|
||||
$compose_icon = $project->getDisplayIconComposeIcon();
|
||||
|
||||
$builtin = id(new PhabricatorFilesComposeIconBuiltinFile())
|
||||
->setColor($compose_color)
|
||||
->setIcon($compose_icon);
|
||||
|
||||
$data = $builtin->loadBuiltinFileData();
|
||||
|
||||
$file = PhabricatorFile::newFromFileData(
|
||||
$data,
|
||||
array(
|
||||
'name' => $builtin->getBuiltinDisplayName(),
|
||||
'profile' => true,
|
||||
'canCDN' => true,
|
||||
));
|
||||
|
||||
$project
|
||||
->setProfileImagePHID($file->getPHID())
|
||||
->save();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -519,6 +519,23 @@ final class PhabricatorProject extends PhabricatorProjectDAO
|
|||
return $this->getColor();
|
||||
}
|
||||
|
||||
public function getDisplayIconComposeIcon() {
|
||||
$icon = $this->getDisplayIconIcon();
|
||||
return $icon;
|
||||
}
|
||||
|
||||
public function getDisplayIconComposeColor() {
|
||||
$color = $this->getDisplayColor();
|
||||
|
||||
$map = array(
|
||||
'grey' => 'charcoal',
|
||||
'checkered' => 'backdrop',
|
||||
);
|
||||
|
||||
return idx($map, $color, $color);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* -( PhabricatorSubscribableInterface )----------------------------------- */
|
||||
|
||||
|
|
Loading…
Reference in a new issue