Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/files/builtin/PhabricatorFilesComposeAvatarBuiltinFile.php
12242 views
1
<?php
2
3
final class PhabricatorFilesComposeAvatarBuiltinFile
4
extends PhabricatorFilesBuiltinFile {
5
6
private $icon;
7
private $color;
8
private $border;
9
10
private $maps = array();
11
12
const VERSION = 'v1';
13
14
public function updateUser(PhabricatorUser $user) {
15
$username = $user->getUsername();
16
17
$image_map = $this->getMap('image');
18
$initial = phutil_utf8_strtoupper(substr($username, 0, 1));
19
$pack = $this->pickMap('pack', $username);
20
$icon = "alphanumeric/{$pack}/{$initial}.png";
21
if (!isset($image_map[$icon])) {
22
$icon = "alphanumeric/{$pack}/_default.png";
23
}
24
25
$border = $this->pickMap('border', $username);
26
$color = $this->pickMap('color', $username);
27
28
$data = $this->composeImage($color, $icon, $border);
29
$name = $this->getImageDisplayName($color, $icon, $border);
30
31
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
32
33
$file = PhabricatorFile::newFromFileData(
34
$data,
35
array(
36
'name' => $name,
37
'profile' => true,
38
'canCDN' => true,
39
));
40
41
$user
42
->setDefaultProfileImagePHID($file->getPHID())
43
->setDefaultProfileImageVersion(self::VERSION)
44
->saveWithoutIndex();
45
46
unset($unguarded);
47
48
return $file;
49
}
50
51
private function getMap($map_key) {
52
if (!isset($this->maps[$map_key])) {
53
switch ($map_key) {
54
case 'pack':
55
$map = $this->newPackMap();
56
break;
57
case 'image':
58
$map = $this->newImageMap();
59
break;
60
case 'color':
61
$map = $this->newColorMap();
62
break;
63
case 'border':
64
$map = $this->newBorderMap();
65
break;
66
default:
67
throw new Exception(pht('Unknown map "%s".', $map_key));
68
}
69
$this->maps[$map_key] = $map;
70
}
71
72
return $this->maps[$map_key];
73
}
74
75
private function pickMap($map_key, $username) {
76
$map = $this->getMap($map_key);
77
$seed = $username.'_'.$map_key;
78
$key = PhabricatorHash::digestToRange($seed, 0, count($map) - 1);
79
return $map[$key];
80
}
81
82
83
public function setIcon($icon) {
84
$this->icon = $icon;
85
return $this;
86
}
87
88
public function getIcon() {
89
return $this->icon;
90
}
91
92
public function setColor($color) {
93
$this->color = $color;
94
return $this;
95
}
96
97
public function getColor() {
98
return $this->color;
99
}
100
101
public function setBorder($border) {
102
$this->border = $border;
103
return $this;
104
}
105
106
public function getBorder() {
107
return $this->border;
108
}
109
110
public function getBuiltinFileKey() {
111
$icon = $this->getIcon();
112
$color = $this->getColor();
113
$border = implode(',', $this->getBorder());
114
$desc = "compose(icon={$icon}, color={$color}, border={$border}";
115
$hash = PhabricatorHash::digestToLength($desc, 40);
116
return "builtin:{$hash}";
117
}
118
119
public function getBuiltinDisplayName() {
120
return $this->getImageDisplayName(
121
$this->getIcon(),
122
$this->getColor(),
123
$this->getBorder());
124
}
125
126
private function getImageDisplayName($icon, $color, $border) {
127
$border = implode(',', $border);
128
return "{$icon}-{$color}-{$border}.png";
129
}
130
131
public function loadBuiltinFileData() {
132
return $this->composeImage(
133
$this->getColor(),
134
$this->getIcon(),
135
$this->getBorder());
136
}
137
138
private function composeImage($color, $image, $border) {
139
// If we don't have the GD extension installed, just return a static
140
// default profile image rather than trying to compose a dynamic one.
141
if (!function_exists('imagecreatefromstring')) {
142
$root = dirname(phutil_get_library_root('phabricator'));
143
$default_path = $root.'/resources/builtin/profile.png';
144
return Filesystem::readFile($default_path);
145
}
146
147
$color_const = hexdec(trim($color, '#'));
148
$true_border = self::rgba2gd($border);
149
$image_map = $this->getMap('image');
150
$data = Filesystem::readFile($image_map[$image]);
151
152
$img = imagecreatefromstring($data);
153
154
// 4 pixel border at 50x50, 32 pixel border at 400x400
155
$canvas = imagecreatetruecolor(400, 400);
156
157
$image_fill = imagefill($canvas, 0, 0, $color_const);
158
if (!$image_fill) {
159
throw new Exception(
160
pht('Failed to save builtin avatar image data (imagefill).'));
161
}
162
163
$border_thickness = imagesetthickness($canvas, 64);
164
if (!$border_thickness) {
165
throw new Exception(
166
pht('Failed to save builtin avatar image data (imagesetthickness).'));
167
}
168
169
$image_rectangle = imagerectangle($canvas, 0, 0, 400, 400, $true_border);
170
if (!$image_rectangle) {
171
throw new Exception(
172
pht('Failed to save builtin avatar image data (imagerectangle).'));
173
}
174
175
$image_copy = imagecopy($canvas, $img, 0, 0, 0, 0, 400, 400);
176
if (!$image_copy) {
177
throw new Exception(
178
pht('Failed to save builtin avatar image data (imagecopy).'));
179
}
180
181
return PhabricatorImageTransformer::saveImageDataInAnyFormat(
182
$canvas,
183
'image/png');
184
}
185
186
private static function rgba2gd(array $rgba) {
187
$r = (int)$rgba[0];
188
$g = (int)$rgba[1];
189
$b = (int)$rgba[2];
190
$a = (int)$rgba[3];
191
$a = (1 - $a) * 255;
192
return ($a << 24) | ($r << 16) | ($g << 8) | $b;
193
}
194
195
private function newImageMap() {
196
$root = dirname(phutil_get_library_root('phabricator'));
197
$root = $root.'/resources/builtin/alphanumeric/';
198
199
$map = array();
200
$list = id(new FileFinder($root))
201
->withType('f')
202
->withFollowSymlinks(true)
203
->find();
204
205
foreach ($list as $file) {
206
$map['alphanumeric/'.$file] = $root.$file;
207
}
208
209
return $map;
210
}
211
212
private function newPackMap() {
213
$root = dirname(phutil_get_library_root('phabricator'));
214
$root = $root.'/resources/builtin/alphanumeric/';
215
216
$map = id(new FileFinder($root))
217
->withType('d')
218
->withFollowSymlinks(false)
219
->find();
220
$map = array_values($map);
221
222
return $map;
223
}
224
225
private function newBorderMap() {
226
return array(
227
array(0, 0, 0, 0),
228
array(0, 0, 0, 0.3),
229
array(255, 255, 255, 0.4),
230
array(255, 255, 255, 0.7),
231
);
232
}
233
234
private function newColorMap() {
235
// Via: http://tools.medialab.sciences-po.fr/iwanthue/
236
237
return array(
238
'#335862',
239
'#2d5192',
240
'#3c5da0',
241
'#99cd86',
242
'#704889',
243
'#5ac59e',
244
'#984060',
245
'#33d4d1',
246
'#9c4050',
247
'#20d8fd',
248
'#944937',
249
'#4bd0e3',
250
'#a25542',
251
'#4eb4f3',
252
'#6da8ec',
253
'#545608',
254
'#829ce5',
255
'#68681d',
256
'#607bc2',
257
'#4b69ad',
258
'#236ead',
259
'#31a0de',
260
'#4f8ed0',
261
'#846f2a',
262
'#bdb0f0',
263
'#518342',
264
'#9166aa',
265
'#5e904e',
266
'#f79dcc',
267
'#158e6b',
268
'#e189b7',
269
'#3ba984',
270
'#a85582',
271
'#4cccb7',
272
'#863d67',
273
'#84c08c',
274
'#7f4c7f',
275
'#a1bb7a',
276
'#65558f',
277
'#445082',
278
'#c9ca8e',
279
'#265582',
280
'#f4b189',
281
'#265582',
282
'#40b8e1',
283
'#814a28',
284
'#80c8f6',
285
'#cf7b5d',
286
'#1db5c7',
287
'#c0606e',
288
'#299a89',
289
'#ef8ead',
290
'#296437',
291
'#d39edb',
292
'#507436',
293
'#b888c9',
294
'#476025',
295
'#9987c5',
296
'#7867a3',
297
'#769b5a',
298
'#c46e9d',
299
'#437d4e',
300
'#d17492',
301
'#115e41',
302
'#ec8794',
303
'#297153',
304
'#d67381',
305
'#57c2c3',
306
'#bc607f',
307
'#86ceac',
308
'#7e3e53',
309
'#72c8b8',
310
'#884349',
311
'#45a998',
312
'#faa38c',
313
'#265582',
314
'#265582',
315
'#e4b788',
316
'#265582',
317
'#bbbc81',
318
'#265582',
319
'#ccb781',
320
'#265582',
321
'#eb957f',
322
'#15729c',
323
'#cf996f',
324
'#369bc5',
325
'#b6685d',
326
'#2da0a1',
327
'#d38275',
328
'#217e70',
329
'#ec9da1',
330
'#146268',
331
'#e8aa95',
332
'#3c6796',
333
'#8da667',
334
'#935f93',
335
'#69a573',
336
'#ae78ad',
337
'#569160',
338
'#d898be',
339
'#8eb4e8',
340
'#5e622c',
341
'#929ad3',
342
'#6c8548',
343
'#576196',
344
'#aed0a0',
345
'#694e79',
346
'#9abb8d',
347
'#8c5175',
348
'#6bb391',
349
'#8b4a5f',
350
'#519878',
351
'#ae7196',
352
'#3d8465',
353
'#e69eb3',
354
'#48663d',
355
'#cdaede',
356
'#71743d',
357
'#63acda',
358
'#7b5d30',
359
'#66bed6',
360
'#3585b0',
361
'#5880b0',
362
'#739acc',
363
'#48a3ba',
364
'#9d565b',
365
'#7fc4ca',
366
'#99566b',
367
'#94cabf',
368
'#7b4b49',
369
'#b1c8eb',
370
'#4e5632',
371
'#ecb2c3',
372
'#2d6158',
373
'#cf8287',
374
'#25889f',
375
'#b2696d',
376
'#6bafb6',
377
'#8c5744',
378
'#84b9d6',
379
'#9db3d6',
380
'#777cad',
381
'#826693',
382
'#86a779',
383
'#9d7fad',
384
'#b193c2',
385
'#547348',
386
'#d5adcb',
387
'#3f674d',
388
'#c98398',
389
'#66865a',
390
'#b2add6',
391
'#5a623d',
392
'#9793bb',
393
'#3c5472',
394
'#d5c5a1',
395
'#5e5a7f',
396
'#2c647e',
397
'#d8b194',
398
'#49607f',
399
'#c7b794',
400
'#335862',
401
'#e3aba7',
402
'#335862',
403
'#d9b9ad',
404
'#335862',
405
'#c48975',
406
'#347b81',
407
'#ad697e',
408
'#799a6d',
409
'#916b88',
410
'#69536b',
411
'#b4c4ad',
412
'#845865',
413
'#96b89d',
414
'#706d92',
415
'#9aa27a',
416
'#5b7292',
417
'#bc967b',
418
'#417792',
419
'#ce9793',
420
'#335862',
421
'#c898a5',
422
'#527a5f',
423
'#b38ba9',
424
'#648d72',
425
'#986b78',
426
'#79afa4',
427
'#966461',
428
'#50959b',
429
'#b27d7a',
430
'#335862',
431
'#335862',
432
'#bcadc4',
433
'#706343',
434
'#749ebc',
435
'#8c6a50',
436
'#92b8c4',
437
'#758cad',
438
'#868e67',
439
'#335862',
440
'#335862',
441
'#335862',
442
'#ac7e8b',
443
'#77a185',
444
'#807288',
445
'#636f51',
446
'#a192a9',
447
'#467a70',
448
'#9b7d73',
449
'#335862',
450
'#335862',
451
'#8c9c85',
452
'#335862',
453
'#81645a',
454
'#5f9489',
455
'#335862',
456
'#789da8',
457
'#335862',
458
'#72826c',
459
'#335862',
460
'#5c8596',
461
'#335862',
462
'#456a74',
463
'#335862',
464
'#335862',
465
'#335862',
466
);
467
}
468
469
}
470
471