00001 <?PHP
00002 #
00003 # FILE: SPTImage.php
00004 #
00005 # METHODS PROVIDED:
00006 # SPTImage($ImageIdOrFileNameOrImageObj,
00007 # $MaxPreviewWidth = NULL, $MaxPreviewHeight = NULL,
00008 # $MaxThumbnailWidth = NULL, $MaxThumbnailHeight = NULL)
00009 # - object constructor
00010 # Delete()
00011 # - delete image and associated files and data
00012 # AltText($NewValue = NULL)
00013 # - get/set alt text attribute
00014 # Id()
00015 # Url()
00016 # PreviewUrl()
00017 # ThumbnailUrl()
00018 # Format()
00019 # Height()
00020 # Width()
00021 # PreviewHeight()
00022 # PreviewWidth()
00023 # ThumbnailHeight()
00024 # ThumbnailWidth()
00025 # - get attributes
00026 #
00027 # AUTHOR: Edward Almasy
00028 #
00029 # Part of CWIS
00030 # Copyright 2002-2011 Internet Scout Project
00031 # http://scout.wisc.edu
00032 #
00033
00034 class SPTImage {
00035
00036 # ---- PUBLIC INTERFACE --------------------------------------------------
00037
00043 const IMAGE_PATH = "ImageStorage/";
00044 const PREVIEW_PATH = "ImageStorage/Previews/";
00045 const THUMBNAIL_PATH = "ImageStorage/Thumbnails/";
00046
00047 # object constructor
00048 function SPTImage($ImageIdOrFileNameOrImageObj,
00049 $MaxWidth = NULL, $MaxHeight = NULL,
00050 $MaxPreviewWidth = NULL, $MaxPreviewHeight = NULL,
00051 $MaxThumbnailWidth = NULL, $MaxThumbnailHeight = NULL)
00052 {
00053 # clear error status (0 = AI_OKAY)
00054 $this->ErrorStatus = 0;
00055
00056 # trigger the Image class file to be autoloaded since some parts of this
00057 # class (SPTImage) use constants defined in it but don't construct Image
00058 # objects
00059 new Image(NULL);
00060
00061 # create and save a database handle for our use
00062 $this->DB = new SPTDatabase();
00063
00064 # if image object was passed in
00065 if (is_object($ImageIdOrFileNameOrImageObj)
00066 && method_exists($ImageIdOrFileNameOrImageObj, "SPTImage"))
00067 {
00068 # create copy of image passed in
00069 $this->CreateCopyOfImage($ImageIdOrFileNameOrImageObj);
00070 }
00071 # else if image ID was passed in
00072 elseif (($ImageIdOrFileNameOrImageObj > 0)
00073 && preg_match("/[0-9]+/", $ImageIdOrFileNameOrImageObj))
00074 {
00075 # load info on existing image
00076 $this->LoadImageInfo($ImageIdOrFileNameOrImageObj);
00077 }
00078 # else assume that value passed in is file name
00079 else
00080 {
00081 # create new image from named file
00082 $this->CreateNewImage($ImageIdOrFileNameOrImageObj,
00083 $MaxWidth, $MaxHeight,
00084 $MaxPreviewWidth, $MaxPreviewHeight,
00085 $MaxThumbnailWidth, $MaxThumbnailHeight);
00086 }
00087 }
00088
00089 # get attributes
00090 function Id() { return $this->Id; }
00091 function Url() { return $this->FileName; }
00092 function PreviewUrl() { return $this->PreviewFileName; }
00093 function ThumbnailUrl() { return $this->ThumbnailFileName; }
00094 function Format() { return $this->Format; }
00095 function Height() { return $this->Height; }
00096 function Width() { return $this->Width; }
00097 function PreviewHeight() { return $this->PreviewHeight; }
00098 function PreviewWidth() { return $this->PreviewWidth; }
00099 function ThumbnailHeight() { return $this->ThumbnailHeight; }
00100 function ThumbnailWidth() { return $this->ThumbnailWidth; }
00101 function ImageStorageDirectory() { return self::IMAGE_PATH; }
00102 function PreviewStorageDirectory() { return self::PREVIEW_PATH; }
00103 function ThumbnailStorageDirectory() { return self::THUMBNAIL_PATH; }
00104 function GetLink() { return $this->FileName; }
00105
00106 # get/set attributes
00107 function AltText($NewValue = NULL)
00108 {
00109 # if new value supplied and new value differs from existing value
00110 if (($NewValue !== NULL) && ($NewValue != $this->AltText))
00111 {
00112 # save new value to database
00113 $this->DB->Query("UPDATE Images SET"
00114 ." AltText = '".addslashes($NewValue)."'"
00115 ." WHERE ImageId = ".$this->Id);
00116
00117 # save new value locally
00118 $this->AltText = $NewValue;
00119 }
00120
00121 # return attribute value to caller
00122 return $this->AltText;
00123 }
00124
00125 # delete image and associated files and data
00126 function Delete()
00127 {
00128 # delete base image file
00129 if (file_exists($this->FileName)) { unlink($this->FileName); }
00130
00131 # delete preview image file
00132 if (file_exists($this->PreviewFileName)) { unlink($this->PreviewFileName); }
00133
00134 # delete thumbnail image file
00135 if (file_exists($this->ThumbnailFileName)) { unlink($this->ThumbnailFileName); }
00136
00137 # delete image info record in database
00138 $this->DB->Query("DELETE FROM Images WHERE ImageId = ".$this->Id);
00139 }
00140
00141 # return error status set by the constructor
00142 function Status()
00143 {
00144 return $this->ErrorStatus;
00145 }
00146
00147 # check to make sure image storage directories are available
00148 # (returns array of error codes or NULL if no errors found)
00149 static function CheckDirectories()
00150 {
00151 # determine paths
00152 $ImagePath = self::IMAGE_PATH;
00153 $PreviewPath = self::PREVIEW_PATH;
00154 $ThumbnailPath = self::THUMBNAIL_PATH;
00155
00156 # assume everything will be okay
00157 $ErrorsFound = NULL;
00158
00159 # check base image directory
00160 if (!is_dir($ImagePath) || !is_writable($ImagePath))
00161 {
00162 if (!is_dir($ImagePath))
00163 {
00164 @mkdir($ImagePath, 0755);
00165 }
00166 else
00167 {
00168 @chmod($ImagePath, 0755);
00169 }
00170 if (!is_dir($ImagePath))
00171 {
00172 $ErrorsFound[] = "Image Storage Directory Not Found";
00173 }
00174 elseif (!is_writable($ImagePath))
00175 {
00176 $ErrorsFound[] = "Image Storage Directory Not Writable";
00177 }
00178 }
00179
00180 # check preview directory
00181 if (!is_dir($PreviewPath) || !is_writable($PreviewPath))
00182 {
00183 if (!is_dir($PreviewPath))
00184 {
00185 @mkdir($PreviewPath, 0755);
00186 }
00187 else
00188 {
00189 @chmod($PreviewPath, 0755);
00190 }
00191 if (!is_dir($PreviewPath))
00192 {
00193 $ErrorsFound[] = "Preview Storage Directory Not Found";
00194 }
00195 elseif (!is_writable($PreviewPath))
00196 {
00197 $ErrorsFound[] = "Preview Storage Directory Not Writable";
00198 }
00199 }
00200
00201 # check thumbnail directory
00202 if (!is_dir($ThumbnailPath) || !is_writable($ThumbnailPath))
00203 {
00204 if (!is_dir($ThumbnailPath))
00205 {
00206 @mkdir($ThumbnailPath, 0755);
00207 }
00208 else
00209 {
00210 @chmod($ThumbnailPath, 0755);
00211 }
00212 if (!is_dir($ThumbnailPath))
00213 {
00214 $ErrorsFound[] = "Thumbnail Storage Directory Not Found";
00215 }
00216 elseif (!is_writable($ThumbnailPath))
00217 {
00218 $ErrorsFound[] = "Thumbnail Storage Directory Not Writable";
00219 }
00220 }
00221
00222 # return any errors found to caller
00223 return $ErrorsFound;
00224 }
00225
00226 public function Resize($MaxWidth, $MaxHeight,
00227 $MaxPreviewWidth, $MaxPreviewHeight,
00228 $MaxThumbnailWidth, $MaxThumbnailHeight)
00229 {
00230 $SrcImage = new Image($this->FileName);
00231
00232 # scale the original image if necessary
00233 $MaxWidth = min($MaxWidth, $SrcImage->XSize());
00234 $MaxHeight = min($MaxHeight, $SrcImage->YSize());
00235 $SrcImage->ScaleTo($MaxWidth, $MaxHeight, TRUE);
00236
00237 # save and reload image info
00238 $SrcImage->SaveAs($this->FileName);
00239 $SrcImage = new Image($this->FileName);
00240
00241 # retrieve image width and height
00242 $this->Height = $SrcImage->YSize();
00243 $this->Width = $SrcImage->XSize();
00244
00245 # generate preview image and calculate width and height
00246 $MaxPreviewWidth = min($MaxPreviewWidth, $this->Width);
00247 $MaxPreviewHeight = min($MaxPreviewHeight, $this->Height);
00248 $SrcImage->ScaleTo($MaxPreviewWidth, $MaxPreviewHeight, TRUE);
00249 $SrcImage->SaveAs($this->PreviewFileName, IMGTYPE_JPEG);
00250 if (($this->Width * $MaxPreviewHeight)
00251 > ($this->Height * $MaxPreviewWidth))
00252 {
00253 $this->PreviewWidth = $MaxPreviewWidth;
00254 $this->PreviewHeight =
00255 ($MaxPreviewWidth * $SrcImage->YSize()) / $SrcImage->XSize();
00256 }
00257 else
00258 {
00259 $this->PreviewWidth =
00260 ($MaxPreviewHeight * $SrcImage->XSize()) / $SrcImage->YSize();
00261 $this->PreviewHeight = $MaxPreviewHeight;
00262 }
00263
00264 # generate thumbnail image and calculate width and height
00265 $MaxThumbnailWidth = min($MaxThumbnailWidth, $this->Width);
00266 $MaxThumbnailHeight = min($MaxThumbnailHeight, $this->Height);
00267 $SrcImage->ScaleTo($MaxThumbnailWidth, $MaxThumbnailHeight, TRUE);
00268 $SrcImage->SaveAs($this->ThumbnailFileName, IMGTYPE_JPEG);
00269 if (($this->Width * $MaxThumbnailHeight)
00270 > ($this->Height * $MaxThumbnailWidth))
00271 {
00272 $this->ThumbnailWidth = $MaxThumbnailWidth;
00273 $this->ThumbnailHeight =
00274 ($MaxThumbnailWidth * $SrcImage->YSize()) / $SrcImage->XSize();
00275 }
00276 else
00277 {
00278 $this->ThumbnailWidth = ($MaxThumbnailHeight * $SrcImage->XSize()) / $SrcImage->YSize();
00279 $this->ThumbnailHeight = $MaxThumbnailHeight;
00280 }
00281
00282 # save image attributes to database
00283 $this->SaveImageInfo();
00284 }
00285
00286 # ---- PRIVATE INTERFACE -------------------------------------------------
00287
00288 var $Id;
00289 var $FileName;
00290 var $PreviewFileName;
00291 var $ThumbnailFileName;
00292 var $Format;
00293 var $AltText;
00294 var $Url;
00295 var $PreviewUrl;
00296 var $ThumbnailUrl;
00297 var $Height;
00298 var $Width;
00299 var $PreviewHeight;
00300 var $PreviewWidth;
00301 var $ThumbnailHeight;
00302 var $ThumbnailWidth;
00303 var $DB;
00304 var $ErrorStatus;
00305
00306 function CreateNewImage($FileName, $MaxWidth, $MaxHeight,
00307 $MaxPreviewWidth, $MaxPreviewHeight,
00308 $MaxThumbnailWidth, $MaxThumbnailHeight)
00309 {
00310 # if file does not exist or is not readable
00311 if (!is_readable($FileName))
00312 {
00313 # set error status
00314 $this->ErrorStatus = AI_FILEUNREADABLE;
00315 }
00316 else
00317 {
00318 # if image is invalid or unsupported type
00319 $SrcImage = new Image($FileName);
00320 if ($SrcImage->Status() != AI_OKAY)
00321 {
00322 # set error status
00323 $this->ErrorStatus = $SrcImage->Status();
00324 }
00325 else
00326 {
00327 # retrieve image type
00328 $this->Format = $SrcImage->Type();
00329
00330 # generate new image ID
00331 $this->Id = $this->GenerateNewImageId();
00332
00333 # generate and set file names
00334 $this->SetFileNames();
00335
00336 # if our image file name differs from file name passed in
00337 if (realpath($this->FileName) != realpath($FileName))
00338 {
00339 # create image file
00340 $SrcImage->SaveAs($this->FileName);
00341
00342 # if create failed set error status and bail out
00343 if ($SrcImage->Status() != AI_OKAY)
00344 {
00345 echo "create failed<br>";
00346 echo "Status: ".$SrcImage->Status()."<br>";
00347 echo "Failed Command: ".$SrcImage->FailedExternalCommand()."<br>";
00348 echo "Missing External Executables: ";
00349 print_r(Image::MissingExternalExecutables());
00350 echo "<br>";
00351 $this->ErrorStatus = $SrcImage->Status();
00352 return;
00353 }
00354 }
00355
00356 # scale the original image if necessary
00357 $MaxWidth = min($MaxWidth, $SrcImage->XSize());
00358 $MaxHeight = min($MaxHeight, $SrcImage->YSize());
00359 $SrcImage->ScaleTo($MaxWidth, $MaxHeight, TRUE);
00360
00361 # save and reload image info
00362 $SrcImage->SaveAs($this->FileName);
00363 $SrcImage = new Image($this->FileName);
00364
00365 # retrieve image width and height
00366 $this->Height = $SrcImage->YSize();
00367 $this->Width = $SrcImage->XSize();
00368
00369 # generate preview image and calculate width and height
00370 $MaxPreviewWidth = min($MaxPreviewWidth, $this->Width);
00371 $MaxPreviewHeight = min($MaxPreviewHeight, $this->Height);
00372 $SrcImage->ScaleTo($MaxPreviewWidth, $MaxPreviewHeight, TRUE);
00373 $SrcImage->SaveAs($this->PreviewFileName, IMGTYPE_JPEG);
00374 if ($SrcImage->Status() != AI_OKAY)
00375 {
00376 echo "preview save as failed<br>";
00377 $this->ErrorStatus = $SrcImage->Status();
00378 return;
00379 }
00380 if (($this->Width * $MaxPreviewHeight)
00381 > ($this->Height * $MaxPreviewWidth))
00382 {
00383 $this->PreviewWidth = $MaxPreviewWidth;
00384 $this->PreviewHeight =
00385 ($MaxPreviewWidth * $SrcImage->YSize()) / $SrcImage->XSize();
00386 }
00387 else
00388 {
00389 $this->PreviewWidth =
00390 ($MaxPreviewHeight * $SrcImage->XSize()) / $SrcImage->YSize();
00391 $this->PreviewHeight = $MaxPreviewHeight;
00392 }
00393
00394 # generate thumbnail image and calculate width and height
00395 $MaxThumbnailWidth = min($MaxThumbnailWidth, $this->Width);
00396 $MaxThumbnailHeight = min($MaxThumbnailHeight, $this->Height);
00397 $SrcImage->ScaleTo($MaxThumbnailWidth, $MaxThumbnailHeight, TRUE);
00398 $SrcImage->SaveAs($this->ThumbnailFileName, IMGTYPE_JPEG);
00399 if ($SrcImage->Status() != AI_OKAY)
00400 {
00401 echo "thumbnail SaveAs failed.<br>";
00402 $this->ErrorStatus = $SrcImage->Status();
00403 return;
00404 }
00405 if (($this->Width * $MaxThumbnailHeight)
00406 > ($this->Height * $MaxThumbnailWidth))
00407 {
00408 $this->ThumbnailWidth = $MaxThumbnailWidth;
00409 $this->ThumbnailHeight =
00410 ($MaxThumbnailWidth * $SrcImage->YSize()) / $SrcImage->XSize();
00411 }
00412 else
00413 {
00414 $this->ThumbnailWidth = ($MaxThumbnailHeight * $SrcImage->XSize()) / $SrcImage->YSize();
00415 $this->ThumbnailHeight = $MaxThumbnailHeight;
00416 }
00417
00418 # save image attributes to database
00419 $this->SaveImageInfo();
00420 }
00421 }
00422 }
00423
00424 function LoadImageInfo($ImageId)
00425 {
00426 # save image ID
00427 $this->Id = $ImageId;
00428
00429 # load image record from database
00430 $this->DB->Query("SELECT * FROM Images WHERE ImageId = ".$ImageId);
00431
00432 # if the ID is invalid
00433 if (!$this->DB->NumRowsSelected())
00434 {
00435 $this->ErrorStatus = AI_INTERNALERROR;
00436 return;
00437 }
00438
00439 $Record = $this->DB->FetchRow();
00440
00441 # load in values from record
00442 $this->Format = $Record["Format"];
00443 $this->AltText = $Record["AltText"];
00444 $this->Height = $Record["Height"];
00445 $this->Width = $Record["Width"];
00446 $this->PreviewHeight = $Record["PreviewHeight"];
00447 $this->PreviewWidth = $Record["PreviewWidth"];
00448 $this->ThumbnailHeight = $Record["ThumbnailHeight"];
00449 $this->ThumbnailWidth = $Record["ThumbnailWidth"];
00450
00451 # generate file names
00452 $this->SetFileNames();
00453 }
00454
00455 function CreateCopyOfImage($SrcImage)
00456 {
00457 $Image = new Image($SrcImage->Url());
00458 if ($Image->Status() != AI_OKAY)
00459 {
00460 # set error status
00461 $this->ErrorStatus = $Image->Status();
00462 return;
00463 }
00464
00465 # generate new image ID
00466 $this->Id = $this->GenerateNewImageId();
00467
00468 # generate file names
00469 $this->SetFileNames();
00470
00471 # copy attributes from source image
00472 $this->Format = $SrcImage->Format();
00473 $this->AltText = $SrcImage->AltText();
00474 $this->Width = $SrcImage->Width();
00475 $this->Height = $SrcImage->Height();
00476 $this->PreviewWidth = $SrcImage->PreviewWidth();
00477 $this->PreviewHeight = $SrcImage->PreviewHeight();
00478 $this->ThumbnailWidth = $SrcImage->ThumbnailWidth();
00479 $this->ThumbnailHeight = $SrcImage->ThumbnailHeight();
00480
00481 # copy source image files
00482 copy($SrcImage->Url(), $this->FileName);
00483 copy($SrcImage->PreviewUrl(), $this->PreviewFileName);
00484 copy($SrcImage->ThumbnailUrl(), $this->ThumbnailFileName);
00485
00486 # save image attributes to database
00487 $this->SaveImageInfo();
00488 }
00489
00490 # generate and save image, preview, and thumnail file names
00491 # (requires image ID and format to be set beforehand)
00492 function SetFileNames()
00493 {
00494 if (Image::Extension($this->Format))
00495 {
00496 $FileExtension = Image::Extension($this->Format);
00497 }
00498 else
00499 {
00500 $FileExtension = "";
00501 }
00502
00503 $this->FileName = self::IMAGE_PATH . "Img--"
00504 .sprintf("%08d.", $this->Id).$FileExtension;
00505 $this->PreviewFileName = self::PREVIEW_PATH . "Preview--"
00506 .sprintf("%08d.", $this->Id).$FileExtension;
00507 $this->ThumbnailFileName = self::THUMBNAIL_PATH . "Thumb--"
00508 .sprintf("%08d.", $this->Id).$FileExtension;
00509 }
00510
00511 # retrieve next image ID
00512 function GenerateNewImageId()
00513 {
00514 # look up highest image ID in database
00515 $CurrentHighestId = $this->DB->Query("SELECT ImageId FROM Images"
00516 ." ORDER BY ImageId DESC LIMIT 1",
00517 "ImageId");
00518
00519 # return next highest ID or 1 if no ID yet used
00520 return ($CurrentHighestId > 0) ? ($CurrentHighestId + 1) : 1;
00521 }
00522
00523 # store image attributes to database
00524 function SaveImageInfo()
00525 {
00526 # look for existing image record with matching ID
00527 $RecordCount = $this->DB->Query("SELECT COUNT(*) AS RecordCount FROM Images"
00528 ." WHERE ImageId = ".$this->Id,
00529 "RecordCount");
00530
00531 # if matching ID found
00532 if ($RecordCount > 0)
00533 {
00534 # update existing image record
00535 $this->DB->Query("UPDATE Images SET"
00536 ." Format = '" .$this->Format."',"
00537 ." AltText = '" .addslashes($this->AltText)."',"
00538 ." Height = '" .$this->Height."',"
00539 ." Width = '" .$this->Width."',"
00540 ." PreviewHeight = '" .$this->PreviewHeight."',"
00541 ." PreviewWidth = '" .$this->PreviewWidth."',"
00542 ." ThumbnailHeight = '".$this->ThumbnailHeight."',"
00543 ." ThumbnailWidth = '" .$this->ThumbnailWidth."'"
00544 ." WHERE ImageId = ".$this->Id);
00545 }
00546 else
00547 {
00548 # add new image record
00549 $this->DB->Query("INSERT INTO Images SET"
00550 ." ImageId = '" .$this->Id."',"
00551 ." Format = '" .$this->Format."',"
00552 ." AltText = '" .addslashes($this->AltText)."',"
00553 ." Height = '" .$this->Height."',"
00554 ." Width = '" .$this->Width."',"
00555 ." PreviewHeight = '" .$this->PreviewHeight."',"
00556 ." PreviewWidth = '" .$this->PreviewWidth."',"
00557 ." ThumbnailHeight = '".$this->ThumbnailHeight."',"
00558 ." ThumbnailWidth = '" .$this->ThumbnailWidth."'");
00559 }
00560 }
00561 }