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