5 # Part of the Collection Workflow Integration System (CWIS) 6 # Copyright 2002-2013 Edward Almasy and Internet Scout Research Group 7 # http://scout.wisc.edu/cwis/ 16 # ---- PUBLIC INTERFACE -------------------------------------------------- 38 $MaxWidth = NULL, $MaxHeight = NULL,
39 $MaxPreviewWidth = NULL, $MaxPreviewHeight = NULL,
40 $MaxThumbnailWidth = NULL, $MaxThumbnailHeight = NULL)
42 # clear error status (0 = AI_OKAY) 43 $this->ErrorStatus = 0;
45 # trigger the Image class file to be autoloaded since some parts of this 46 # class (SPTImage) use constants defined in it but don't construct Image 50 # create and save a database handle for our use 53 # if image object was passed in 54 if (is_object($ImageIdOrFileNameOrImageObj)
55 && method_exists($ImageIdOrFileNameOrImageObj,
"SPTImage"))
57 # create copy of image passed in 58 $this->CreateCopyOfImage($ImageIdOrFileNameOrImageObj);
60 # else if image ID was passed in 61 elseif (($ImageIdOrFileNameOrImageObj > 0)
62 && preg_match(
"/[0-9]+/", $ImageIdOrFileNameOrImageObj))
64 # load info on existing image 65 $this->LoadImageInfo($ImageIdOrFileNameOrImageObj);
67 # else assume that value passed in is file name 70 # create new image from named file 71 $this->CreateNewImage($ImageIdOrFileNameOrImageObj,
72 $MaxWidth, $MaxHeight,
73 $MaxPreviewWidth, $MaxPreviewHeight,
74 $MaxThumbnailWidth, $MaxThumbnailHeight);
93 $Url = $this->FileName;
94 $SignalResult = $GLOBALS[
"AF"]->SignalEvent(
95 "EVENT_IMAGE_URL_FILTER", array(
97 "ImageSize" =>
"Full"));
98 $Url = $SignalResult[
"Url"];
108 $Url = $this->PreviewFileName;
109 $SignalResult = $GLOBALS[
"AF"]->SignalEvent(
110 "EVENT_IMAGE_URL_FILTER", array(
112 "ImageSize" =>
"Preview"));
113 $Url = $SignalResult[
"Url"];
123 $Url = $this->ThumbnailFileName;
124 $SignalResult = $GLOBALS[
"AF"]->SignalEvent(
125 "EVENT_IMAGE_URL_FILTER", array(
127 "ImageSize" =>
"Thumbnail"));
128 $Url = $SignalResult[
"Url"];
139 return $this->Format;
148 $Image =
new Image($this->FileName);
149 return $Image->Mimetype();
158 return $this->Height;
176 return $this->PreviewHeight;
185 return $this->PreviewWidth;
194 return $this->ThumbnailHeight;
203 return $this->ThumbnailWidth;
212 # for each possible storage location 213 foreach (self::$ImageStorageLocations as $Dir)
218 # return location to caller 223 # return default (most preferred) location to caller 224 return self::$ImageStorageLocations[0];
233 # for each possible storage location 234 foreach (self::$PreviewStorageLocations as $Dir)
239 # return location to caller 244 # return default (most preferred) location to caller 245 return self::$PreviewStorageLocations[0];
254 # for each possible storage location 255 foreach (self::$ThumbnailStorageLocations as $Dir)
260 # return location to caller 265 # return default (most preferred) location to caller 266 return self::$ThumbnailStorageLocations[0];
275 return $this->FileName;
286 # if new value supplied and new value differs from existing value 287 if (($NewValue !== NULL) && ($NewValue != $this->
AltText))
289 # save new value to database 290 $this->DB->Query(
"UPDATE Images SET" 291 .
" AltText = '".addslashes($NewValue).
"'" 292 .
" WHERE ImageId = ".$this->
Id);
294 # save new value locally 298 # return attribute value to caller 299 return $this->AltText;
309 if (($NewValue !== NULL) && ($NewValue != $this->
LinkTarget))
311 $this->DB->Query(
"UPDATE Images SET" 312 .
" LinkTarget = '".addslashes($NewValue).
"'" 313 .
" WHERE ImageId = ".$this->
Id);
317 return $this->LinkTarget;
326 # delete base image file 327 if (file_exists($this->FileName)) { unlink($this->FileName); }
329 # delete preview image file 330 if (file_exists($this->PreviewFileName)) { unlink($this->PreviewFileName); }
332 # delete thumbnail image file 333 if (file_exists($this->ThumbnailFileName)) { unlink($this->ThumbnailFileName); }
335 # delete image info record in database 336 $this->DB->Query(
"DELETE FROM Images WHERE ImageId = ".$this->
Id);
345 return $this->ErrorStatus;
356 $ImagePath = self::ImageStorageDirectory();
357 $PreviewPath = self::PreviewStorageDirectory();
358 $ThumbnailPath = self::ThumbnailStorageDirectory();
360 # assume everything will be okay 363 # check base image directory 364 if (!is_dir($ImagePath) || !is_writable($ImagePath))
366 if (!is_dir($ImagePath))
368 @mkdir($ImagePath, 0755);
372 @chmod($ImagePath, 0755);
374 if (!is_dir($ImagePath))
376 $ErrorsFound[] =
"Image Storage Directory Not Found";
378 elseif (!is_writable($ImagePath))
380 $ErrorsFound[] =
"Image Storage Directory Not Writable";
384 # check preview directory 385 if (!is_dir($PreviewPath) || !is_writable($PreviewPath))
387 if (!is_dir($PreviewPath))
389 @mkdir($PreviewPath, 0755);
393 @chmod($PreviewPath, 0755);
395 if (!is_dir($PreviewPath))
397 $ErrorsFound[] =
"Preview Storage Directory Not Found";
399 elseif (!is_writable($PreviewPath))
401 $ErrorsFound[] =
"Preview Storage Directory Not Writable";
405 # check thumbnail directory 406 if (!is_dir($ThumbnailPath) || !is_writable($ThumbnailPath))
408 if (!is_dir($ThumbnailPath))
410 @mkdir($ThumbnailPath, 0755);
414 @chmod($ThumbnailPath, 0755);
416 if (!is_dir($ThumbnailPath))
418 $ErrorsFound[] =
"Thumbnail Storage Directory Not Found";
420 elseif (!is_writable($ThumbnailPath))
422 $ErrorsFound[] =
"Thumbnail Storage Directory Not Writable";
426 # return any errors found to caller 440 public function Resize($MaxWidth, $MaxHeight,
441 $MaxPreviewWidth, $MaxPreviewHeight,
442 $MaxThumbnailWidth, $MaxThumbnailHeight)
444 $SrcImage =
new Image($this->FileName);
446 # scale the original image if necessary 447 $MaxWidth = min($MaxWidth, $SrcImage->XSize());
448 $MaxHeight = min($MaxHeight, $SrcImage->YSize());
449 $SrcImage->ScaleTo($MaxWidth, $MaxHeight, TRUE);
451 # save and reload image info 452 $SrcImage->SaveAs($this->FileName);
453 $SrcImage =
new Image($this->FileName);
455 # retrieve image width and height 456 $this->
Height = $SrcImage->YSize();
457 $this->
Width = $SrcImage->XSize();
459 # generate preview image and calculate width and height 460 $MaxPreviewWidth = min($MaxPreviewWidth, $this->
Width);
461 $MaxPreviewHeight = min($MaxPreviewHeight, $this->
Height);
462 $SrcImage->ScaleTo($MaxPreviewWidth, $MaxPreviewHeight, TRUE);
463 $SrcImage->SaveAs($this->PreviewFileName);
464 if (($this->
Width * $MaxPreviewHeight)
465 > ($this->
Height * $MaxPreviewWidth))
469 ($MaxPreviewWidth * $SrcImage->YSize()) / $SrcImage->XSize();
474 ($MaxPreviewHeight * $SrcImage->XSize()) / $SrcImage->YSize();
478 # generate thumbnail image and calculate width and height 479 $MaxThumbnailWidth = min($MaxThumbnailWidth, $this->
Width);
480 $MaxThumbnailHeight = min($MaxThumbnailHeight, $this->
Height);
481 $SrcImage->ScaleTo($MaxThumbnailWidth, $MaxThumbnailHeight, TRUE);
482 $SrcImage->SaveAs($this->ThumbnailFileName);
483 if (($this->
Width * $MaxThumbnailHeight)
484 > ($this->
Height * $MaxThumbnailWidth))
488 ($MaxThumbnailWidth * $SrcImage->YSize()) / $SrcImage->XSize();
493 ($MaxThumbnailHeight * $SrcImage->XSize()) / $SrcImage->YSize();
497 # save image attributes to database 498 $this->SaveImageInfo();
512 "Attempt to get ImageLinks at %FILE%:%LINE%." 513 .
" (ImageLinks many only be fetched by Resource.)");
517 case self::SIZE_FULL:
520 case self::SIZE_PREVIEW:
523 case self::SIZE_THUMBNAIL:
527 throw new Exception(
"Unknown image size requested");
531 "t" => $this->ThumbnailFileName,
532 "p" => $this->PreviewFileName,
533 "f" => $this->FileName);
535 # default to returning the path to the file 536 $ReturnValue = $Files[$Suffix];
538 # but if we have CleanURL support 539 if ($GLOBALS[
"AF"]->HtaccessSupport())
541 # make sure our ImageLinks dir exists 542 if (!is_dir(self::CACHE_PATH))
544 mkdir(self::CACHE_PATH);
547 # create symlinks for this field if they don't already exist 548 $BaseDir = dirname(__DIR__).
"/";
549 foreach ($Files as $FileSuffix => $SrcFile)
552 implode(
"_", array($ResourceId, $FieldId, $Index, $FileSuffix))
555 if (!file_exists(self::CACHE_PATH.
"/".$LinkName))
557 symlink($BaseDir.$SrcFile, self::CACHE_PATH.
"/".$LinkName);
560 # if this is the requested symlink, generate a url to return 561 if ($Suffix == $FileSuffix)
563 $ReturnValue = OurBaseUrl().
"viewimage/".$LinkName;
580 "Attempt to delete ImageLinks of a resource at %FILE%:%LINE%." 581 .
" (ImageLinks many only be cleared by Resource.)");
583 if (!is_dir(self::CACHE_PATH))
588 $Files = glob(self::CACHE_PATH.
"/".$ResourceId.
"_".$FieldId.
"_*");
589 foreach ($Files as $file)
591 if (file_exists($File) && is_link($File))
603 if (!is_dir(self::CACHE_PATH))
608 foreach (scandir(self::CACHE_PATH) as $Entry)
610 $Link = self::CACHE_PATH.
"/".$Entry;
623 if (!is_dir(self::CACHE_PATH))
628 foreach (scandir(self::CACHE_PATH) as $Entry)
630 $Link = self::CACHE_PATH.
"/".$Entry;
633 $link_info = lstat($Link);
635 if (time() - $link_info[
'ctime'] > self::$SymlinkExpirationTime)
643 # ---- PRIVATE INTERFACE ------------------------------------------------- 647 private $PreviewFileName;
648 private $ThumbnailFileName;
654 private $ThumbnailUrl;
657 private $PreviewHeight;
658 private $PreviewWidth;
659 private $ThumbnailHeight;
660 private $ThumbnailWidth;
662 private $ErrorStatus;
664 static private $SymlinkExpirationTime = 86400;
667 static private $ImageStorageLocations = array(
671 static private $PreviewStorageLocations = array(
672 "local/data/images/previews",
673 "ImageStorage/Previews",
675 static private $ThumbnailStorageLocations = array(
676 "local/data/images/thumbnails",
677 "ImageStorage/Thumbnails",
691 private function CreateNewImage($FileName, $MaxWidth, $MaxHeight,
692 $MaxPreviewWidth, $MaxPreviewHeight, $MaxThumbnailWidth, $MaxThumbnailHeight)
694 # if file does not exist or is not readable 695 $IsReadable = @is_readable($FileName);
696 if ($IsReadable !== TRUE)
703 # if image is invalid or unsupported type 704 $SrcImage =
new Image($FileName);
705 $this->
Format = $SrcImage->Type();
706 if ($SrcImage->Status() !=
AI_OKAY)
709 $this->ErrorStatus = $SrcImage->Status();
713 # generate new image ID 714 $this->
Id = $this->GenerateNewImageId();
716 # generate and set file names 717 $this->SetFileNames();
719 # if our image file name differs from file name passed in 720 if (realpath($this->FileName) != realpath($FileName))
723 $SrcImage->SaveAs($this->FileName);
725 # if create failed set error status and bail out 726 if ($SrcImage->Status() !=
AI_OKAY)
728 $this->DB->Query(
"DELETE FROM Images WHERE ImageId = " 730 $this->ErrorStatus = $SrcImage->Status();
735 # scale the original image if necessary 736 $MaxWidth = min($MaxWidth, $SrcImage->XSize());
737 $MaxHeight = min($MaxHeight, $SrcImage->YSize());
739 # change the minimum width if the height is the limiting factor 740 if ($SrcImage->YSize() * $MaxWidth / $SrcImage->XSize() > $MaxHeight)
743 $SrcImage->XSize() * $MaxHeight / $SrcImage->YSize());
746 # change the minimum height since the width is the limiting factor 750 $SrcImage->YSize() * $MaxWidth / $SrcImage->XSize());
754 $SrcImage->ScaleTo($MaxWidth, $MaxHeight, TRUE);
756 # save and reload image info 757 $SrcImage->SaveAs($this->FileName);
758 $SrcImage =
new Image($this->FileName);
760 # retrieve image width and height 761 $this->
Height = $SrcImage->YSize();
762 $this->
Width = $SrcImage->XSize();
764 # create the preview and thumbnail images 765 foreach (array(
"Preview",
"Thumbnail") as $ImageType)
767 # variable name strings to use in the variable variables below 768 $MaxWidthVar =
"Max".$ImageType.
"Width";
769 $MaxHeightVar =
"Max".$ImageType.
"Height";
771 # find the mininum values for the width and height 772 $$MaxWidthVar = min($$MaxWidthVar, $this->
Width);
773 $$MaxHeightVar= min($$MaxHeightVar, $this->
Height);
775 # change the minimum width if the height is the limiting factor 776 if ($this->
Height * $$MaxWidthVar / $this->
Width > $$MaxHeightVar)
779 round($this->
Width * $$MaxHeightVar / $this->
Height);
782 # change the minimum height since the width is the limiting factor 786 round($this->
Height * $$MaxWidthVar / $this->
Width);
789 # scale the image and save it to a new file 790 $SrcImage->ScaleTo($$MaxWidthVar, $$MaxHeightVar, TRUE);
791 $SrcImage->SaveAs($this->{$ImageType.
"FileName"});
793 # scaling/saving failed 794 if ($SrcImage->Status() !=
AI_OKAY)
796 $this->DB->Query(
"DELETE FROM Images WHERE ImageId = " 798 $this->ErrorStatus = $SrcImage->Status();
802 # save the dimensions 803 $this->{$ImageType.
"Width"} = $$MaxWidthVar;
804 $this->{$ImageType.
"Height"} = $$MaxHeightVar;
807 # save image attributes to database 808 $this->SaveImageInfo();
817 private function LoadImageInfo($ImageId)
820 $this->
Id = $ImageId;
822 # load image record from database 823 $this->DB->Query(
"SELECT * FROM Images WHERE ImageId = ".$ImageId);
825 # if the ID is invalid 826 if (!$this->DB->NumRowsSelected())
832 $Record = $this->DB->FetchRow();
834 # load in values from record 835 $this->
Format = $Record[
"Format"];
836 $this->
AltText = $Record[
"AltText"];
838 $this->
Height = $Record[
"Height"];
839 $this->
Width = $Record[
"Width"];
845 # generate file names 846 $this->SetFileNames();
853 private function CreateCopyOfImage($SrcImage)
855 $Image =
new Image($SrcImage->Url());
856 if ($Image->Status() !=
AI_OKAY)
859 $this->ErrorStatus = $Image->
Status();
863 # generate new image ID 864 $this->
Id = $this->GenerateNewImageId();
866 # generate file names 867 $this->SetFileNames();
869 # copy attributes from source image 870 $this->
Format = $SrcImage->Format();
871 $this->
AltText = $SrcImage->AltText();
872 $this->
Width = $SrcImage->Width();
873 $this->
Height = $SrcImage->Height();
879 # copy source image files 880 copy($SrcImage->Url(), $this->FileName);
881 copy($SrcImage->PreviewUrl(), $this->PreviewFileName);
882 copy($SrcImage->ThumbnailUrl(), $this->ThumbnailFileName);
884 # save image attributes to database 885 $this->SaveImageInfo();
892 private function SetFileNames()
903 $this->FileName = $this->DetermineFileName(
904 self::$ImageStorageLocations,
"Img--", $FileExtension);
905 $this->PreviewFileName = $this->DetermineFileName(
906 self::$PreviewStorageLocations,
"Preview--", $FileExtension);
907 $this->ThumbnailFileName = $this->DetermineFileName(
908 self::$ThumbnailStorageLocations,
"Thumb--", $FileExtension);
920 private function DetermineFileName($Locations, $Prefix, $Extension)
922 # build base name for file 923 $BaseName = $Prefix.sprintf(
"%08d.", $this->
Id).$Extension;
925 # for each possible location 926 foreach ($Locations as $Dir)
928 # build full file name for location 929 $FileName = $Dir.
"/".$BaseName;
931 # if file exists in location return full file name 932 if (file_exists($FileName)) {
return $FileName; }
935 # for each possible location 936 foreach ($Locations as $Dir)
938 # build full file name for location 939 $FileName = $Dir.
"/".$BaseName;
941 # if location is writable return full file name 942 if (is_dir($Dir) && is_writable($Dir)) {
return $FileName; }
945 # return full file name for default location 946 return $Locations[0].
"/".$BaseName;
953 private function GenerateNewImageId()
955 # add new entry to database 956 $this->DB->Query(
"INSERT INTO Images (AltText) VALUES ('')");
958 # return ID of inserted image 959 return $this->DB->LastInsertId();
965 private function SaveImageInfo()
967 # update existing image record 968 $this->DB->Query(
"UPDATE Images SET" 969 .
" Format = '" .$this->
Format.
"'," 970 .
" AltText = '" .addslashes($this->AltText).
"'," 971 .
" Height = '" .$this->Height.
"'," 972 .
" Width = '" .$this->Width.
"'," 973 .
" PreviewHeight = '" .$this->PreviewHeight.
"'," 974 .
" PreviewWidth = '" .$this->PreviewWidth.
"'," 976 .
" ThumbnailWidth = '" .$this->ThumbnailWidth.
"'" 977 .
" WHERE ImageId = ".$this->Id);
Mimetype()
Get the MIME type for the image.
__construct($ImageIdOrFileNameOrImageObj, $MaxWidth=NULL, $MaxHeight=NULL, $MaxPreviewWidth=NULL, $MaxPreviewHeight=NULL, $MaxThumbnailWidth=NULL, $MaxThumbnailHeight=NULL)
Object constructor.
LinkTarget($NewValue=NULL)
Get or set the link target value for the image.
static CheckMyCaller($DesiredCaller, $ExceptionMsg=NULL)
Check the caller of the current function.
ThumbnailWidth()
Get the width of the thumbnail image for this image.
SQL database abstraction object with smart query caching.
Delete()
Delete the image, that is, remove its record from the database and delete the associated image files ...
PreviewHeight()
Get the height of the preview image for this image.
PreviewWidth()
Get the width of the preview image for this image.
Status()
Get the error status set by the constructor.
static CheckDirectories()
Check that the image storage directories are available, creating them and attempting to change their ...
const CACHE_PATH
path containg the symlinks mapping images/fields to resources
ThumbnailHeight()
Get the height of the thumbnail image for this image.
Width()
Get the width of the image.
Encapsulates a full-size, preview, and thumbnail image.
Url()
Get the path to the image.
static PreviewStorageDirectory()
Get the path to the preview image storage directory.
PreviewUrl()
Get the path to the preview image for this image.
Format()
Get the format of the image.
GetLink()
Get the path to the full-size image.
Id()
Get the ID of the image in the database.
Height()
Get the height of the image.
Resize($MaxWidth, $MaxHeight, $MaxPreviewWidth, $MaxPreviewHeight, $MaxThumbnailWidth, $MaxThumbnailHeight)
Resize the full-size, preview, and thumbnail images based on the given dimension restrictions.
GetImageUrlForResource($ResourceId, $FieldId, $Index, $Size)
Get the URL pointing to an image.
static ExpireImageSymlinks()
Expire old symlinks used for cached image mappings.
static ClearImageSymlinksForResource($ResourceId, $FieldId)
Remove symlinks used for to cache image mappings.
static ClearImageSymlinks()
Remove all symlinks used for a cached image mapping.
AltText($NewValue=NULL)
Get or set the alternate text value for the image.
static ThumbnailStorageDirectory()
Get the path to the thumbnail image storage directory.
static ImageStorageDirectory()
Get the path to the (full-size) image storage directory.
ThumbnailUrl()
Get the path to the thumbnail image for this image.
static Extension($Type=NULL)