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 if (!is_readable($FileName))
702 # if image is invalid or unsupported type 703 $SrcImage =
new Image($FileName);
704 $this->
Format = $SrcImage->Type();
705 if ($SrcImage->Status() !=
AI_OKAY)
708 $this->ErrorStatus = $SrcImage->Status();
712 # generate new image ID 713 $this->
Id = $this->GenerateNewImageId();
715 # generate and set file names 716 $this->SetFileNames();
718 # if our image file name differs from file name passed in 719 if (realpath($this->FileName) != realpath($FileName))
722 $SrcImage->SaveAs($this->FileName);
724 # if create failed set error status and bail out 725 if ($SrcImage->Status() !=
AI_OKAY)
727 $this->DB->Query(
"DELETE FROM Images WHERE ImageId = " 729 $this->ErrorStatus = $SrcImage->Status();
734 # scale the original image if necessary 735 $MaxWidth = min($MaxWidth, $SrcImage->XSize());
736 $MaxHeight = min($MaxHeight, $SrcImage->YSize());
738 # change the minimum width if the height is the limiting factor 739 if ($SrcImage->YSize() * $MaxWidth / $SrcImage->XSize() > $MaxHeight)
742 $SrcImage->XSize() * $MaxHeight / $SrcImage->YSize());
745 # change the minimum height since the width is the limiting factor 749 $SrcImage->YSize() * $MaxWidth / $SrcImage->XSize());
753 $SrcImage->ScaleTo($MaxWidth, $MaxHeight, TRUE);
755 # save and reload image info 756 $SrcImage->SaveAs($this->FileName);
757 $SrcImage =
new Image($this->FileName);
759 # retrieve image width and height 760 $this->
Height = $SrcImage->YSize();
761 $this->
Width = $SrcImage->XSize();
763 # create the preview and thumbnail images 764 foreach (array(
"Preview",
"Thumbnail") as $ImageType)
766 # variable name strings to use in the variable variables below 767 $MaxWidthVar =
"Max".$ImageType.
"Width";
768 $MaxHeightVar =
"Max".$ImageType.
"Height";
770 # find the mininum values for the width and height 771 $$MaxWidthVar = min($$MaxWidthVar, $this->
Width);
772 $$MaxHeightVar= min($$MaxHeightVar, $this->
Height);
774 # change the minimum width if the height is the limiting factor 775 if ($this->
Height * $$MaxWidthVar / $this->
Width > $$MaxHeightVar)
778 round($this->
Width * $$MaxHeightVar / $this->
Height);
781 # change the minimum height since the width is the limiting factor 785 round($this->
Height * $$MaxWidthVar / $this->
Width);
788 # scale the image and save it to a new file 789 $SrcImage->ScaleTo($$MaxWidthVar, $$MaxHeightVar, TRUE);
790 $SrcImage->SaveAs($this->{$ImageType.
"FileName"});
792 # scaling/saving failed 793 if ($SrcImage->Status() !=
AI_OKAY)
795 $this->DB->Query(
"DELETE FROM Images WHERE ImageId = " 797 $this->ErrorStatus = $SrcImage->Status();
801 # save the dimensions 802 $this->{$ImageType.
"Width"} = $$MaxWidthVar;
803 $this->{$ImageType.
"Height"} = $$MaxHeightVar;
806 # save image attributes to database 807 $this->SaveImageInfo();
816 private function LoadImageInfo($ImageId)
819 $this->
Id = $ImageId;
821 # load image record from database 822 $this->DB->Query(
"SELECT * FROM Images WHERE ImageId = ".$ImageId);
824 # if the ID is invalid 825 if (!$this->DB->NumRowsSelected())
831 $Record = $this->DB->FetchRow();
833 # load in values from record 834 $this->
Format = $Record[
"Format"];
835 $this->
AltText = $Record[
"AltText"];
837 $this->
Height = $Record[
"Height"];
838 $this->
Width = $Record[
"Width"];
844 # generate file names 845 $this->SetFileNames();
852 private function CreateCopyOfImage($SrcImage)
854 $Image =
new Image($SrcImage->Url());
855 if ($Image->Status() !=
AI_OKAY)
858 $this->ErrorStatus = $Image->
Status();
862 # generate new image ID 863 $this->
Id = $this->GenerateNewImageId();
865 # generate file names 866 $this->SetFileNames();
868 # copy attributes from source image 869 $this->
Format = $SrcImage->Format();
870 $this->
AltText = $SrcImage->AltText();
871 $this->
Width = $SrcImage->Width();
872 $this->
Height = $SrcImage->Height();
878 # copy source image files 879 copy($SrcImage->Url(), $this->FileName);
880 copy($SrcImage->PreviewUrl(), $this->PreviewFileName);
881 copy($SrcImage->ThumbnailUrl(), $this->ThumbnailFileName);
883 # save image attributes to database 884 $this->SaveImageInfo();
891 private function SetFileNames()
902 $this->FileName = $this->DetermineFileName(
903 self::$ImageStorageLocations,
"Img--", $FileExtension);
904 $this->PreviewFileName = $this->DetermineFileName(
905 self::$PreviewStorageLocations,
"Preview--", $FileExtension);
906 $this->ThumbnailFileName = $this->DetermineFileName(
907 self::$ThumbnailStorageLocations,
"Thumb--", $FileExtension);
919 private function DetermineFileName($Locations, $Prefix, $Extension)
921 # build base name for file 922 $BaseName = $Prefix.sprintf(
"%08d.", $this->
Id).$Extension;
924 # for each possible location 925 foreach ($Locations as $Dir)
927 # build full file name for location 928 $FileName = $Dir.
"/".$BaseName;
930 # if file exists in location return full file name 931 if (file_exists($FileName)) {
return $FileName; }
934 # for each possible location 935 foreach ($Locations as $Dir)
937 # build full file name for location 938 $FileName = $Dir.
"/".$BaseName;
940 # if location is writable return full file name 941 if (is_dir($Dir) && is_writable($Dir)) {
return $FileName; }
944 # return full file name for default location 945 return $Locations[0].
"/".$BaseName;
952 private function GenerateNewImageId()
954 # add new entry to database 955 $this->DB->Query(
"INSERT INTO Images (AltText) VALUES ('')");
957 # return ID of inserted image 958 return $this->DB->LastInsertId();
964 private function SaveImageInfo()
966 # update existing image record 967 $this->DB->Query(
"UPDATE Images SET" 968 .
" Format = '" .$this->
Format.
"'," 969 .
" AltText = '" .addslashes($this->AltText).
"'," 970 .
" Height = '" .$this->Height.
"'," 971 .
" Width = '" .$this->Width.
"'," 972 .
" PreviewHeight = '" .$this->PreviewHeight.
"'," 973 .
" PreviewWidth = '" .$this->PreviewWidth.
"'," 975 .
" ThumbnailWidth = '" .$this->ThumbnailWidth.
"'" 976 .
" 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)