@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1<?php
2
3final class PhabricatorFilesComposeIconBuiltinFile
4 extends PhabricatorFilesBuiltinFile {
5
6 private $icon;
7 private $color;
8
9 public function setIcon($icon) {
10 $this->icon = $icon;
11 return $this;
12 }
13
14 public function getIcon() {
15 return $this->icon;
16 }
17
18 public function setColor($color) {
19 $this->color = $color;
20 return $this;
21 }
22
23 public function getColor() {
24 return $this->color;
25 }
26
27 public function getBuiltinFileKey() {
28 $icon = $this->getIcon();
29 $color = $this->getColor();
30 $desc = "compose(icon={$icon}, color={$color})";
31 $hash = PhabricatorHash::digestToLength($desc, 40);
32 return "builtin:{$hash}";
33 }
34
35 public function getBuiltinDisplayName() {
36 $icon = $this->getIcon();
37 $color = $this->getColor();
38 return "{$icon}-{$color}.png";
39 }
40
41 public function loadBuiltinFileData() {
42 return $this->composeImage($this->getColor(), $this->getIcon());
43 }
44
45 public static function getAllIcons() {
46 $root = dirname(phutil_get_library_root('phabricator'));
47 $root = $root.'/resources/builtin/projects/';
48
49 $quips = self::getIconQuips();
50
51 $map = array();
52 $list = Filesystem::listDirectory($root, $include_hidden = false);
53 foreach ($list as $file) {
54 $count = 0;
55 $short = preg_replace('/\.png$/', '', $file, -1, $count);
56 if ($count === 1) {
57 $map[$short] = array(
58 'path' => $root.$file,
59 'quip' => idx($quips, $short, $short),
60 );
61 }
62 }
63
64 return $map;
65 }
66
67 public static function getAllColors() {
68 $colors = id(new CelerityResourceTransformer())
69 ->getCSSVariableMap();
70
71 $colors = array_select_keys(
72 $colors,
73 array(
74 'red',
75 'orange',
76 'yellow',
77 'green',
78 'blue',
79 'sky',
80 'indigo',
81 'violet',
82 'pink',
83 'charcoal',
84 'backdrop',
85 ));
86
87 $quips = self::getColorQuips();
88
89 $map = array();
90 foreach ($colors as $name => $color) {
91 $map[$name] = array(
92 'color' => $color,
93 'quip' => idx($quips, $name, $name),
94 );
95 }
96
97 return $map;
98 }
99
100 private function composeImage($color, $icon) {
101 // If we don't have the GD extension installed, just return a static
102 // default project image rather than trying to compose one.
103 if (!function_exists('imagecreatefromstring')) {
104 $root = dirname(phutil_get_library_root('phabricator'));
105 $default_path = $root.'/resources/builtin/profile.png';
106 return Filesystem::readFile($default_path);
107 }
108
109 $color_map = self::getAllColors();
110 $color = idx($color_map, $color);
111 if (!$color) {
112 $fallback = 'backdrop';
113 $color = idx($color_map, $fallback);
114 if (!$color) {
115 throw new Exception(
116 pht(
117 'Fallback compose color ("%s") does not exist!',
118 $fallback));
119 }
120 }
121
122 $color_hex = idx($color, 'color');
123 $color_const = hexdec(trim($color_hex, '#'));
124
125 $icon_map = self::getAllIcons();
126 $icon = idx($icon_map, $icon);
127 if (!$icon) {
128 $fallback = 'fa-umbrella';
129 $icon = idx($icon_map, $fallback);
130 if (!$icon) {
131 throw new Exception(
132 pht(
133 'Fallback compose icon ("%s") does not exist!',
134 $fallback));
135 }
136 }
137
138 $path = idx($icon, 'path');
139 $data = Filesystem::readFile($path);
140
141 $icon_img = imagecreatefromstring($data);
142
143 $canvas = imagecreatetruecolor(200, 200);
144 imagefill($canvas, 0, 0, $color_const);
145 imagecopy($canvas, $icon_img, 0, 0, 0, 0, 200, 200);
146
147 return PhabricatorImageTransformer::saveImageDataInAnyFormat(
148 $canvas,
149 'image/png');
150 }
151
152 private static function getIconQuips() {
153 return array(
154 'fa-android' => pht('Friendly Robot'),
155 'fa-apple' => pht('Friendly Fruit'),
156 'fa-beer' => pht('Liquid Carbs'),
157 'fa-bomb' => pht('Boom!'),
158 'fa-book' => pht('Read Me'),
159 'fa-briefcase' => pht('Briefcase'),
160 'fa-bug' => pht('Bug'),
161 'fa-building' => pht('Company'),
162 'fa-calendar' => pht('Deadline'),
163 'fa-camera-retro' => pht('Leica Enthusiast'),
164 'fa-chrome' => pht('Shiny'),
165 'fa-cloud' => pht('The Cloud'),
166 'fa-coffee' => pht('Go Juice'),
167 'fa-comments' => pht('Cartoon Captions'),
168 'fa-credit-card' => pht('Accounting'),
169 'fa-database' => pht('Stack of Pancakes'),
170 'fa-desktop' => pht('Cardboard Box'),
171 'fa-diamond' => pht('Isometric-Hexoctahedral'),
172 'fa-empire' => pht('Bad Guys'),
173 'fa-envelope' => pht('Communication'),
174 'fa-facebook' => pht('College Site'),
175 'fa-fax' => pht('Communication Device'),
176 'fa-film' => pht('Physical Film'),
177 'fa-firefox' => pht('Blake Ross'),
178 'fa-flag-checkered' => pht('Goal'),
179 'fa-flask' => pht('Experimental'),
180 'fa-folder' => pht('Folder'),
181 'fa-gamepad' => pht('Half-Life 3 Confirmed'),
182 'fa-gears' => pht('Mechanical'),
183 'fa-google' => pht('Car Company'),
184 'fa-hand-peace-o' => pht('Peace'),
185 'fa-hashtag' => pht('Not Slack'),
186 'fa-heart' => pht('Myocardial Infarction'),
187 'fa-internet-explorer' => pht('Now Just Edge'),
188 'fa-key' => pht('Primitive Security'),
189 'fa-legal' => pht('Hired Protection'),
190 'fa-linux' => pht('M\'Lady'),
191 'fa-lock' => pht('Policy'),
192 'fa-map-marker' => pht('Destination Beacon'),
193 'fa-microphone' => pht('Podcasting'),
194 'fa-mobile' => pht('Tiny Pocket Cat Meme Machine'),
195 'fa-money' => pht('1 of 99 Problems'),
196 'fa-phone' => pht('Grandma Uses This'),
197 'fa-pie-chart' => pht('Not Actually Edible'),
198 'fa-rebel' => pht('Good Guys'),
199 'fa-reddit-alien' => pht('Updoot In 5 Seconds'),
200 'fa-safari' => pht('Fruit Exploration'),
201 'fa-search' => pht('Dust Detector'),
202 'fa-server' => pht('Heating Units'),
203 'fa-shopping-cart' => pht('Buy Stuff'),
204 'fa-sitemap' => pht('Sitemap'),
205 'fa-star' => pht('The More You Know'),
206 'fa-tablet' => pht('Cellular Telephone For Giants'),
207 'fa-tag' => pht('You\'re It'),
208 'fa-tags' => pht('Tags'),
209 'fa-trash-o' => pht('Garbage'),
210 'fa-truck' => pht('Release'),
211 'fa-twitter' => pht('Bird Stencil'),
212 'fa-umbrella' => pht('An Umbrella'),
213 'fa-university' => pht('School'),
214 'fa-user-secret' => pht('Shhh'),
215 'fa-user' => pht('Individual'),
216 'fa-users' => pht('Team'),
217 'fa-warning' => pht('No Caution Required, Everything Looks Safe'),
218 'fa-wheelchair' => pht('Accessibility'),
219 'fa-windows' => pht('Windows'),
220 );
221 }
222
223 private static function getColorQuips() {
224 return array(
225 'red' => pht('Verbillion'),
226 'orange' => pht('Navel Orange'),
227 'yellow' => pht('Prim Goldenrod'),
228 'green' => pht('Lustrous Verdant'),
229 'blue' => pht('Tropical Deep'),
230 'sky' => pht('Wide Open Sky'),
231 'indigo' => pht('Pleated Khaki'),
232 'violet' => pht('Aged Merlot'),
233 'pink' => pht('Easter Bunny'),
234 'charcoal' => pht('Gemstone'),
235 'backdrop' => pht('Driven Snow'),
236 );
237 }
238
239}