00001 <?PHP
00002
00003 #
00004 # FILE: SPT--SPTImage.php
00005 #
00006 # METHODS PROVIDED:
00007 # SPTImage($ImageIdOrFileNameOrImageObj,
00008 # $MaxPreviewWidth = NULL, $MaxPreviewHeight = NULL,
00009 # $MaxThumbnailWidth = NULL, $MaxThumbnailHeight = NULL)
00010 # - object constructor
00011 # Delete()
00012 # - delete image and associated files and data
00013 # AltText($NewValue = NULL)
00014 # - get/set alt text attribute
00015 # Id()
00016 # Url()
00017 # PreviewUrl()
00018 # ThumbnailUrl()
00019 # Format()
00020 # Height()
00021 # Width()
00022 # PreviewHeight()
00023 # PreviewWidth()
00024 # ThumbnailHeight()
00025 # ThumbnailWidth()
00026 # - get attributes
00027 #
00028 # AUTHOR: Edward Almasy
00029 #
00030 # Part of the Scout Portal Toolkit
00031 # Copyright 2002-2003 Internet Scout Project
00032 # http://scout.wisc.edu
00033 #
00034
00035
00036 class SPTImage {
00037
00038 # ---- PUBLIC INTERFACE --------------------------------------------------
00039
00040 # object constructor
00041 function SPTImage($ImageIdOrFileNameOrImageObj,
00042 $MaxPreviewWidth = NULL, $MaxPreviewHeight = NULL,
00043 $MaxThumbnailWidth = NULL, $MaxThumbnailHeight = NULL)
00044 {
00045 # clear error status (0 = AI_OKAY)
00046 $this->ErrorStatus = 0;
00047
00048 # trigger the Image class file to be autoloaded since some parts of this
00049 # class (SPTImage) use constants defined in it but don't construct Image
00050 # objects
00051 new Image(NULL);
00052
00053 # set base path for image storage
00054 # (NOTE: This is also used for URLs, so it must be a relative path.)
00055 # (NOTE: These must match paths in SPTImage::CheckDirectories()!!!)
00056 $this->ImagePath = "ImageStorage/";
00057 $this->PreviewPath = $this->ImagePath."Previews/";
00058 $this->ThumbnailPath = $this->ImagePath."Thumbnails/";
00059
00060 # create and save a database handle for our use
00061 $this->DB = new SPTDatabase();
00062
00063 # if image object was passed in
00064 if (is_object($ImageIdOrFileNameOrImageObj)
00065 && method_exists($ImageIdOrFileNameOrImageObj, "SPTImage"))
00066 {
00067 # create copy of image passed in
00068 $this->CreateCopyOfImage($ImageIdOrFileNameOrImageObj);
00069 }
00070 # else if image ID was passed in
00071 elseif (($ImageIdOrFileNameOrImageObj > 0)
00072 && preg_match("/[0-9]+/", $ImageIdOrFileNameOrImageObj))
00073 {
00074 # load info on existing image
00075 $this->LoadImageInfo($ImageIdOrFileNameOrImageObj);
00076 }
00077 # else assume that value passed in is file name
00078 else
00079 {
00080 # create new image from named file
00081 $this->CreateNewImage($ImageIdOrFileNameOrImageObj,
00082 $MaxPreviewWidth, $MaxPreviewHeight,
00083 $MaxThumbnailWidth, $MaxThumbnailHeight);
00084 }
00085 }
00086
00087 # get attributes
00088 function Id() { return $this->Id; }
00089 function Url() { return $this->FileName; }
00090 function PreviewUrl() { return $this->PreviewFileName; }
00091 function ThumbnailUrl() { return $this->ThumbnailFileName; }
00092 function Format() { return $this->Format; }
00093 function Height() { return $this->Height; }
00094 function Width() { return $this->Width; }
00095 function PreviewHeight() { return $this->PreviewHeight; }
00096 function PreviewWidth() { return $this->PreviewWidth; }
00097 function ThumbnailHeight() { return $this->ThumbnailHeight; }
00098 function ThumbnailWidth() { return $this->ThumbnailWidth; }
00099 function ImageStorageDirectory() { return $this->ImagePath; }
00100 function PreviewStorageDirectory() { return $this->PreviewPath; }
00101 function ThumbnailStorageDirectory() { return $this->ThumbnailPath; }
00102 function GetLink() { return $this->FileName; }
00103
00104 # get/set attributes
00105 function AltText($NewValue = NULL)
00106 {
00107 # if new value supplied and new value differs from existing value
00108 if (($NewValue !== NULL) && ($NewValue != $this->AltText))
00109 {
00110 # save new value to database
00111 $this->DB->Query("UPDATE Images SET"
00112 ." AltText = '".addslashes($NewValue)."'"
00113 ." WHERE ImageId = ".$this->Id);
00114
00115 # save new value locally
00116 $this->AltText = $NewValue;
00117 }
00118
00119 # return attribute value to caller
00120 return $this->AltText;
00121 }
00122
00123 # delete image and associated files and data
00124 function Delete()
00125 {
00126 # delete base image file
00127 if (file_exists($this->FileName)) { unlink($this->FileName); }
00128
00129 # delete preview image file
00130 if (file_exists($this->PreviewFileName)) { unlink($this->PreviewFileName); }
00131
00132 # delete thumbnail image file
00133 if (file_exists($this->ThumbnailFileName)) { unlink($this->ThumbnailFileName); }
00134
00135 # delete image info record in database
00136 $this->DB->Query("DELETE FROM Images WHERE ImageId = ".$this->Id);
00137 }
00138
00139 # return error status set by the constructor
00140 function Status()
00141 {
00142 return $this->ErrorStatus;
00143 }
00144
00145 # check to make sure image storage directories are available
00146 # (returns array of error codes or NULL if no errors found)
00147 # (may be called statically)
00148 function CheckDirectories()
00149 {
00150 # determine paths
00151 # (NOTE: These must match paths in the constructur!!!)
00152 $ImagePath = "ImageStorage/";
00153 $PreviewPath = $ImagePath."Previews/";
00154 $ThumbnailPath = $ImagePath."Thumbnails/";
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
00227 # ---- PRIVATE INTERFACE -------------------------------------------------
00228
00229 var $Id;
00230 var $FileName;
00231 var $ImagePath;
00232 var $PreviewPath;
00233 var $ThumbnailPath;
00234 var $PreviewFileName;
00235 var $ThumbnailFileName;
00236 var $Format;
00237 var $AltText;
00238 var $Url;
00239 var $PreviewUrl;
00240 var $ThumbnailUrl;
00241 var $Height;
00242 var $Width;
00243 var $PreviewHeight;
00244 var $PreviewWidth;
00245 var $ThumbnailHeight;
00246 var $ThumbnailWidth;
00247 var $DB;
00248 var $ErrorStatus;
00249
00250 function CreateNewImage($FileName, $MaxPreviewWidth, $MaxPreviewHeight,
00251 $MaxThumbnailWidth, $MaxThumbnailHeight)
00252 {
00253 # if file does not exist or is not readable
00254 if (!is_readable($FileName))
00255 {
00256 # set error status
00257 $this->ErrorStatus = AI_FILEUNREADABLE;
00258 }
00259 else
00260 {
00261 # if image is invalid or unsupported type
00262 $SrcImage = new Image($FileName);
00263 if ($SrcImage->Status() != AI_OKAY)
00264 {
00265 # set error status
00266 $this->ErrorStatus = $SrcImage->Status();
00267 }
00268 else
00269 {
00270 # retrieve image type
00271 $this->Format = $SrcImage->Type();
00272
00273 # generate new image ID
00274 $this->Id = $this->GenerateNewImageId();
00275
00276 # generate and set file names
00277 $this->SetFileNames();
00278
00279 # if our image file name differs from file name passed in
00280 if (realpath($this->FileName) != realpath($FileName))
00281 {
00282 # create image file
00283 $SrcImage->SaveAs($this->FileName);
00284
00285 # if create failed set error status and bail out
00286 if ($SrcImage->Status() != AI_OKAY)
00287 {
00288 echo "create failed<br>";
00289 echo "Status: ".$SrcImage->Status()."<br>";
00290 echo "Failed Command: ".$SrcImage->FailedExternalCommand()."<br>";
00291 echo "Missing External Executables: ";
00292 print_r(Image::MissingExternalExecutables());
00293 echo "<br>";
00294 $this->ErrorStatus = $SrcImage->Status();
00295 return;
00296 }
00297 }
00298
00299 # retrieve image width and height
00300 $this->Height = $SrcImage->YSize();
00301 $this->Width = $SrcImage->XSize();
00302
00303 # generate preview image and calculate width and height
00304 $MaxPreviewWidth = min($MaxPreviewWidth, $this->Width);
00305 $MaxPreviewHeight = min($MaxPreviewHeight, $this->Height);
00306 $SrcImage->ScaleTo($MaxPreviewWidth, $MaxPreviewHeight, TRUE);
00307 $SrcImage->SaveAs($this->PreviewFileName, IMGTYPE_JPEG);
00308 if ($SrcImage->Status() != AI_OKAY)
00309 {
00310 echo "preview save as failed<br>";
00311 $this->ErrorStatus = $SrcImage->Status();
00312 return;
00313 }
00314 if (($this->Width * $MaxPreviewHeight)
00315 > ($this->Height * $MaxPreviewWidth))
00316 {
00317 $this->PreviewWidth = $MaxPreviewWidth;
00318 $this->PreviewHeight =
00319 ($MaxPreviewWidth * $SrcImage->YSize()) / $SrcImage->XSize();
00320 }
00321 else
00322 {
00323 $this->PreviewWidth =
00324 ($MaxPreviewHeight * $SrcImage->XSize()) / $SrcImage->YSize();
00325 $this->PreviewHeight = $MaxPreviewHeight;
00326 }
00327
00328 # generate thumbnail image and calculate width and height
00329 $MaxThumbnailWidth = min($MaxThumbnailWidth, $this->Width);
00330 $MaxThumbnailHeight = min($MaxThumbnailHeight, $this->Height);
00331 $SrcImage->ScaleTo($MaxThumbnailWidth, $MaxThumbnailHeight, TRUE);
00332 $SrcImage->SaveAs($this->ThumbnailFileName, IMGTYPE_JPEG);
00333 if ($SrcImage->Status() != AI_OKAY)
00334 {
00335 echo "thumbnail SaveAs failed.<br>";
00336 $this->ErrorStatus = $SrcImage->Status();
00337 return;
00338 }
00339 if (($this->Width * $MaxThumbnailHeight)
00340 > ($this->Height * $MaxThumbnailWidth))
00341 {
00342 $this->ThumbnailWidth = $MaxThumbnailWidth;
00343 $this->ThumbnailHeight =
00344 ($MaxThumbnailWidth * $SrcImage->YSize()) / $SrcImage->XSize();
00345 }
00346 else
00347 {
00348 $this->ThumbnailWidth = ($MaxThumbnailHeight * $SrcImage->XSize()) / $SrcImage->YSize();
00349 $this->ThumbnailHeight = $MaxThumbnailHeight;
00350 }
00351
00352 # save image attributes to database
00353 $this->SaveImageInfo();
00354 }
00355 }
00356 }
00357
00358 function LoadImageInfo($ImageId)
00359 {
00360 # save image ID
00361 $this->Id = $ImageId;
00362
00363 # load image record from database
00364 $this->DB->Query("SELECT * FROM Images WHERE ImageId = ".$ImageId);
00365
00366 # if the ID is invalid
00367 if (!$this->DB->NumRowsSelected())
00368 {
00369 $this->ErrorStatus = AI_INTERNALERROR;
00370 return;
00371 }
00372
00373 $Record = $this->DB->FetchRow();
00374
00375 # load in values from record
00376 $this->Format = $Record["Format"];
00377 $this->AltText = $Record["AltText"];
00378 $this->Height = $Record["Height"];
00379 $this->Width = $Record["Width"];
00380 $this->PreviewHeight = $Record["PreviewHeight"];
00381 $this->PreviewWidth = $Record["PreviewWidth"];
00382 $this->ThumbnailHeight = $Record["ThumbnailHeight"];
00383 $this->ThumbnailWidth = $Record["ThumbnailWidth"];
00384
00385 # generate file names
00386 $this->SetFileNames();
00387 }
00388
00389 function CreateCopyOfImage($SrcImage)
00390 {
00391 $Image = new Image($SrcImage->Url());
00392 if ($Image->Status() != AI_OKAY)
00393 {
00394 # set error status
00395 $this->ErrorStatus = $Image->Status();
00396 return;
00397 }
00398
00399 # generate new image ID
00400 $this->Id = $this->GenerateNewImageId();
00401
00402 # generate file names
00403 $this->SetFileNames();
00404
00405 # copy attributes from source image
00406 $this->Format = $SrcImage->Format();
00407 $this->AltText = $SrcImage->AltText();
00408 $this->Width = $SrcImage->Width();
00409 $this->Height = $SrcImage->Height();
00410 $this->PreviewWidth = $SrcImage->PreviewWidth();
00411 $this->PreviewHeight = $SrcImage->PreviewHeight();
00412 $this->ThumbnailWidth = $SrcImage->ThumbnailWidth();
00413 $this->ThumbnailHeight = $SrcImage->ThumbnailHeight();
00414
00415 # copy source image files
00416 copy($SrcImage->Url(), $this->FileName);
00417 copy($SrcImage->PreviewUrl(), $this->PreviewFileName);
00418 copy($SrcImage->ThumbnailUrl(), $this->ThumbnailFileName);
00419
00420 # save image attributes to database
00421 $this->SaveImageInfo();
00422 }
00423
00424 # generate and save image, preview, and thumnail file names
00425 # (requires image ID and format to be set beforehand)
00426 function SetFileNames()
00427 {
00428 if (Image::Extension($this->Format))
00429 {
00430 $FileExtension = Image::Extension($this->Format);
00431 }
00432 else
00433 {
00434 $FileExtension = "";
00435 }
00436
00437 $this->FileName = $this->ImagePath."Img--"
00438 .sprintf("%08d.", $this->Id).$FileExtension;
00439 $this->PreviewFileName = $this->PreviewPath."Preview--"
00440 .sprintf("%08d.", $this->Id).$FileExtension;
00441 $this->ThumbnailFileName = $this->ThumbnailPath."Thumb--"
00442 .sprintf("%08d.", $this->Id).$FileExtension;
00443 }
00444
00445 # retrieve next image ID
00446 function GenerateNewImageId()
00447 {
00448 # look up highest image ID in database
00449 $CurrentHighestId = $this->DB->Query("SELECT ImageId FROM Images"
00450 ." ORDER BY ImageId DESC LIMIT 1",
00451 "ImageId");
00452
00453 # return next highest ID or 1 if no ID yet used
00454 return ($CurrentHighestId > 0) ? ($CurrentHighestId + 1) : 1;
00455 }
00456
00457 # store image attributes to database
00458 function SaveImageInfo()
00459 {
00460 # look for existing image record with matching ID
00461 $RecordCount = $this->DB->Query("SELECT COUNT(*) AS RecordCount FROM Images"
00462 ." WHERE ImageId = ".$this->Id,
00463 "RecordCount");
00464
00465 # if matching ID found
00466 if ($RecordCount > 0)
00467 {
00468 # update existing image record
00469 $this->DB->Query("UPDATE Images SET"
00470 ." Format = '" .$this->Format."',"
00471 ." AltText = '" .addslashes($this->AltText)."',"
00472 ." Height = '" .$this->Height."',"
00473 ." Width = '" .$this->Width."',"
00474 ." PreviewHeight = '" .$this->PreviewHeight."',"
00475 ." PreviewWidth = '" .$this->PreviewWidth."',"
00476 ." ThumbnailHeight = '".$this->ThumbnailHeight."',"
00477 ." ThumbnailWidth = '" .$this->ThumbnailWidth."'"
00478 ." WHERE ImageId = ".$this->Id);
00479 }
00480 else
00481 {
00482 # add new image record
00483 $this->DB->Query("INSERT INTO Images SET"
00484 ." ImageId = '" .$this->Id."',"
00485 ." Format = '" .$this->Format."',"
00486 ." AltText = '" .addslashes($this->AltText)."',"
00487 ." Height = '" .$this->Height."',"
00488 ." Width = '" .$this->Width."',"
00489 ." PreviewHeight = '" .$this->PreviewHeight."',"
00490 ." PreviewWidth = '" .$this->PreviewWidth."',"
00491 ." ThumbnailHeight = '".$this->ThumbnailHeight."',"
00492 ." ThumbnailWidth = '" .$this->ThumbnailWidth."'");
00493 }
00494 }
00495 }
00496
00497
00498 ?>