]> git.mxchange.org Git - friendica.git/blob - src/Object/Image.php
Moved global PAGE_* to Profile class (#5500)
[friendica.git] / src / Object / Image.php
1 <?php
2 /**
3  * @file src/Object/Image.php
4  * @brief This file contains the Image class for image processing
5  */
6 namespace Friendica\Object;
7
8 use Friendica\App;
9 use Friendica\Core\Cache;
10 use Friendica\Core\Config;
11 use Friendica\Core\L10n;
12 use Friendica\Core\System;
13 use Friendica\Database\DBA;
14 use Friendica\Model\Contact;
15 use Friendica\Model\Photo;
16 use Friendica\Util\Network;
17 use Exception;
18 use Imagick;
19 use ImagickPixel;
20
21 /**
22  * Class to handle images
23  */
24 class Image
25 {
26         private $image;
27
28         /*
29          * Put back gd stuff, not everybody have Imagick
30          */
31         private $imagick;
32         private $width;
33         private $height;
34         private $valid;
35         private $type;
36         private $types;
37
38         /**
39          * @brief supported mimetypes and corresponding file extensions
40          * @return array
41          */
42         public static function supportedTypes()
43         {
44                 if (class_exists('Imagick')) {
45                         // Imagick::queryFormats won't help us a lot there...
46                         // At least, not yet, other parts of friendica uses this array
47                         $t = [
48                                 'image/jpeg' => 'jpg',
49                                 'image/png' => 'png',
50                                 'image/gif' => 'gif'
51                         ];
52                 } else {
53                         $t = [];
54                         $t['image/jpeg'] ='jpg';
55                         if (imagetypes() & IMG_PNG) {
56                                 $t['image/png'] = 'png';
57                         }
58                 }
59
60                 return $t;
61         }
62
63         /**
64          * @brief Constructor
65          * @param object  $data data
66          * @param boolean $type optional, default null
67          * @return object
68          */
69         public function __construct($data, $type = null)
70         {
71                 $this->imagick = class_exists('Imagick');
72                 $this->types = static::supportedTypes();
73                 if (!array_key_exists($type, $this->types)) {
74                         $type='image/jpeg';
75                 }
76                 $this->type = $type;
77
78                 if ($this->isImagick() && $this->loadData($data)) {
79                         return true;
80                 } else {
81                         // Failed to load with Imagick, fallback
82                         $this->imagick = false;
83                 }
84                 return $this->loadData($data);
85         }
86
87         /**
88          * @brief Destructor
89          * @return void
90          */
91         public function __destruct()
92         {
93                 if ($this->image) {
94                         if ($this->isImagick()) {
95                                 $this->image->clear();
96                                 $this->image->destroy();
97                                 return;
98                         }
99                         if (is_resource($this->image)) {
100                                 imagedestroy($this->image);
101                         }
102                 }
103         }
104
105         /**
106          * @return boolean
107          */
108         public function isImagick()
109         {
110                 return $this->imagick;
111         }
112
113         /**
114          * @brief Maps Mime types to Imagick formats
115          * @return array With with image formats (mime type as key)
116          */
117         public static function getFormatsMap()
118         {
119                 $m = [
120                         'image/jpeg' => 'JPG',
121                         'image/png' => 'PNG',
122                         'image/gif' => 'GIF'
123                 ];
124                 return $m;
125         }
126
127         /**
128          * @param object $data data
129          * @return boolean
130          */
131         private function loadData($data)
132         {
133                 if ($this->isImagick()) {
134                         $this->image = new Imagick();
135                         try {
136                                 $this->image->readImageBlob($data);
137                         } catch (Exception $e) {
138                                 // Imagick couldn't use the data
139                                 return false;
140                         }
141
142                         /*
143                          * Setup the image to the format it will be saved to
144                          */
145                         $map = self::getFormatsMap();
146                         $format = $map[$this->type];
147                         $this->image->setFormat($format);
148
149                         // Always coalesce, if it is not a multi-frame image it won't hurt anyway
150                         $this->image = $this->image->coalesceImages();
151
152                         /*
153                          * setup the compression here, so we'll do it only once
154                          */
155                         switch ($this->getType()) {
156                                 case "image/png":
157                                         $quality = Config::get('system', 'png_quality');
158                                         if ((! $quality) || ($quality > 9)) {
159                                                 $quality = PNG_QUALITY;
160                                         }
161                                         /*
162                                          * From http://www.imagemagick.org/script/command-line-options.php#quality:
163                                          *
164                                          * 'For the MNG and PNG image formats, the quality value sets
165                                          * the zlib compression level (quality / 10) and filter-type (quality % 10).
166                                          * The default PNG "quality" is 75, which means compression level 7 with adaptive PNG filtering,
167                                          * unless the image has a color map, in which case it means compression level 7 with no PNG filtering'
168                                          */
169                                         $quality = $quality * 10;
170                                         $this->image->setCompressionQuality($quality);
171                                         break;
172                                 case "image/jpeg":
173                                         $quality = Config::get('system', 'jpeg_quality');
174                                         if ((! $quality) || ($quality > 100)) {
175                                                 $quality = JPEG_QUALITY;
176                                         }
177                                         $this->image->setCompressionQuality($quality);
178                         }
179
180                         // The 'width' and 'height' properties are only used by non-Imagick routines.
181                         $this->width  = $this->image->getImageWidth();
182                         $this->height = $this->image->getImageHeight();
183                         $this->valid  = true;
184
185                         return true;
186                 }
187
188                 $this->valid = false;
189                 $this->image = @imagecreatefromstring($data);
190                 if ($this->image !== false) {
191                         $this->width  = imagesx($this->image);
192                         $this->height = imagesy($this->image);
193                         $this->valid  = true;
194                         imagealphablending($this->image, false);
195                         imagesavealpha($this->image, true);
196
197                         return true;
198                 }
199
200                 return false;
201         }
202
203         /**
204          * @return boolean
205          */
206         public function isValid()
207         {
208                 if ($this->isImagick()) {
209                         return ($this->image !== false);
210                 }
211                 return $this->valid;
212         }
213
214         /**
215          * @return mixed
216          */
217         public function getWidth()
218         {
219                 if (!$this->isValid()) {
220                         return false;
221                 }
222
223                 if ($this->isImagick()) {
224                         return $this->image->getImageWidth();
225                 }
226                 return $this->width;
227         }
228
229         /**
230          * @return mixed
231          */
232         public function getHeight()
233         {
234                 if (!$this->isValid()) {
235                         return false;
236                 }
237
238                 if ($this->isImagick()) {
239                         return $this->image->getImageHeight();
240                 }
241                 return $this->height;
242         }
243
244         /**
245          * @return mixed
246          */
247         public function getImage()
248         {
249                 if (!$this->isValid()) {
250                         return false;
251                 }
252
253                 if ($this->isImagick()) {
254                         /* Clean it */
255                         $this->image = $this->image->deconstructImages();
256                         return $this->image;
257                 }
258                 return $this->image;
259         }
260
261         /**
262          * @return mixed
263          */
264         public function getType()
265         {
266                 if (!$this->isValid()) {
267                         return false;
268                 }
269
270                 return $this->type;
271         }
272
273         /**
274          * @return mixed
275          */
276         public function getExt()
277         {
278                 if (!$this->isValid()) {
279                         return false;
280                 }
281
282                 return $this->types[$this->getType()];
283         }
284
285         /**
286          * @param integer $max max dimension
287          * @return mixed
288          */
289         public function scaleDown($max)
290         {
291                 if (!$this->isValid()) {
292                         return false;
293                 }
294
295                 $width = $this->getWidth();
296                 $height = $this->getHeight();
297
298                 $dest_width = $dest_height = 0;
299
300                 if ((! $width)|| (! $height)) {
301                         return false;
302                 }
303
304                 if ($width > $max && $height > $max) {
305                         // very tall image (greater than 16:9)
306                         // constrain the width - let the height float.
307
308                         if ((($height * 9) / 16) > $width) {
309                                 $dest_width = $max;
310                                 $dest_height = intval(($height * $max) / $width);
311                         } elseif ($width > $height) {
312                                 // else constrain both dimensions
313                                 $dest_width = $max;
314                                 $dest_height = intval(($height * $max) / $width);
315                         } else {
316                                 $dest_width = intval(($width * $max) / $height);
317                                 $dest_height = $max;
318                         }
319                 } else {
320                         if ($width > $max) {
321                                 $dest_width = $max;
322                                 $dest_height = intval(($height * $max) / $width);
323                         } else {
324                                 if ($height > $max) {
325                                         // very tall image (greater than 16:9)
326                                         // but width is OK - don't do anything
327
328                                         if ((($height * 9) / 16) > $width) {
329                                                 $dest_width = $width;
330                                                 $dest_height = $height;
331                                         } else {
332                                                 $dest_width = intval(($width * $max) / $height);
333                                                 $dest_height = $max;
334                                         }
335                                 } else {
336                                         $dest_width = $width;
337                                         $dest_height = $height;
338                                 }
339                         }
340                 }
341
342                 return $this->scale($dest_width, $dest_height);
343         }
344
345         /**
346          * @param integer $degrees degrees to rotate image
347          * @return mixed
348          */
349         public function rotate($degrees)
350         {
351                 if (!$this->isValid()) {
352                         return false;
353                 }
354
355                 if ($this->isImagick()) {
356                         $this->image->setFirstIterator();
357                         do {
358                                 $this->image->rotateImage(new ImagickPixel(), -$degrees); // ImageMagick rotates in the opposite direction of imagerotate()
359                         } while ($this->image->nextImage());
360                         return;
361                 }
362
363                 // if script dies at this point check memory_limit setting in php.ini
364                 $this->image  = imagerotate($this->image, $degrees, 0);
365                 $this->width  = imagesx($this->image);
366                 $this->height = imagesy($this->image);
367         }
368
369         /**
370          * @param boolean $horiz optional, default true
371          * @param boolean $vert  optional, default false
372          * @return mixed
373          */
374         public function flip($horiz = true, $vert = false)
375         {
376                 if (!$this->isValid()) {
377                         return false;
378                 }
379
380                 if ($this->isImagick()) {
381                         $this->image->setFirstIterator();
382                         do {
383                                 if ($horiz) {
384                                         $this->image->flipImage();
385                                 }
386                                 if ($vert) {
387                                         $this->image->flopImage();
388                                 }
389                         } while ($this->image->nextImage());
390                         return;
391                 }
392
393                 $w = imagesx($this->image);
394                 $h = imagesy($this->image);
395                 $flipped = imagecreate($w, $h);
396                 if ($horiz) {
397                         for ($x = 0; $x < $w; $x++) {
398                                 imagecopy($flipped, $this->image, $x, 0, $w - $x - 1, 0, 1, $h);
399                         }
400                 }
401                 if ($vert) {
402                         for ($y = 0; $y < $h; $y++) {
403                                 imagecopy($flipped, $this->image, 0, $y, 0, $h - $y - 1, $w, 1);
404                         }
405                 }
406                 $this->image = $flipped;
407         }
408
409         /**
410          * @param string $filename filename
411          * @return mixed
412          */
413         public function orient($filename)
414         {
415                 if ($this->isImagick()) {
416                         // based off comment on http://php.net/manual/en/imagick.getimageorientation.php
417                         $orientation = $this->image->getImageOrientation();
418                         switch ($orientation) {
419                                 case Imagick::ORIENTATION_BOTTOMRIGHT:
420                                         $this->image->rotateimage("#000", 180);
421                                         break;
422                                 case Imagick::ORIENTATION_RIGHTTOP:
423                                         $this->image->rotateimage("#000", 90);
424                                         break;
425                                 case Imagick::ORIENTATION_LEFTBOTTOM:
426                                         $this->image->rotateimage("#000", -90);
427                                         break;
428                         }
429
430                         $this->image->setImageOrientation(Imagick::ORIENTATION_TOPLEFT);
431                         return true;
432                 }
433                 // based off comment on http://php.net/manual/en/function.imagerotate.php
434
435                 if (!$this->isValid()) {
436                         return false;
437                 }
438
439                 if ((!function_exists('exif_read_data')) || ($this->getType() !== 'image/jpeg')) {
440                         return;
441                 }
442
443                 $exif = @exif_read_data($filename, null, true);
444                 if (!$exif) {
445                         return;
446                 }
447
448                 $ort = $exif['IFD0']['Orientation'];
449
450                 switch ($ort) {
451                         case 1: // nothing
452                                 break;
453
454                         case 2: // horizontal flip
455                                 $this->flip();
456                                 break;
457
458                         case 3: // 180 rotate left
459                                 $this->rotate(180);
460                                 break;
461
462                         case 4: // vertical flip
463                                 $this->flip(false, true);
464                                 break;
465
466                         case 5: // vertical flip + 90 rotate right
467                                 $this->flip(false, true);
468                                 $this->rotate(-90);
469                                 break;
470
471                         case 6: // 90 rotate right
472                                 $this->rotate(-90);
473                                 break;
474
475                         case 7: // horizontal flip + 90 rotate right
476                                 $this->flip();
477                                 $this->rotate(-90);
478                                 break;
479
480                         case 8: // 90 rotate left
481                                 $this->rotate(90);
482                                 break;
483                 }
484
485                 //      logger('exif: ' . print_r($exif,true));
486                 return $exif;
487         }
488
489         /**
490          * @param integer $min minimum dimension
491          * @return mixed
492          */
493         public function scaleUp($min)
494         {
495                 if (!$this->isValid()) {
496                         return false;
497                 }
498
499                 $width = $this->getWidth();
500                 $height = $this->getHeight();
501
502                 $dest_width = $dest_height = 0;
503
504                 if ((!$width)|| (!$height)) {
505                         return false;
506                 }
507
508                 if ($width < $min && $height < $min) {
509                         if ($width > $height) {
510                                 $dest_width = $min;
511                                 $dest_height = intval(($height * $min) / $width);
512                         } else {
513                                 $dest_width = intval(($width * $min) / $height);
514                                 $dest_height = $min;
515                         }
516                 } else {
517                         if ($width < $min) {
518                                 $dest_width = $min;
519                                 $dest_height = intval(($height * $min) / $width);
520                         } else {
521                                 if ($height < $min) {
522                                         $dest_width = intval(($width * $min) / $height);
523                                         $dest_height = $min;
524                                 } else {
525                                         $dest_width = $width;
526                                         $dest_height = $height;
527                                 }
528                         }
529                 }
530
531                 return $this->scale($dest_width, $dest_height);
532         }
533
534         /**
535          * @param integer $dim dimension
536          * @return mixed
537          */
538         public function scaleToSquare($dim)
539         {
540                 if (!$this->isValid()) {
541                         return false;
542                 }
543
544                 return $this->scale($dim, $dim);
545         }
546
547         /**
548          * @brief Scale image to target dimensions
549          *
550          * @param int $dest_width
551          * @param int $dest_height
552          * @return boolean
553          */
554         private function scale($dest_width, $dest_height)
555         {
556                 if (!$this->isValid()) {
557                         return false;
558                 }
559
560                 if ($this->isImagick()) {
561                         /*
562                          * If it is not animated, there will be only one iteration here,
563                          * so don't bother checking
564                          */
565                         // Don't forget to go back to the first frame
566                         $this->image->setFirstIterator();
567                         do {
568                                 // FIXME - implement horizontal bias for scaling as in following GD functions
569                                 // to allow very tall images to be constrained only horizontally.
570                                 $this->image->scaleImage($dest_width, $dest_height);
571                         } while ($this->image->nextImage());
572
573                         // These may not be necessary anymore
574                         $this->width  = $this->image->getImageWidth();
575                         $this->height = $this->image->getImageHeight();
576                 } else {
577                         $dest = imagecreatetruecolor($dest_width, $dest_height);
578                         imagealphablending($dest, false);
579                         imagesavealpha($dest, true);
580
581                         if ($this->type=='image/png') {
582                                 imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
583                         }
584
585                         imagecopyresampled($dest, $this->image, 0, 0, 0, 0, $dest_width, $dest_height, $this->width, $this->height);
586
587                         if ($this->image) {
588                                 imagedestroy($this->image);
589                         }
590
591                         $this->image = $dest;
592                         $this->width  = imagesx($this->image);
593                         $this->height = imagesy($this->image);
594                 }
595
596                 return true;
597         }
598
599         /**
600          * @param integer $max maximum
601          * @param integer $x   x coordinate
602          * @param integer $y   y coordinate
603          * @param integer $w   width
604          * @param integer $h   height
605          * @return mixed
606          */
607         public function crop($max, $x, $y, $w, $h)
608         {
609                 if (!$this->isValid()) {
610                         return false;
611                 }
612
613                 if ($this->isImagick()) {
614                         $this->image->setFirstIterator();
615                         do {
616                                 $this->image->cropImage($w, $h, $x, $y);
617                                 /*
618                                  * We need to remove the canva,
619                                  * or the image is not resized to the crop:
620                                  * http://php.net/manual/en/imagick.cropimage.php#97232
621                                  */
622                                 $this->image->setImagePage(0, 0, 0, 0);
623                         } while ($this->image->nextImage());
624                         return $this->scaleDown($max);
625                 }
626
627                 $dest = imagecreatetruecolor($max, $max);
628                 imagealphablending($dest, false);
629                 imagesavealpha($dest, true);
630                 if ($this->type=='image/png') {
631                         imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
632                 }
633                 imagecopyresampled($dest, $this->image, 0, 0, $x, $y, $max, $max, $w, $h);
634                 if ($this->image) {
635                         imagedestroy($this->image);
636                 }
637                 $this->image = $dest;
638                 $this->width  = imagesx($this->image);
639                 $this->height = imagesy($this->image);
640         }
641
642         /**
643          * @param string $path file path
644          * @return mixed
645          */
646         public function saveToFilePath($path)
647         {
648                 if (!$this->isValid()) {
649                         return false;
650                 }
651
652                 $string = $this->asString();
653
654                 $a = get_app();
655
656                 $stamp1 = microtime(true);
657                 file_put_contents($path, $string);
658                 $a->save_timestamp($stamp1, "file");
659         }
660
661         /**
662          * @brief Magic method allowing string casting of an Image object
663          *
664          * Ex: $data = $Image->asString();
665          * can be replaced by
666          * $data = (string) $Image;
667          *
668          * @return string
669          */
670         public function __toString() {
671                 return $this->asString();
672         }
673
674         /**
675          * @return mixed
676          */
677         public function asString()
678         {
679                 if (!$this->isValid()) {
680                         return false;
681                 }
682
683                 if ($this->isImagick()) {
684                         /* Clean it */
685                         $this->image = $this->image->deconstructImages();
686                         $string = $this->image->getImagesBlob();
687                         return $string;
688                 }
689
690                 $quality = false;
691
692                 ob_start();
693
694                 // Enable interlacing
695                 imageinterlace($this->image, true);
696
697                 switch ($this->getType()) {
698                         case "image/png":
699                                 $quality = Config::get('system', 'png_quality');
700                                 if ((!$quality) || ($quality > 9)) {
701                                         $quality = PNG_QUALITY;
702                                 }
703                                 imagepng($this->image, null, $quality);
704                                 break;
705                         case "image/jpeg":
706                                 $quality = Config::get('system', 'jpeg_quality');
707                                 if ((!$quality) || ($quality > 100)) {
708                                         $quality = JPEG_QUALITY;
709                                 }
710                                 imagejpeg($this->image, null, $quality);
711                 }
712                 $string = ob_get_contents();
713                 ob_end_clean();
714
715                 return $string;
716         }
717
718         /**
719          * Guess image mimetype from filename or from Content-Type header
720          *
721          * @param string  $filename Image filename
722          * @param boolean $fromcurl Check Content-Type header from curl request
723          *
724          * @return object
725          */
726         public static function guessType($filename, $fromcurl = false)
727         {
728                 logger('Image: guessType: '.$filename . ($fromcurl?' from curl headers':''), LOGGER_DEBUG);
729                 $type = null;
730                 if ($fromcurl) {
731                         $a = get_app();
732                         $headers=[];
733                         $h = explode("\n", $a->get_curl_headers());
734                         foreach ($h as $l) {
735                                 $data = array_map("trim", explode(":", trim($l), 2));
736                                 if (count($data) > 1) {
737                                         list($k,$v) = $data;
738                                         $headers[$k] = $v;
739                                 }
740                         }
741                         if (array_key_exists('Content-Type', $headers))
742                                 $type = $headers['Content-Type'];
743                 }
744                 if (is_null($type)) {
745                         // Guessing from extension? Isn't that... dangerous?
746                         if (class_exists('Imagick') && file_exists($filename) && is_readable($filename)) {
747                                 /**
748                                  * Well, this not much better,
749                                  * but at least it comes from the data inside the image,
750                                  * we won't be tricked by a manipulated extension
751                                  */
752                                 $image = new Imagick($filename);
753                                 $type = $image->getImageMimeType();
754                                 $image->setInterlaceScheme(Imagick::INTERLACE_PLANE);
755                         } else {
756                                 $ext = pathinfo($filename, PATHINFO_EXTENSION);
757                                 $types = self::supportedTypes();
758                                 $type = "image/jpeg";
759                                 foreach ($types as $m => $e) {
760                                         if ($ext == $e) {
761                                                 $type = $m;
762                                         }
763                                 }
764                         }
765                 }
766                 logger('Image: guessType: type='.$type, LOGGER_DEBUG);
767                 return $type;
768         }
769
770         /**
771          * @param string $url url
772          * @return object
773          */
774         public static function getInfoFromURL($url)
775         {
776                 $data = [];
777
778                 if (empty($url)) {
779                         return $data;
780                 }
781
782                 $data = Cache::get($url);
783
784                 if (is_null($data) || !$data || !is_array($data)) {
785                         $img_str = Network::fetchUrl($url, true, $redirects, 4);
786
787                         if (!$img_str) {
788                                 return false;
789                         }
790
791                         $filesize = strlen($img_str);
792
793                         try {
794                                 if (function_exists("getimagesizefromstring")) {
795                                         $data = getimagesizefromstring($img_str);
796                                 } else {
797                                         $tempfile = tempnam(get_temppath(), "cache");
798
799                                         $a = get_app();
800                                         $stamp1 = microtime(true);
801                                         file_put_contents($tempfile, $img_str);
802                                         $a->save_timestamp($stamp1, "file");
803
804                                         $data = getimagesize($tempfile);
805                                         unlink($tempfile);
806                                 }
807                         } catch (Exception $e) {
808                                 return false;
809                         }
810
811                         if ($data) {
812                                 $data["size"] = $filesize;
813                         }
814
815                         Cache::set($url, $data);
816                 }
817
818                 return $data;
819         }
820
821         /**
822          * @param integer $width  width
823          * @param integer $height height
824          * @param integer $max    max
825          * @return array
826          */
827         public static function getScalingDimensions($width, $height, $max)
828         {
829                 $dest_width = $dest_height = 0;
830
831                 if ((!$width) || (!$height)) {
832                         return false;
833                 }
834
835                 if ($width > $max && $height > $max) {
836                         // very tall image (greater than 16:9)
837                         // constrain the width - let the height float.
838
839                         if ((($height * 9) / 16) > $width) {
840                                 $dest_width = $max;
841                                 $dest_height = intval(($height * $max) / $width);
842                         } elseif ($width > $height) {
843                                 // else constrain both dimensions
844                                 $dest_width = $max;
845                                 $dest_height = intval(($height * $max) / $width);
846                         } else {
847                                 $dest_width = intval(($width * $max) / $height);
848                                 $dest_height = $max;
849                         }
850                 } else {
851                         if ($width > $max) {
852                                 $dest_width = $max;
853                                 $dest_height = intval(($height * $max) / $width);
854                         } else {
855                                 if ($height > $max) {
856                                         // very tall image (greater than 16:9)
857                                         // but width is OK - don't do anything
858
859                                         if ((($height * 9) / 16) > $width) {
860                                                 $dest_width = $width;
861                                                 $dest_height = $height;
862                                         } else {
863                                                 $dest_width = intval(($width * $max) / $height);
864                                                 $dest_height = $max;
865                                         }
866                                 } else {
867                                         $dest_width = $width;
868                                         $dest_height = $height;
869                                 }
870                         }
871                 }
872                 return ["width" => $dest_width, "height" => $dest_height];
873         }
874
875         /**
876          * @brief This function is used by the fromgplus addon
877          * @param object  $a         App
878          * @param integer $uid       user id
879          * @param string  $imagedata optional, default empty
880          * @param string  $url       optional, default empty
881          * @return array
882          */
883         public static function storePhoto(App $a, $uid, $imagedata = "", $url = "")
884         {
885                 $r = q(
886                         "SELECT `user`.`nickname`, `user`.`page-flags`, `contact`.`id` FROM `user` INNER JOIN `contact` on `user`.`uid` = `contact`.`uid`
887                         WHERE `user`.`uid` = %d AND `user`.`blocked` = 0 AND `contact`.`self` = 1 LIMIT 1",
888                         intval($uid)
889                 );
890
891                 if (!DBA::isResult($r)) {
892                         logger("Can't detect user data for uid ".$uid, LOGGER_DEBUG);
893                         return([]);
894                 }
895
896                 $page_owner_nick  = $r[0]['nickname'];
897
898                 /// @TODO
899                 /// $default_cid      = $r[0]['id'];
900                 /// $community_page   = (($r[0]['page-flags'] == Contact::PAGE_COMMUNITY) ? true : false);
901
902                 if ((strlen($imagedata) == 0) && ($url == "")) {
903                         logger("No image data and no url provided", LOGGER_DEBUG);
904                         return([]);
905                 } elseif (strlen($imagedata) == 0) {
906                         logger("Uploading picture from ".$url, LOGGER_DEBUG);
907
908                         $stamp1 = microtime(true);
909                         $imagedata = @file_get_contents($url);
910                         $a->save_timestamp($stamp1, "file");
911                 }
912
913                 $maximagesize = Config::get('system', 'maximagesize');
914
915                 if (($maximagesize) && (strlen($imagedata) > $maximagesize)) {
916                         logger("Image exceeds size limit of ".$maximagesize, LOGGER_DEBUG);
917                         return([]);
918                 }
919
920                 $tempfile = tempnam(get_temppath(), "cache");
921
922                 $stamp1 = microtime(true);
923                 file_put_contents($tempfile, $imagedata);
924                 $a->save_timestamp($stamp1, "file");
925
926                 $data = getimagesize($tempfile);
927
928                 if (!isset($data["mime"])) {
929                         unlink($tempfile);
930                         logger("File is no picture", LOGGER_DEBUG);
931                         return([]);
932                 }
933
934                 $Image = new Image($imagedata, $data["mime"]);
935
936                 if (!$Image->isValid()) {
937                         unlink($tempfile);
938                         logger("Picture is no valid picture", LOGGER_DEBUG);
939                         return([]);
940                 }
941
942                 $Image->orient($tempfile);
943                 unlink($tempfile);
944
945                 $max_length = Config::get('system', 'max_image_length');
946                 if (! $max_length) {
947                         $max_length = MAX_IMAGE_LENGTH;
948                 }
949
950                 if ($max_length > 0) {
951                         $Image->scaleDown($max_length);
952                 }
953
954                 $width = $Image->getWidth();
955                 $height = $Image->getHeight();
956
957                 $hash = Photo::newResource();
958
959                 $smallest = 0;
960
961                 // Pictures are always public by now
962                 //$defperm = '<'.$default_cid.'>';
963                 $defperm = "";
964                 $visitor = 0;
965
966                 $r = Photo::store($Image, $uid, $visitor, $hash, $tempfile, L10n::t('Wall Photos'), 0, 0, $defperm);
967
968                 if (!$r) {
969                         logger("Picture couldn't be stored", LOGGER_DEBUG);
970                         return([]);
971                 }
972
973                 $image = ["page" => System::baseUrl().'/photos/'.$page_owner_nick.'/image/'.$hash,
974                         "full" => System::baseUrl()."/photo/{$hash}-0.".$Image->getExt()];
975
976                 if ($width > 800 || $height > 800) {
977                         $image["large"] = System::baseUrl()."/photo/{$hash}-0.".$Image->getExt();
978                 }
979
980                 if ($width > 640 || $height > 640) {
981                         $Image->scaleDown(640);
982                         $r = Photo::store($Image, $uid, $visitor, $hash, $tempfile, L10n::t('Wall Photos'), 1, 0, $defperm);
983                         if ($r) {
984                                 $image["medium"] = System::baseUrl()."/photo/{$hash}-1.".$Image->getExt();
985                         }
986                 }
987
988                 if ($width > 320 || $height > 320) {
989                         $Image->scaleDown(320);
990                         $r = Photo::store($Image, $uid, $visitor, $hash, $tempfile, L10n::t('Wall Photos'), 2, 0, $defperm);
991                         if ($r) {
992                                 $image["small"] = System::baseUrl()."/photo/{$hash}-2.".$Image->getExt();
993                         }
994                 }
995
996                 if ($width > 160 && $height > 160) {
997                         $x = 0;
998                         $y = 0;
999
1000                         $min = $Image->getWidth();
1001                         if ($min > 160) {
1002                                 $x = ($min - 160) / 2;
1003                         }
1004
1005                         if ($Image->getHeight() < $min) {
1006                                 $min = $Image->getHeight();
1007                                 if ($min > 160) {
1008                                         $y = ($min - 160) / 2;
1009                                 }
1010                         }
1011
1012                         $min = 160;
1013                         $Image->crop(160, $x, $y, $min, $min);
1014
1015                         $r = Photo::store($Image, $uid, $visitor, $hash, $tempfile, L10n::t('Wall Photos'), 3, 0, $defperm);
1016                         if ($r) {
1017                                 $image["thumb"] = System::baseUrl()."/photo/{$hash}-3.".$Image->getExt();
1018                         }
1019                 }
1020
1021                 // Set the full image as preview image. This will be overwritten, if the picture is larger than 640.
1022                 $image["preview"] = $image["full"];
1023
1024                 // Deactivated, since that would result in a cropped preview, if the picture wasn't larger than 320
1025                 //if (isset($image["thumb"]))
1026                 //  $image["preview"] = $image["thumb"];
1027
1028                 // Unsure, if this should be activated or deactivated
1029                 //if (isset($image["small"]))
1030                 //  $image["preview"] = $image["small"];
1031
1032                 if (isset($image["medium"])) {
1033                         $image["preview"] = $image["medium"];
1034                 }
1035
1036                 return($image);
1037         }
1038 }