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)
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)
649 $this->LoadResourceAndFieldIds();
650 return $this->ResourceId;
659 $this->LoadResourceAndFieldIds();
660 return $this->FieldId;
664 # ---- PRIVATE INTERFACE ------------------------------------------------- 668 private $PreviewFileName;
669 private $ThumbnailFileName;
675 private $ThumbnailUrl;
678 private $PreviewHeight;
679 private $PreviewWidth;
682 private $ThumbnailHeight;
683 private $ThumbnailWidth;
685 private $ErrorStatus;
687 static private $SymlinkExpirationTime = 86400;
690 static private $ImageStorageLocations = array(
694 static private $PreviewStorageLocations = array(
695 "local/data/images/previews",
696 "ImageStorage/Previews",
698 static private $ThumbnailStorageLocations = array(
699 "local/data/images/thumbnails",
700 "ImageStorage/Thumbnails",
714 private function CreateNewImage($FileName, $MaxWidth, $MaxHeight,
715 $MaxPreviewWidth, $MaxPreviewHeight, $MaxThumbnailWidth, $MaxThumbnailHeight)
717 # if file does not exist or is not readable 718 $IsReadable = @is_readable($FileName);
719 if ($IsReadable !== TRUE)
726 # if image is invalid or unsupported type 727 $SrcImage =
new Image($FileName);
728 $this->
Format = $SrcImage->Type();
729 if ($SrcImage->Status() !=
AI_OKAY)
732 $this->ErrorStatus = $SrcImage->Status();
736 # generate new image ID 737 $this->
Id = $this->GenerateNewImageId();
739 # generate and set file names 740 $this->SetFileNames();
742 # if our image file name differs from file name passed in 743 if (realpath($this->FileName) != realpath($FileName))
746 $SrcImage->SaveAs($this->FileName);
748 # if create failed set error status and bail out 749 if ($SrcImage->Status() !=
AI_OKAY)
751 $this->DB->Query(
"DELETE FROM Images WHERE ImageId = " 753 $this->ErrorStatus = $SrcImage->Status();
758 # scale the original image if necessary 759 $MaxWidth = min($MaxWidth, $SrcImage->XSize());
760 $MaxHeight = min($MaxHeight, $SrcImage->YSize());
762 # change the minimum width if the height is the limiting factor 763 if ($SrcImage->YSize() * $MaxWidth / $SrcImage->XSize() > $MaxHeight)
766 $SrcImage->XSize() * $MaxHeight / $SrcImage->YSize());
769 # change the minimum height since the width is the limiting factor 773 $SrcImage->YSize() * $MaxWidth / $SrcImage->XSize());
777 $SrcImage->ScaleTo($MaxWidth, $MaxHeight, TRUE);
779 # save and reload image info 780 $SrcImage->SaveAs($this->FileName);
781 $SrcImage =
new Image($this->FileName);
783 # retrieve image width and height 784 $this->
Height = $SrcImage->YSize();
785 $this->
Width = $SrcImage->XSize();
787 # create the preview and thumbnail images 788 foreach (array(
"Preview",
"Thumbnail") as $ImageType)
790 # variable name strings to use in the variable variables below 791 $MaxWidthVar =
"Max".$ImageType.
"Width";
792 $MaxHeightVar =
"Max".$ImageType.
"Height";
794 # find the mininum values for the width and height 795 $$MaxWidthVar = min($$MaxWidthVar, $this->
Width);
796 $$MaxHeightVar= min($$MaxHeightVar, $this->
Height);
798 # change the minimum width if the height is the limiting factor 799 if ($this->
Height * $$MaxWidthVar / $this->
Width > $$MaxHeightVar)
802 round($this->
Width * $$MaxHeightVar / $this->
Height);
805 # change the minimum height since the width is the limiting factor 809 round($this->
Height * $$MaxWidthVar / $this->
Width);
812 # scale the image and save it to a new file 813 $SrcImage->ScaleTo($$MaxWidthVar, $$MaxHeightVar, TRUE);
814 $SrcImage->SaveAs($this->{$ImageType.
"FileName"});
816 # scaling/saving failed 817 if ($SrcImage->Status() !=
AI_OKAY)
819 $this->DB->Query(
"DELETE FROM Images WHERE ImageId = " 821 $this->ErrorStatus = $SrcImage->Status();
825 # save the dimensions 826 $this->{$ImageType.
"Width"} = $$MaxWidthVar;
827 $this->{$ImageType.
"Height"} = $$MaxHeightVar;
830 # save image attributes to database 831 $this->SaveImageInfo();
840 private function LoadImageInfo($ImageId)
843 $this->
Id = $ImageId;
845 # load image record from database 846 $this->DB->Query(
"SELECT * FROM Images WHERE ImageId = ".$ImageId);
848 # if the ID is invalid 849 if (!$this->DB->NumRowsSelected())
855 $Record = $this->DB->FetchRow();
857 # load in values from record 858 $this->
Format = $Record[
"Format"];
859 $this->
AltText = $Record[
"AltText"];
861 $this->
Height = $Record[
"Height"];
862 $this->
Width = $Record[
"Width"];
868 # generate file names 869 $this->SetFileNames();
876 private function CreateCopyOfImage($SrcImage)
878 $Image =
new Image($SrcImage->Url());
879 if ($Image->Status() !=
AI_OKAY)
882 $this->ErrorStatus = $Image->
Status();
886 # generate new image ID 887 $this->
Id = $this->GenerateNewImageId();
889 # generate file names 890 $this->SetFileNames();
892 # copy attributes from source image 893 $this->
Format = $SrcImage->Format();
894 $this->
AltText = $SrcImage->AltText();
895 $this->
Width = $SrcImage->Width();
896 $this->
Height = $SrcImage->Height();
902 # copy source image files 903 copy($SrcImage->Url(), $this->FileName);
904 copy($SrcImage->PreviewUrl(), $this->PreviewFileName);
905 copy($SrcImage->ThumbnailUrl(), $this->ThumbnailFileName);
907 # save image attributes to database 908 $this->SaveImageInfo();
915 private function SetFileNames()
926 $this->FileName = $this->DetermineFileName(
927 self::$ImageStorageLocations,
"Img--", $FileExtension);
928 $this->PreviewFileName = $this->DetermineFileName(
929 self::$PreviewStorageLocations,
"Preview--", $FileExtension);
930 $this->ThumbnailFileName = $this->DetermineFileName(
931 self::$ThumbnailStorageLocations,
"Thumb--", $FileExtension);
943 private function DetermineFileName($Locations, $Prefix, $Extension)
945 # build base name for file 946 $BaseName = $Prefix.sprintf(
"%08d.", $this->
Id).$Extension;
948 # for each possible location 949 foreach ($Locations as $Dir)
951 # build full file name for location 952 $FileName = $Dir.
"/".$BaseName;
954 # if file exists in location return full file name 955 if (file_exists($FileName)) {
return $FileName; }
958 # for each possible location 959 foreach ($Locations as $Dir)
961 # build full file name for location 962 $FileName = $Dir.
"/".$BaseName;
964 # if location is writable return full file name 965 if (is_dir($Dir) && is_writable($Dir)) {
return $FileName; }
968 # return full file name for default location 969 return $Locations[0].
"/".$BaseName;
976 private function GenerateNewImageId()
978 # add new entry to database 979 $this->DB->Query(
"INSERT INTO Images (AltText) VALUES ('')");
981 # return ID of inserted image 982 return $this->DB->LastInsertId();
988 private function SaveImageInfo()
990 # update existing image record 991 $this->DB->Query(
"UPDATE Images SET" 992 .
" Format = '" .$this->
Format.
"'," 993 .
" AltText = '" .addslashes($this->AltText).
"'," 994 .
" Height = '" .$this->Height.
"'," 995 .
" Width = '" .$this->Width.
"'," 996 .
" PreviewHeight = '" .$this->PreviewHeight.
"'," 997 .
" PreviewWidth = '" .$this->PreviewWidth.
"'," 999 .
" ThumbnailWidth = '" .$this->ThumbnailWidth.
"'" 1000 .
" WHERE ImageId = ".$this->Id);
1006 private function LoadResourceAndFieldIds()
1010 $this->DB->Query(
"SELECT ResourceId, FieldId FROM ResourceImageInts" 1011 .
" WHERE ImageId = '".addslashes($this->
Id).
"'");
1012 if ($this->DB->NumRowsSelected())
1014 $Row = $this->DB->FetchRow();
1016 $this->
FieldId = $Row[
"FieldId"];
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.
static BaseUrl()
Get current base URL (the part before index.php) (e.g.
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 ...
ResourceId()
Get ID of resource associated with image.
PreviewHeight()
Get the height of the preview image for this image.
FieldId()
Get ID of field associated with 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.
static ExtensionForType($Type)
return the file name extension for the image, given a type.
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.