Completed
Push — master ( d21053...c59b30 )
by
unknown
08:11
created

TidypicsImage::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 3
ccs 0
cts 3
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Tidypics Image class
4
 *
5
 * @package TidypicsImage
6
 * @author Cash Costello
7
 * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
8
 */
9
10
11
class TidypicsImage extends ElggFile {
12
	protected function initializeAttributes() {
13
		parent::initializeAttributes();
14
15
		$this->attributes['subtype'] = "image";
16
	}
17
18
	public function __construct($guid = null) {
19
		parent::__construct($guid);
20
	}
21
22
	/**
23
	 * Save the image
24
	 *
25
	 * @warning container_guid must be set first
26
	 *
27
	 * @param array $data
28
	 * @return bool
29
	 */
30
	public function save($data = null) {
31
32
		if (!parent::save()) {
33
			return false;
34
		}
35
36
		if ($data) {
37
			// new image
38
			$this->simpletype = "image";
39
			$this->saveImageFile($data);
40
			$this->saveThumbnails();
41
			$this->extractExifData();
42
		}
43
44
		return true;
45
	}
46
47
	/**
48
	 * Delete image
49
	 *
50
	 * @return bool
51
	 */
52
	public function delete() {
53
54
		// check if batch should be deleted
55
		$batch = elgg_get_entities_from_relationship(array(
56
			'relationship' => 'belongs_to_batch',
57
			'relationship_guid' => $this->guid,
58
			'inverse_relationship' => false,
59
		));
60 View Code Duplication
		if ($batch) {
61
			$batch = $batch[0];
62
			$count = elgg_get_entities_from_relationship(array(
63
				'relationship' => 'belongs_to_batch',
64
				'relationship_guid' => $batch->guid,
65
				'inverse_relationship' => true,
66
				'count' => true,
67
			));
68
			if ($count == 1) {
69
				// last image so delete batch
70
				$batch->delete();
71
			}
72
		}
73
74
		$this->removeThumbnails();
75
76
		$album = get_entity($this->container_guid);
77
		if ($album) {
78
			$album->removeImage($this->guid);
79
		}
80
81
		// update quota
82
		$owner = $this->getOwnerEntity();
83
		$owner->image_repo_size = (int)$owner->image_repo_size - $this->getSize();
84
85
		return parent::delete();
86
	}
87
88
	/**
89
	 * Get the title of the image
90
	 *
91
	 * @return string
92
	 */
93
	public function getTitle() {
94
		if ($this->title) {
95
			return $this->title;
96
		} else {
97
			return $this->originalfilename;
98
		}
99
	}
100
101
	/**
102
	 * Get the URL for the web page of this image
103
	 *
104
	 * @return string
105
	 */
106
	public function getURL() {
107
		$title = elgg_get_friendly_title($this->getTitle());
108
		$url = "photos/image/$this->guid/$title";
109
		return elgg_normalize_url($url);
110
	}
111
112
	/**
113
	 * Get the src URL for the image
114
	 *
115
	 * @return string
116
	 */
117
	public function getIconURL($size = 'small') {
118
		if ($size == 'tiny') {
119
			$size = 'thumb';
120
		}
121
		return elgg_normalize_url("photos/thumbnail/$this->guid/$size/");
122
	}
123
124
	/**
125
	 * Get the view information for this image
126
	 *
127
	 * @param $viewer_guid The guid of the viewer
128
	 * @return array with number of views, number of unique viewers, and number of views for this viewer
129
	 */
130
	public function getViewInfo($viewer_guid = 0) {
131
		if ($viewer_guid == 0) {
132
			$viewer_guid = elgg_get_logged_in_user_guid();
133
		}
134
135
		$views = elgg_get_annotations(array(
136
			'guid' => $this->getGUID(),
137
			'annotation_name' => 'tp_view',
138
			'limit' => 0,
139
		));
140
		if ($views) {
141
			$total_views = count($views);
142
143
			if ($this->getOwnerGUID() == $viewer_guid) {
144
				// get unique number of viewers
145
				$diff_viewers = array();
146
				foreach ($views as $view) {
147
					$diff_viewers[$view->owner_guid] = 1;
148
				}
149
				$unique_viewers = count($diff_viewers);
150
			} else if ($viewer_guid) {
151
				// get the number of times this user has viewed the photo
152
				$my_views = 0;
153
				foreach ($views as $view) {
154
					if ($view->owner_guid == $viewer_guid) {
155
						$my_views++;
156
					}
157
				}
158
			}
159
160
			$view_info = array("total" => $total_views, "unique" => $unique_viewers, "mine" => $my_views);
0 ignored issues
show
Bug introduced by
The variable $unique_viewers does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The variable $my_views does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
161
		}
162
		else {
163
			$view_info = array("total" => 0, "unique" => 0, "mine" => 0);
164
		}
165
166
		return $view_info;
167
	}
168
169
	/**
170
	 * Add a view to this image
171
	 *
172
	 * @param $viewer_guid
173
	 * @return void
174
	 */
175
	public function addView($viewer_guid = 0) {
176
		if ($viewer_guid == 0) {
177
			$viewer_guid = elgg_get_logged_in_user_guid();
178
		}
179
180
		if ($viewer_guid != $this->owner_guid && tp_is_person()) {
181
			create_annotation($this->getGUID(), "tp_view", "1", "integer", $viewer_guid, ACCESS_PUBLIC);
182
		}
183
	}
184
185
186
	/**
187
	 * Set the internal filenames
188
	 */
189
	protected function setOriginalFilename($originalName) {
190
		$prefix = "image/" . $this->container_guid . "/";
191
		$filestorename = elgg_strtolower(time() . $originalName);
192
		$this->setFilename($prefix . $filestorename);
193
		$this->originalfilename = $originalName;
194
	}
195
196
	/**
197
	 * Auto-correction of image orientation based on exif data
198
	 *
199
	 * @param array $data
200
	 */
201
	protected function OrientationCorrection($data) {
202
		// catch for those who don't have exif module loaded
203
		if (!is_callable('exif_read_data')) {
204
			return;
205
		}
206
		$exif = exif_read_data($data['tmp_name']);
207
		$orientation = isset($exif['Orientation']) ? $exif['Orientation'] : 0;
208
		if($orientation != 0 || $orientation != 1) {
209
210
			$imageLib = elgg_get_plugin_setting('image_lib', 'tidypics');
211
212
			if ($imageLib == 'ImageMagick') {
213
				// ImageMagick command line
214
				$im_path = elgg_get_plugin_setting('im_path', 'tidypics');
215
				if (!$im_path) {
216
					$im_path = "/usr/bin/";
217
				}
218
				if (substr($im_path, strlen($im_path)-1, 1) != "/") {
219
					$im_path .= "/";
220
				}
221
222
				$filename = $data['tmp_name'];
223
				$command = $im_path . "mogrify -auto-orient $filename";
224
				$output = array();
225
				$ret = 0;
226
				exec($command, $output, $ret);
227
			} else if ($imageLib == 'ImageMagickPHP') {
228
				// imagick php extension
229
				$rotate = false;
230
				$flop = false;
231
				$angle = 0;
232 View Code Duplication
				switch($orientation) {
233
					case 2:
234
						$rotate = false;
235
						$flop = true;
236
						break;
237
					case 3:
238
						$rotate = true;
239
						$flop = false;
240
						$angle = 180;
241
						break;
242
					case 4:
243
						$rotate = true;
244
						$flop = true;
245
						$angle = 180;
246
						break;
247
					case 5:
248
						$rotate = true;
249
						$flop = true;
250
						$angle = 90;
251
						break;
252
					case 6:
253
						$rotate = true;
254
						$flop = false;
255
						$angle = 90;
256
						break;
257
					case 7:
258
						$rotate = true;
259
						$flop = true;
260
						$angle = -90;
261
						break;
262
					case 8:
263
						$rotate = true;
264
						$flop = false;
265
						$angle = -90;
266
						break;
267
					default:
268
						$rotate = false;
269
						$flop = false;
270
						break;
271
				}
272
				$imagick = new Imagick();
273
				$imagick->readImage($data['tmp_name']);
274
				if ($rotate) {
275
					$imagick->rotateImage('#000000', $angle);
276
				}
277
				if ($flop) {
278
					$imagick->flopImage();
279
				}
280
				$imagick->setImageOrientation(imagick::ORIENTATION_TOPLEFT);
281
				$imagick->writeImage($data['tmp_name']);
282
				$imagick->clear();
283
				$imagick->destroy(); 
284
			} else {
285
				// make sure the in memory image size does not exceed memory available
286
				$imginfo = getimagesize($data['tmp_name']);
287
				$requiredMemory1 = ceil($imginfo[0] * $imginfo[1] * 5.35);
288
				$requiredMemory2 = ceil($imginfo[0] * $imginfo[1] * ($imginfo['bits'] / 8) * $imginfo['channels'] * 2.5);
289
				$requiredMemory = (int)max($requiredMemory1, $requiredMemory2);
290
291
				$mem_avail = elgg_get_ini_setting_in_bytes('memory_limit');
292
				$mem_used = memory_get_usage();
293
294
				$mem_avail = $mem_avail - $mem_used - 2097152; // 2 MB buffer
295
				if ($requiredMemory < $mem_avail) {
296
					$image = imagecreatefromstring(file_get_contents($data['tmp_name']));
297
					$rotate = false;
298
					$flip = false;
299
					$angle = 0;
300 View Code Duplication
					switch($orientation) {
301
						case 2:
302
							$rotate = false;
303
							$flip = true;
304
							break;
305
						case 3:
306
							$rotate = true;
307
							$flip = false;
308
							$angle = 180;
309
							break;
310
						case 4:
311
							$rotate = true;
312
							$flip = true;
313
							$angle = 180;
314
							break;
315
						case 5:
316
							$rotate = true;
317
							$flip = true;
318
							$angle = -90;
319
							break;
320
						case 6:
321
							$rotate = true;
322
							$flip = false;
323
							$angle = -90;
324
							break;
325
						case 7:
326
							$rotate = true;
327
							$flip = true;
328
							$angle = 90;
329
							break;
330
						case 8:
331
							$rotate = true;
332
							$flip = false;
333
							$angle = 90;
334
							break;
335
						default:
336
							$rotate = false;
337
							$flip = false;
338
							break;
339
					}
340
					if ($rotate) {
341
						$image = imagerotate($image, $angle, 0);
342
						imagejpeg($image, $data['tmp_name']);
343
					}
344
					if ($flip) {
345
						$mem_avail = elgg_get_ini_setting_in_bytes('memory_limit');
346
						$mem_used = memory_get_usage();
347
348
						$mem_avail = $mem_avail - $mem_used - 2097152; // 2 MB buffer
349
						if (($requiredMemory) < $mem_avail) {
350
							$width = imagesx($image);
351
							$height = imagesy($image);
352
							$src_x = 0;
353
							$src_y = 0;
354
							$src_width = $width;
355
							$src_height = $height;
356
							$src_x = $width -1;
357
							$src_width = -$width;
358
							$imgdest = imagecreatetruecolor($width, $height);
359
							imagecopyresampled($imgdest, $image, 0, 0, $src_x, $src_y, $width, $height, $src_width, $src_height);
360
							imagejpeg($imgdest, $data['tmp_name']);
361
							imagedestroy($imgdest);
362
						}
363
					}
364
					imagedestroy($image);
365
				}
366
			}
367
		}
368
	}
369
370
	/**
371
	 * Save the uploaded image
372
	 *
373
	 * @param array $data
374
	 */
375
	protected function saveImageFile($data) {
376
		$this->checkUploadErrors($data);
0 ignored issues
show
Documentation introduced by
$data is of type array, but the function expects a object<type>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
377
378
		$this->OrientationCorrection($data);
379
380
		// we need to make sure the directory for the album exists
381
		// @note for group albums, the photos are distributed among the users
382
		$dir = tp_get_img_dir($this->getContainerGUID());
383
		if (!file_exists($dir)) {
384
			mkdir($dir, 0755, true);
385
		}
386
387
		// move the uploaded file into album directory
388
		$this->setOriginalFilename($data['name']);
389
		$filename = $this->getFilenameOnFilestore();
390
		$result = move_uploaded_file($data['tmp_name'], $filename);
391
		if (!$result) {
392
			return false;
393
		}
394
395
		$owner = $this->getOwnerEntity();
396
		$owner->image_repo_size = (int)$owner->image_repo_size + $this->getSize();
397
398
		return true;
399
	}
400
401
	/**
402
	 * Need to restore sanity to this function
403
	 * @param type $data
404
	 */
405
	protected function checkUploadErrors($data) {
406
		// check for upload errors
407
		if ($data['error']) {
408
			if ($data['error'] == 1) {
409
				trigger_error('Tidypics warning: image exceeded server php upload limit', E_USER_WARNING);
410
				throw new Exception(elgg_echo('tidypics:image_mem'));
411
			} else {
412
				throw new Exception(elgg_echo('tidypics:unk_error'));
413
			}
414
		}
415
416
		// must be an image
417
		if (!tp_upload_check_format($data['type'])) {
418
			throw new Exception(elgg_echo('tidypics:not_image'));
419
		}
420
421
		// make sure file does not exceed memory limit
422
		if (!tp_upload_check_max_size($data['size'])) {
423
			throw new Exception(elgg_echo('tidypics:image_mem'));
424
		}
425
426
		// make sure the in memory image size does not exceed memory available
427
		$imginfo = getimagesize($data['tmp_name']);
428
		$requiredMemory1 = ceil($imginfo[0] * $imginfo[1] * 5.35);
429
		$requiredMemory2 = ceil($imginfo[0] * $imginfo[1] * ($imginfo['bits'] / 8) * $imginfo['channels'] * 2.5);
430
		$requiredMemory = (int)max($requiredMemory1, $requiredMemory2);
431
		$image_lib = elgg_get_plugin_setting('image_lib', 'tidypics');
432
		if (!tp_upload_memory_check($image_lib, $requiredMemory)) {
433
			trigger_error('Tidypics warning: image memory size too large for resizing so rejecting', E_USER_WARNING);
434
			throw new Exception(elgg_echo('tidypics:image_pixels'));
435
		}
436
437
		// make sure file fits quota
438
		if (!tp_upload_check_quota($data['size'], elgg_get_logged_in_user_guid())) {
439
			throw new Exception(elgg_echo('tidypics:cannot_upload_exceeds_quota'));
440
		}
441
	}
442
443
	/**
444
	 * Save the image thumbnails
445
	 */
446
	protected function saveThumbnails() {
447
		elgg_load_library('tidypics:resize');
448
449
		$imageLib = elgg_get_plugin_setting('image_lib', 'tidypics');
450
451
		$prefix = "image/" . $this->container_guid . "/";
452
		$filename = $this->getFilename();
453
		$filename = substr($filename, strrpos($filename, '/') + 1);
454
455
		if ($imageLib == 'ImageMagick') {
456
			// ImageMagick command line
457
			if (tp_create_im_cmdline_thumbnails($this, $prefix, $filename) != true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison !== instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
458
				trigger_error('Tidypics warning: failed to create thumbnails - ImageMagick command line', E_USER_WARNING);
459
			}
460
		} else if ($imageLib == 'ImageMagickPHP') {
461
			// imagick php extension
462
			if (tp_create_imagick_thumbnails($this, $prefix, $filename) != true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison !== instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
463
				trigger_error('Tidypics warning: failed to create thumbnails - ImageMagick PHP', E_USER_WARNING);
464
			}
465
		} else {
466
			if (tp_create_gd_thumbnails($this, $prefix, $filename) != true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison !== instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
467
				trigger_error('Tidypics warning: failed to create thumbnails - GD', E_USER_WARNING);
468
			}
469
		}
470
	}
471
472
	/**
473
	 * Get the image data of a thumbnail
474
	 *
475
	 * @param string $size
476
	 * @return string
477
	 */
478
	public function getThumbnail($size) {
479
		switch ($size) {
480
			case 'thumb':
481
				$thumb = $this->thumbnail;
482
				break;
483
			case 'small':
484
				$thumb = $this->smallthumb;
485
				break;
486
			case 'large':
487
				$thumb = $this->largethumb;
488
				break;
489
			default:
490
				return '';
491
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
492
		}
493
494
		if (!$thumb) {
495
			return '';
496
		}
497
498
		$file = new ElggFile();
499
		$file->owner_guid = $this->getOwnerGUID();
500
		$file->setFilename($thumb);
501
		return $file->grabFile();
502
	}
503
504
	public function getImage() {
505
		return $this->grabFile();
506
	}
507
508
	/**
509
	 * Extract EXIF Data from image
510
	 *
511
	 * @warning image file must be saved first
512
	 */
513
	public function extractExifData() {
514
		elgg_load_library('tidypics:exif');
515
		td_get_exif($this);
516
	}
517
518
	/**
519
	 * Has the photo been tagged with "in this photo" tags
520
	 *
521
	 * @return true/false
0 ignored issues
show
Documentation introduced by
The doc-type true/false could not be parsed: Unknown type name "true/false" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
522
	 */
523
	public function isPhotoTagged() {
524
		$num_tags = elgg_get_annotations(array('guid' => $this->getGUID(), 'type' => 'object', 'subtype' => 'image', 'annotation_name' => 'phototag', 'count' => true));
525
		if ($num_tags > 0) {
526
			return true;
527
		} else {
528
			return false;
529
		}
530
	}
531
532
	/**
533
	 * Get an array of photo tag information
534
	 *
535
	 * @return array
536
	 */
537
	public function getPhotoTags() {
538
539
		$tags = array();
540
		$annotations = elgg_get_annotations(array(
541
			'guid' => $this->getGUID(),
542
			'annotation_name' => 'phototag',
543
		));
544
		foreach ($annotations as $annotation) {
545
			$tag = unserialize($annotation->value);
546
			$tag->annotation_id = $annotation->id;
547
			$tags[] = $tag;
548
		}
549
550
		return $tags;
551
	}
552
553
	/**
554
	 * Remove thumbnails - usually in preparation for deletion
555
	 *
556
	 * The thumbnails are not actually ElggObjects so we create
557
	 * temporary objects to delete them.
558
	 */
559
	protected function removeThumbnails() {
560
		$thumbnail = $this->thumbnail;
561
		$smallthumb = $this->smallthumb;
562
		$largethumb = $this->largethumb;
563
564
		//delete standard thumbnail image
565 View Code Duplication
		if ($thumbnail) {
566
			$delfile = new ElggFile();
567
			$delfile->owner_guid = $this->getOwnerGUID();
568
			$delfile->setFilename($thumbnail);
569
			$delfile->delete();
570
		}
571
		//delete small thumbnail image
572 View Code Duplication
		if ($smallthumb) {
573
			$delfile = new ElggFile();
574
			$delfile->owner_guid = $this->getOwnerGUID();
575
			$delfile->setFilename($smallthumb);
576
			$delfile->delete();
577
		}
578
		//delete large thumbnail image
579 View Code Duplication
		if ($largethumb) {
580
			$delfile = new ElggFile();
581
			$delfile->owner_guid = $this->getOwnerGUID();
582
			$delfile->setFilename($largethumb);
583
			$delfile->delete();
584
		}
585
	}
586
}
587