1<?php 2 3 4// force UTF-8 Ø 5 6define('IMAGE_SORT_DIRECTION', getOption('image_sortdirection')); 7define('IMAGE_SORT_TYPE', getOption('image_sorttype')); 8 9Gallery::addAlbumHandler('alb', 'dynamicAlbum'); 10 11/** 12 * Wrapper instantiation function for albums. Do not instantiate directly 13 * @param string $folder8 the name of the folder (inernal character set) 14 * @param bool $cache true if the album should be fetched from the cache 15 * @param bool $quiet true to supress error messages 16 * @return Album 17 */ 18function newAlbum($folder8, $cache = true, $quiet = false) { 19 global $_zp_albumHandlers; 20 $suffix = getSuffix($folder8); 21 if (!$suffix || !array_key_exists($suffix, $_zp_albumHandlers) || is_dir(ALBUM_FOLDER_SERVERPATH . internalToFilesystem($folder8))) { 22 return new Album($folder8, $cache, $quiet); 23 } else { 24 return new $_zp_albumHandlers[$suffix]($folder8, $cache, $quiet); 25 } 26} 27 28/** 29 * Returns true if the object is a zenphoto 'album' 30 * 31 * @param object $album 32 * @return bool 33 */ 34function isAlbumClass($album = NULL) { 35 global $_zp_current_album; 36 if (is_null($album)) { 37 if (!in_context(ZP_ALBUM)) 38 return false; 39 $album = $_zp_current_album; 40 } 41 return is_object($album) && ($album->table == 'albums'); 42} 43 44/** 45 * Album Base Class 46 * @package core 47 * @subpackage classes\objects 48 */ 49class AlbumBase extends MediaObject { 50 51 public $name; // Folder name of the album (full path from the albums folder) 52 public $linkname; // may have the .alb suffix stripped off 53 public $localpath; // Latin1 full server path to the album 54 public $exists = true; // Does the folder exist? 55 public $images = null; // Full images array storage. 56 public $parent = null; // The parent album name 57 public $parentalbum = null; // The parent album's album object (lazy) 58 public $parentalbums = null; // Array of objects of parent albums (lazy) 59 public $sidecars = array(); // keeps the list of suffixes associated with this album 60 public $manage_rights = MANAGE_ALL_ALBUM_RIGHTS; 61 public $manage_some_rights = ALBUM_RIGHTS; 62 public $view_rights = ALL_ALBUMS_RIGHTS; 63 protected $subalbums = null; // Full album array storage. 64 protected $index; 65 protected $lastimagesort = NULL; // remember the order for the last album/image sorts 66 protected $lastsubalbumsort = NULL; 67 protected $albumthumbnail = NULL; // remember the album thumb for the duration of the script 68 protected $subrights = NULL; // cache for album subrights 69 protected $num_allalbums = null; // count of all subalbums of all sublevels 70 protected $num_allimages = null; // count of all images of all sublevels 71 protected $is_public = null; 72 73 function __construct($folder8, $cache = true) { 74 $this->linkname = $this->name = $folder8; 75 $this->instantiate('albums', array('folder' => $this->name), 'folder', false, true); 76 $this->exists = false; 77 } 78 79 /** 80 * Sets default values for a new album 81 * 82 * @return bool 83 */ 84 protected function setDefaults() { 85 global $_zp_gallery; 86 if (TEST_RELEASE) { 87 $bt = debug_backtrace(); 88 $good = false; 89 foreach ($bt as $b) { 90 if ($b['function'] == "newAlbum") { 91 $good = true; 92 break; 93 } 94 } 95 if (!$good) { 96 zp_error(gettext('An album object was instantiated without using the newAlbum() function.'), E_USER_WARNING); 97 } 98 } 99// Set default data for a new Album (title and parent_id) 100 $parentalbum = NULL; 101 $this->setShow($_zp_gallery->getAlbumPublish()); 102 $this->set('mtime', time()); 103 $this->setLastChange(); 104 $title = trim($this->name); 105 $this->set('title', sanitize($title, 2)); 106 return true; 107 } 108 109 /** 110 * Returns the folder on the filesystem 111 * 112 * @return string 113 */ 114 function getFileName() { 115 return $this->name; 116 } 117 118 /** 119 * Returns the folder on the filesystem 120 * 121 * @return string 122 */ 123 function getFolder() { 124 return $this->name; 125 } 126 127 /** 128 * Returns The parent Album of this Album. NULL if this is a top-level album. 129 * 130 * @return object 131 */ 132 function getParent() { 133 if (is_null($this->parentalbum)) { 134 $slashpos = strrpos($this->name, "/"); 135 if ($slashpos) { 136 $parent = substr($this->name, 0, $slashpos); 137 $parentalbum = newAlbum($parent, true, true); 138 if ($parentalbum->exists) { 139 return $parentalbum; 140 } 141 } 142 } else if ($this->parentalbum->exists) { 143 return $this->parentalbum; 144 } 145 return NULL; 146 } 147 148 /** 149 * Gets an array of parent album objects 150 * 151 * @since Zenphoto 1.5.5 152 * 153 * @return array|null 154 */ 155 function getParents() { 156 if (is_null($this->parentalbums)) { 157 $parents = array(); 158 $album = $this; 159 while (!is_null($album = $album->getParent())) { 160 array_unshift($parents, $album); 161 } 162 return $this->parentalbums = $parents; 163 } else { 164 return $this->parentalbums; 165 } 166 } 167 168 169 function getParentID() { 170 return $this->get('parentid'); 171 } 172 173 /** 174 * Returns the place data of an album 175 * 176 * @return string 177 */ 178 function getLocation($locale = NULL) { 179 $text = $this->get('location'); 180 if ($locale !== 'all') { 181 $text = get_language_string($text, $locale); 182 } 183 $text = unTagURLs($text); 184 return $text; 185 } 186 187 /** 188 * Stores the album place 189 * 190 * @param string $place text for the place field 191 */ 192 function setLocation($place) { 193 $this->set('location', tagURLs($place)); 194 } 195 196 /** 197 * Returns either the subalbum sort direction or the image sort direction of the album 198 * 199 * @param string $what 'image_' if you want the image direction, 200 * 'album' if you want it for the album 201 * 202 * @return string 203 */ 204 function getSortDirection($what = 'image') { 205 global $_zp_gallery; 206 if ($what == 'image') { 207 $direction = $this->get('image_sortdirection'); 208 $type = $this->get('sort_type'); 209 } else { 210 $direction = $this->get('album_sortdirection'); 211 $type = $this->get('subalbum_sort_type'); 212 } 213 if (empty($type)) { 214// using inherited type, so use inherited direction 215 $parentalbum = $this->getParent(); 216 if (is_null($parentalbum)) { 217 if ($what == 'image') { 218 $direction = IMAGE_SORT_DIRECTION; 219 } else { 220 $direction = $_zp_gallery->getSortDirection(); 221 } 222 } else { 223 $direction = $parentalbum->getSortDirection($what); 224 } 225 } 226 return $direction; 227 } 228 229 /** 230 * Returns the sort type of the album images 231 * Will return a parent sort type if the sort type for this album is empty 232 * 233 * @return string 234 */ 235 function getSortType($what = 'image') { 236 global $_zp_gallery; 237 if ($what == 'image') { 238 $type = $this->get('sort_type'); 239 } else { 240 $type = $this->get('subalbum_sort_type'); 241 } 242 if (empty($type)) { 243 $parentalbum = $this->getParent(); 244 if (is_null($parentalbum)) { 245 if ($what == 'image') { 246 $type = IMAGE_SORT_TYPE; 247 } else { 248 $type = $_zp_gallery->getSortType(); 249 } 250 } else { 251 $type = $parentalbum->getSortType($what); 252 } 253 } 254 return $type; 255 } 256 257 /** 258 * sets sort directions for the album 259 * 260 * @param bool $val the direction 261 * @param string $what 'image_sortdirection' if you want the image direction, 262 * 'album_sortdirection' if you want it for the album 263 */ 264 function setSortDirection($val, $what = 'image') { 265 if ($what == 'image') { 266 $this->set('image_sortdirection', (int) ($val && true)); 267 } else { 268 $this->set('album_sortdirection', (int) ($val && true)); 269 } 270 } 271 272 /** 273 * Stores the sort type for the album 274 * 275 * @param string $sorttype the album sort type 276 * @param string $what 'Description'image' or 'album' 277 */ 278 function setSortType($sorttype, $what = 'image') { 279 if ($what == 'image') { 280 $this->set('sort_type', $sorttype); 281 } else { 282 $this->set('subalbum_sort_type', $sorttype); 283 } 284 } 285 286 /** 287 * Returns the DB key associated with the image sort type 288 * 289 * @param string $sorttype the sort type 290 * @return string 291 */ 292 function getImageSortKey($sorttype = null) { 293 if (is_null($sorttype)) { 294 $sorttype = $this->getSortType(); 295 } 296 return lookupSortKey($sorttype, 'filename', 'images'); 297 } 298 299 /** 300 * Returns the DB key associated with the subalbum sort type 301 * 302 * @param string $sorttype subalbum sort type 303 * @return string 304 */ 305 function getAlbumSortKey($sorttype = null) { 306 if (empty($sorttype)) { 307 $sorttype = $this->getSortType('album'); 308 } 309 return lookupSortKey($sorttype, 'sort_order', 'albums'); 310 } 311 312 /** 313 * Returns all folder names for all the subdirectories. 314 * 315 * @param string $page Which page of subalbums to display. 316 * @param string $sorttype The sort strategy 317 * @param string $sortdirection The direction of the sort 318 * @param bool $care set to false if the order does not matter 319 * @param bool $mine set true/false to override ownership 320 * @return array 321 */ 322 function getAlbums($page = 0, $sorttype = null, $sortdirection = null, $care = true, $mine = NULL) { 323 if ($page == 0) { 324 return $this->subalbums; 325 } else { 326 $albums_per_page = max(1, getOption('albums_per_page')); 327 return array_slice($this->subalbums, $albums_per_page * ($page - 1), $albums_per_page); 328 } 329 } 330 331 /** 332 * Returns the count of direct child subalbums 333 * 334 * @return int 335 */ 336 function getNumAlbums() { 337 return count($this->getAlbums(0, NULL, NULL, false)); 338 } 339 340 /** 341 * Returns the count of all subalbums of all sublevels 342 * Note that dynamic albums are not counted 343 * 344 * @since Zenphoto 1.5.2 345 */ 346 function getNumAllAlbums() { 347 if (!is_null($this->num_allalbums)) { 348 return $this->num_allalbums; 349 } else { 350 $count = $this->getNumAlbums(); 351 $subalbums = $this->getAlbums(); 352 foreach ($subalbums as $folder) { 353 $subalbum = newAlbum($folder); 354 if (!$subalbum->isDynamic()) { 355 $count += $subalbum->getNumAllAlbums(); 356 } 357 } 358 return $count; 359 } 360 } 361 362 /** 363 * Returns a of a slice of the images for this album. They will 364 * also be sorted according to the sort type of this album, or by filename if none 365 * has been set. 366 * 367 * @param string $page Which page of images should be returned. If zero, all images are returned. 368 * @param int $firstPageCount count of images that go on the album/image transition page 369 * @param string $sorttype optional sort type 370 * @param string $sortdirection optional sort direction 371 * @param bool $care set to false if the order of the images does not matter 372 * @param bool $mine set true/false to override ownership 373 * 374 * @return array 375 */ 376 function getImages($page = 0, $firstPageCount = 0, $sorttype = null, $sortdirection = null, $care = true, $mine = NULL) { 377// Return the cut of images based on $page. Page 0 means show all. 378 if ($page == 0) { 379 return $this->images; 380 } else { 381// Only return $firstPageCount images if we are on the first page and $firstPageCount > 0 382 if (($page == 1) && ($firstPageCount > 0)) { 383 $pageStart = 0; 384 $images_per_page = $firstPageCount; 385 } else { 386 if ($firstPageCount > 0) { 387 $fetchPage = $page - 2; 388 } else { 389 $fetchPage = $page - 1; 390 } 391 $images_per_page = max(1, getOption('images_per_page')); 392 $pageStart = (int) ($firstPageCount + $images_per_page * $fetchPage); 393 } 394 return array_slice($this->images, $pageStart, $images_per_page); 395 } 396 } 397 398 /** 399 * Returns the number of images in this album (not counting its subalbums) 400 * 401 * @return int 402 */ 403 function getNumImages() { 404 if (is_null($this->images)) { 405 return count($this->getImages(0, 0, NULL, NULL, false)); 406 } 407 return count($this->images); 408 } 409 410 /** 411 * Returns the number of images in this album and subalbums of all levels 412 * Note that dynamic albums are not counted. 413 * 414 * @since Zenphoto 1.5.2 415 */ 416 function getNumAllImages() { 417 if (!is_null($this->num_allimages)) { 418 return $this->num_allimages; 419 } else { 420 $count = $this->getNumImages(); 421 $subalbums = $this->getAlbums(); 422 foreach ($subalbums as $folder) { 423 $subalbum = newAlbum($folder); 424 if (!$subalbum->isDynamic()) { 425 $count += $subalbum->getNumAllImages(); 426 } 427 } 428 return $count; 429 } 430 } 431 432 /** 433 * Returns an image from the album based on the index passed. 434 * 435 * @param int $index 436 * @return int 437 */ 438 function getImage($index) { 439 $images = $this->getImages(); 440 if ($index >= 0 && $index < count($images)) { 441 return newImage($this, $this->images[$index]); 442 } 443 return false; 444 } 445 446 /** 447 * Gets the album's set thumbnail image from the database if one exists, 448 * otherwise, finds the first image in the album or sub-album and returns it 449 * as an Image object. 450 * 451 * @return Image 452 */ 453 function getAlbumThumbImage() { 454 global $_zp_albumthumb_selector, $_zp_gallery; 455 456 if (!is_null($this->albumthumbnail)) { 457 return $this->albumthumbnail; 458 } 459 460 $albumdir = $this->localpath; 461 $thumb = $this->get('thumb'); 462 if (is_null($thumb)) { 463 $this->set('thumb', $thumb = getOption('AlbumThumbSelect')); 464 } 465 $i = strpos($thumb, '/'); 466 if ($root = ($i === 0)) { 467 $thumb = substr($thumb, 1); // strip off the slash 468 $albumdir = ALBUM_FOLDER_SERVERPATH; 469 } 470 if (!empty($thumb) && !is_numeric($thumb)) { 471 if (file_exists($albumdir . internalToFilesystem($thumb))) { 472 if ($i === false) { 473 return newImage($this, $thumb); 474 } else { 475 $pieces = explode('/', $thumb); 476 $i = count($pieces); 477 $thumb = $pieces[$i - 1]; 478 unset($pieces[$i - 1]); 479 $albumdir = implode('/', $pieces); 480 if (!$root) { 481 $albumdir = $this->name . "/" . $albumdir; 482 } else { 483 $albumdir = $albumdir . "/"; 484 } 485 $this->albumthumbnail = newImage(newAlbum($albumdir), $thumb); 486 return $this->albumthumbnail; 487 } 488 } else { 489 $this->set('thumb', $thumb = getOption('AlbumThumbSelect')); 490 } 491 } 492 if ($shuffle = empty($thumb)) { 493 $thumbs = $this->getImages(0, 0, NULL, NULL, false); 494 } else { 495 $thumbs = $this->getImages(0, 0, $_zp_albumthumb_selector[(int) $thumb]['field'], $_zp_albumthumb_selector[(int) $thumb]['direction']); 496 } 497 if (!is_null($thumbs)) { 498 if ($shuffle) { 499 shuffle($thumbs); 500 } 501 $mine = $this->isMyItem(LIST_RIGHTS); 502 $other = NULL; 503 while (count($thumbs) > 0) { 504 // first check for images 505 $thumb = array_shift($thumbs); 506 $thumb = newImage($this, $thumb); 507 if ($mine || $thumb->isPublished()) { 508 if (isImagePhoto($thumb)) { 509 // legitimate image 510 $this->albumthumbnail = $thumb; 511 return $this->albumthumbnail; 512 } else { 513 if (!is_null($thumb->objectsThumb)) { 514 // "other" image with a thumb sidecar 515 $this->albumthumbnail = $thumb; 516 return $this->albumthumbnail; 517 } else { 518 if (is_null($other)) { 519 $other = $thumb; 520 } 521 } 522 } 523 } 524 } 525 if (!is_null($other)) { 526 // "other" image, default thumb 527 $this->albumthumbnail = $other; 528 return $this->albumthumbnail; 529 } 530 } 531 532 // Otherwise, look in sub-albums. 533 $subalbums = $this->getAlbums(); 534 if (!is_null($subalbums)) { 535 if ($shuffle) { 536 shuffle($subalbums); 537 } 538 while (count($subalbums) > 0) { 539 $folder = array_pop($subalbums); 540 $subalbum = newAlbum($folder); 541 $pwd = $subalbum->getPassword(); 542 if (($subalbum->isPublished() && empty($pwd)) || $subalbum->isMyItem(LIST_RIGHTS)) { 543 $thumb = $subalbum->getAlbumThumbImage(); 544 if (strtolower(get_class($thumb)) !== 'transientimage' && $thumb->exists) { 545 $this->albumthumbnail = $thumb; 546 return $thumb; 547 } 548 } 549 } 550 } 551 552 $nullimage = SERVERPATH . '/' . ZENFOLDER . '/images/imageDefault.png'; 553 // check for theme imageDefault.png 554 $theme = ''; 555 $uralbum = getUralbum($this); 556 $albumtheme = $uralbum->getAlbumTheme(); 557 if (!empty($albumtheme)) { 558 $theme = $albumtheme; 559 } else { 560 $theme = $_zp_gallery->getCurrentTheme(); 561 } 562 if (!empty($theme)) { 563 $themeimage = SERVERPATH . '/' . THEMEFOLDER . '/' . $theme . '/images/imageDefault.png'; 564 if (file_exists(internalToFilesystem($themeimage))) { 565 $nullimage = $themeimage; 566 } 567 } 568 569 $this->albumthumbnail = new transientimage($this, $nullimage); 570 return $this->albumthumbnail; 571 } 572 573 /** 574 * Gets the thumbnail URL for the album thumbnail image as returned by $this->getAlbumThumbImage(); 575 * @return string 576 */ 577 function getThumb() { 578 $image = $this->getAlbumThumbImage(); 579 return $image->getThumb('album'); 580 } 581 582 /** 583 * Stores the thumbnail path for an album thumg 584 * 585 * @param string $filename thumbnail path 586 */ 587 function setThumb($filename) { 588 $this->set('thumb', $filename); 589 } 590 591 /** 592 * Returns an URL to the album, including the current page number 593 * 594 * @param string $page if not null, apppend as page # 595 * @return string 596 */ 597 function getLink($page = NULL) { 598 global $_zp_current_album; 599 global $_zp_page; 600 if (is_null($page) && $_zp_current_album && $_zp_current_album->name == $this->name) { 601 $page = $_zp_page; 602 } 603 $rewrite = pathurlencode($this->linkname) . '/'; 604 $plain = '/index.php?album=' . pathurlencode($this->name); 605 if ($page > 1) { 606 $rewrite .=_PAGE_ . '/' . $page . '/'; 607 $plain .= "&page=$page"; 608 } 609 return zp_apply_filter('getLink', rewrite_path($rewrite, $plain), $this, $page); 610 } 611 612 /** 613 * Delete the entire album PERMANENTLY. Be careful! This is unrecoverable. 614 * Returns true if successful 615 * 616 * @return bool 617 */ 618 function remove() { 619 $rslt = false; 620 if (PersistentObject::remove()) { 621 query("DELETE FROM " . prefix('options') . "WHERE `ownerid`=" . $this->id); 622 query("DELETE FROM " . prefix('comments') . "WHERE `type`='albums' AND `ownerid`=" . $this->id); 623 query("DELETE FROM " . prefix('obj_to_tag') . "WHERE `type`='albums' AND `objectid`=" . $this->id); 624 $rslt = true; 625 $filestoremove = safe_glob(substr($this->localpath, 0, -1) . '.*'); 626 foreach ($filestoremove as $file) { 627 if (in_array(strtolower(getSuffix($file)), $this->sidecars)) { 628 @chmod($file, 0777); 629 unlink($file); 630 } 631 } 632 $this->setUpdatedDateParents(); 633 } 634 return $rslt; 635 } 636 637 /** 638 * common album move code 639 * @param type $newfolder 640 * @return int 641 */ 642 protected function _move($newfolder) { 643 // First, ensure the new base directory exists. 644 $dest = ALBUM_FOLDER_SERVERPATH . internalToFilesystem($newfolder); 645 // Check to see if the destination already exists 646 if (file_exists($dest)) { 647 // Disallow moving an album over an existing one. 648 if (!(CASE_INSENSITIVE && strtolower($dest) == strtolower(rtrim($this->localpath, '/')))) { 649 return 3; 650 } 651 } 652 if(!$this->isValidMoveCopyDestination($newfolder)) { 653 // Disallow moving to a subfolder of the current folder. 654 return 4; 655 } 656 $filemask = substr($this->localpath, 0, -1) . '.*'; 657 $perms = FOLDER_MOD; 658 @chmod($this->localpath, 0777); 659 $success = @rename(rtrim($this->localpath, '/'), $dest); 660 @chmod($dest, $perms); 661 662 if ($success) { 663 $this->localpath = $dest . "/"; 664 $filestomove = safe_glob($filemask); 665 foreach ($filestomove as $file) { 666 if (in_array(strtolower(getSuffix($file)), $this->sidecars)) { 667 $d = stripslashes($dest) . '.' . getSuffix($file); 668 @chmod($file, 0777); 669 $success = $success && @rename($file, $d); 670 @chmod($d, FILE_MOD); 671 } 672 } 673 clearstatcache(); 674 $success = self::move($newfolder); 675 if ($success) { 676 // Update old parent(s) that "lost" an album! 677 $this->setUpdatedDateParents(); 678 $this->save(); 679 680 $this->updateParent($newfolder); 681 682 //rename the cache folder 683 $cacherename = @rename(SERVERCACHE . '/' . $this->name, SERVERCACHE . '/' . $newfolder); 684 return 0; 685 } 686 } 687 return 1; 688 } 689 690 691 692 /** 693 * Move this album to the location specified by $newfolder, copying all 694 * metadata, subalbums, and subalbums' metadata with it. 695 * @param $newfolder string the folder to move to, including the name of the current folder (possibly renamed). 696 * @return int 0 on success and error indicator on failure. 697 * 698 */ 699 function move($newfolder) { 700 return parent::move(array('folder' => $newfolder)); 701 } 702 703 /** 704 * Rename this album folder. Alias for move($newfoldername); 705 * @param string $newfolder the new folder name of this album (including subalbum paths) 706 * @return boolean true on success or false on failure. 707 */ 708 function rename($newfolder) { 709 return $this->move($newfolder); 710 } 711 712 protected function succeed($dest) { 713 return false; 714 } 715 716 /** 717 * Copy this album to the location specified by $newfolder, copying all 718 * metadata, subalbums, and subalbums' metadata with it. 719 * @param $newfolder string the folder to copy to, including the name of the current folder (possibly renamed). 720 * @return int 0 on success and error indicator on failure. 721 * 722 */ 723 function copy($newfolder) { 724 // album name to destination folder 725 if (substr($newfolder, -1, 1) != '/') { 726 $newfolder .= '/'; 727 } 728 $newfolder .= basename($this->localpath); 729 // First, ensure the new base directory exists. 730 $dest = ALBUM_FOLDER_SERVERPATH . internalToFilesystem($newfolder); 731 // Check to see if the destination directory already exists 732 if (file_exists($dest)) { 733 // Disallow moving an album over an existing one. 734 return 3; 735 } 736 if(!$this->isValidMoveCopyDestination($newfolder)) { 737 // Disallow copying to a subfolder of the current folder (infinite loop). 738 return 4; 739 } 740 $success = $this->succeed($dest); 741 $filemask = substr($this->localpath, 0, -1) . '.*'; 742 if ($success) { 743 // replicate the album metadata and sub-files 744 $uniqueset = array('folder' => $newfolder); 745 $parentname = dirname($newfolder); 746 if (empty($parentname) || $parentname == '/' || $parentname == '.') { 747 $uniqueset['parentid'] = NULL; 748 } else { 749 $parent = newAlbum($parentname); 750 $uniqueset['parentid'] = $parent->getID(); 751 } 752 $newID = parent::copy($uniqueset); 753 if ($newID) { 754 //replicate the tags 755 storeTags(readTags($this->getID(), 'albums'), $newID, 'albums'); 756 //copy the sidecar files 757 $filestocopy = safe_glob($filemask); 758 foreach ($filestocopy as $file) { 759 if (in_array(strtolower(getSuffix($file)), $this->sidecars)) { 760 $success = $success && @copy($file, dirname($dest) . '/' . basename($file)); 761 } 762 } 763 764 } 765 } 766 if ($success) { 767 $newalbum = newAlbum($newfolder); 768 $newalbum->setUpdatedDate(); 769 $newalbum->setUpdatedDateParents(); 770 return 0; 771 } else { 772 return 1; 773 } 774 } 775 776 /** 777 * Checks is the destination is not a subfolder of the current folder itself 778 * 779 * @since Zenphoto 1.5.5 780 * 781 * @param string $destination album name to move or copy to 782 * @return boolean 783 */ 784 function isValidMoveCopyDestination($destination) { 785 $oldfolders = explode('/', $this->name); 786 $newfolders = explode('/', $destination); 787 $sub = count($newfolders) > count($oldfolders); 788 if ($sub) { 789 for ($i = 0; $i < count($oldfolders); $i++) { 790 if ($newfolders[$i] != $oldfolders[$i]) { 791 $sub = false; 792 break; 793 } 794 } 795 if ($sub) { 796 // Disallow moving to a subfolder of the current folder. 797 return false; 798 } 799 } 800 return true; 801 } 802 803 /** 804 * For every image in the album, look for its file. Delete from the database 805 * if the file does not exist. Same for each sub-directory/album. 806 * 807 * @param bool $deep set to true for a thorough cleansing 808 */ 809 function garbageCollect($deep = false) { 810 811 } 812 813 /** 814 * Load all of the filenames that are found in this Albums directory on disk. 815 * Returns an array with all the names. 816 * 817 * @param $dirs Whether or not to return directories ONLY with the file array. 818 * @return array 819 */ 820 protected function loadFileNames($dirs = false) { 821 822 } 823 824 /** 825 * Returns true if the album is "dynamic" 826 * 827 * @return bool 828 */ 829 function isDynamic() { 830 return false; 831 } 832 833 /** 834 * Returns the search parameters for a dynamic album 835 * 836 * @return string 837 */ 838 function getSearchParams() { 839 return NULL; 840 } 841 842 /** 843 * Sets the search parameters of a dynamic album 844 * 845 * @param string $params The search string to produce the dynamic album 846 */ 847 function setSearchParams($params) { 848 849 } 850 851 /** 852 * Returns the search engine for a dynamic album 853 * 854 * @return object 855 */ 856 function getSearchEngine() { 857 return NULL; 858 } 859 860 /** 861 * checks access to the album 862 * @param bit $action What the requestor wants to do 863 * 864 * returns true of access is allowed 865 */ 866 function isMyItem($action) { 867 global $_zp_loggedin; 868 if ($parent = parent::isMyItem($action)) { 869 return $parent; 870 } 871 if (zp_loggedin($action)) { 872 $subRights = $this->albumSubRights(); 873 if (is_null($subRights)) { 874// no direct rights, but if this is a private gallery and the album is published he should be allowed to see it 875 if (GALLERY_SECURITY != 'public' && $this->isPublished() && $action == LIST_RIGHTS) { 876 return LIST_RIGHTS; 877 } 878 } else { 879 $albumrights = LIST_RIGHTS; 880 if ($subRights & (MANAGED_OBJECT_RIGHTS_EDIT)) { 881 $albumrights = $albumrights | ALBUM_RIGHTS; 882 } 883 if ($subRights & MANAGED_OBJECT_RIGHTS_UPLOAD) { 884 $albumrights = $albumrights | UPLOAD_RIGHTS; 885 } 886 if ($action & $albumrights) { 887 return ($_zp_loggedin ^ (ALBUM_RIGHTS | UPLOAD_RIGHTS)) | $albumrights; 888 } else { 889 return false; 890 } 891 } 892 } 893 return false; 894 } 895 896 /** 897 * Checks if guest is loggedin for the album 898 * @param unknown_type $hint 899 * @param unknown_type $show 900 */ 901 function checkforGuest(&$hint = NULL, &$show = NULL) { 902 if (!parent::checkForGuest()) { 903 return false; 904 } 905 return checkAlbumPassword($this, $hint); 906 } 907 908 /** 909 * 910 * returns true if there is any protection on the album 911 */ 912 function isProtected() { 913 return $this->checkforGuest() != 'zp_public_access'; 914 } 915 916 917 /** 918 * Returns true if this album is published and also all of its parents. 919 * 920 * @since Zenphoto 1.5.5 921 * 922 * @return bool 923 */ 924 function isPublic() { 925 if (is_null($this->is_public)) { 926 if (!$this->isPublished()) { 927 return $this->is_public = false; 928 } 929 $parent = $this->getParent(); 930 if($parent && !$parent->isPublic()) { 931 return $this->is_public = false; 932 } 933 return $this->is_public = true; 934 } else { 935 return $this->is_public; 936 } 937 } 938 939 /** 940 * Gets the owner of the album respectively of a parent album if not set specifically 941 * 942 * @global obj $_zp_authority 943 * @param bool $fullname Set to true to get the full name (if the owner is a vaild user of the site and has the full name defined) 944 * @return string 945 */ 946 function getOwner($fullname = false) { 947 global $_zp_authority; 948 $owner = $this->get('owner'); 949 if (empty($owner)) { 950 $p = $this->getParent(); 951 if (is_object($p)) { 952 $owner = $p->getOwner(); 953 } else { 954 $admin = $_zp_authority->getMasterUser(); 955 $owner = $admin->getUser(); 956 if ($fullname && !empty($admin->getName())) { 957 return $admin->getName(); 958 } 959 } 960 } else { 961 if ($fullname) { 962 return Zenphoto_Administrator::getNameByUser($owner); 963 } 964 } 965 return $owner; 966 } 967 968 function setOwner($owner) { 969 $this->set('owner', $owner); 970 } 971 972 /** 973 * 974 * Date at which the album last discovered an image 975 */ 976 function getUpdatedDate() { 977 return $this->get('updateddate'); 978 } 979 980 function setUpdatedDate($date = null) { 981 if(is_null($date)) { 982 $date = date('Y-m-d H:i:s'); 983 } 984 return $this->set('updateddate', $date); 985 } 986 987 /** 988 * Sets the current date to all parent albums of this album recursively 989 * @since Zenphoto 1.5.5 990 */ 991 function setUpdatedDateParents() { 992 $parent = $this->getParent(); 993 if($parent) { 994 $parent->setUpdatedDate(); 995 $parent->save(); 996 $parent->setUpdatedDateParents(); 997 } 998 } 999 1000 /** 1001 * Returns the theme for the album 1002 * 1003 * @return string 1004 */ 1005 function getAlbumTheme() { 1006 global $_zp_gallery; 1007 if (in_context(ZP_SEARCH_LINKED)) { 1008 return $_zp_gallery->getCurrentTheme(); 1009 } else { 1010 return $this->get('album_theme'); 1011 } 1012 } 1013 1014 /** 1015 * Sets the theme of the album 1016 * 1017 * @param string $theme 1018 */ 1019 function setAlbumTheme($theme) { 1020 $this->set('album_theme', $theme); 1021 } 1022 1023 /** 1024 * returns the album watermark 1025 * @return string 1026 */ 1027 function getWatermark() { 1028 return $this->get('watermark'); 1029 } 1030 1031 /** 1032 * Sets the album watermark 1033 * @param string $wm 1034 */ 1035 function setWatermark($wm) { 1036 $this->set('watermark', $wm); 1037 } 1038 1039 /** 1040 * Returns the album watermark thumb 1041 * 1042 * @return bool 1043 */ 1044 function getWatermarkThumb() { 1045 return $this->get('watermark_thumb'); 1046 } 1047 1048 /** 1049 * Sets the custom watermark usage 1050 * 1051 * @param $wm 1052 */ 1053 function setWatermarkThumb($wm) { 1054 $this->set('watermark_thumb', $wm); 1055 } 1056 1057 /** 1058 * returns the mitigated album rights. 1059 * returns NULL if not a managed album 1060 */ 1061 function albumSubRights() { 1062 if (!is_null($this->subrights)) { 1063 return $this->subrights; 1064 } 1065 global $_zp_admin_album_list; 1066 if (zp_loggedin(MANAGE_ALL_ALBUM_RIGHTS)) { 1067 $this->subrights = MANAGED_OBJECT_RIGHTS_EDIT | MANAGED_OBJECT_RIGHTS_UPLOAD | MANAGED_OBJECT_RIGHTS_VIEW; 1068 return $this->subrights; 1069 } 1070 if (zp_loggedin(VIEW_UNPUBLISHED_RIGHTS)) { 1071 $base = MANAGED_OBJECT_RIGHTS_VIEW; 1072 } else { 1073 $base = NULL; 1074 } 1075 getManagedAlbumList(); 1076 if (count($_zp_admin_album_list) > 0) { 1077 $desired_folders = explode('/', $this->name); 1078 foreach ($_zp_admin_album_list as $adminalbum => $rights) { 1079// see if it is one of the managed folders or a subfolder there of 1080 $admin_folders = explode('/', $adminalbum); 1081 $level = 0; 1082 $ok = true; 1083 foreach ($admin_folders as $folder) { 1084 if ($level >= count($desired_folders) || $folder != $desired_folders[$level]) { 1085 $ok = false; 1086 break; 1087 } 1088 $level++; 1089 } 1090 if ($ok) { 1091 $this->subrights = $rights | $base; 1092 return $this->subrights; 1093 } 1094 } 1095 } 1096 $this->subrights = $base; 1097 return $this->subrights; 1098 } 1099 1100 /** 1101 * sortImageArray will sort an array of Images based on the given key. The 1102 * key must be one of (filename, title, sort_order) at the moment. 1103 * 1104 * @param array $images The array of filenames to be sorted. 1105 * @param string $sorttype optional sort type 1106 * @param string $sortdirection optional sort direction 1107 * @param bool $mine set to true/false to override ownership clause 1108 * @return array 1109 */ 1110 protected function sortImageArray($images, $sorttype, $sortdirection, $mine = NULL) { 1111 if (is_null($mine)) { 1112 $mine = $this->isMyItem(LIST_RIGHTS | MANAGE_ALL_ALBUM_RIGHTS); 1113 } 1114 if ($mine && !($mine & (MANAGE_ALL_ALBUM_RIGHTS))) { 1115 //check for managed album view unpublished image rights 1116 $mine = $this->albumSubRights() & (MANAGED_OBJECT_RIGHTS_EDIT | MANAGED_OBJECT_RIGHTS_VIEW); 1117 } 1118 $sortkey = $this->getImageSortKey($sorttype); 1119 if (($sortkey == '`sort_order`') || ($sortkey == 'RAND()')) { 1120 // manual sort is always ascending 1121 $order = false; 1122 } else { 1123 if (!is_null($sortdirection)) { 1124 $order = strtoupper($sortdirection) == 'DESC'; 1125 } else { 1126 $order = $this->getSortDirection('image'); 1127 } 1128 } 1129 $result = query($sql = "SELECT * FROM " . prefix("images") . " WHERE `albumid`= " . $this->getID() . ' ORDER BY ' . $sortkey . ' ' . $sortdirection); 1130 $results = array(); 1131 while ($row = db_fetch_assoc($result)) { 1132 $filename = $row['filename']; 1133 if (($key = array_search($filename, $images)) !== false) { 1134 // the image exists in the filesystem 1135 $results[] = $row; 1136 unset($images[$key]); 1137 } else { // the image no longer exists 1138 $id = $row['id']; 1139 query("DELETE FROM " . prefix('images') . " WHERE `id`=$id"); // delete the record 1140 query("DELETE FROM " . prefix('comments') . " WHERE `type` ='images' AND `ownerid`= '$id'"); // remove image comments 1141 } 1142 } 1143 db_free_result($result); 1144 foreach ($images as $filename) { 1145 // these images are not in the database 1146 $imageobj = newImage($this, $filename); 1147 $results[] = $imageobj->getData(); 1148 } 1149 // now put the results into the right order 1150 $results = sortByKey($results, str_replace('`', '', $sortkey), $order); 1151 // the results are now in the correct order 1152 $images_ordered = array(); 1153 foreach ($results as $key => $row) { 1154 // check for visible 1155 switch (themeObject::checkScheduledPublishing($row)) { 1156 case 1: 1157 $imageobj = newImage($this, $row['filename']); 1158 $imageobj->setShow(0); 1159 $imageobj->save(); 1160 case 2: 1161 $row['show'] = 0; 1162 break; 1163 } 1164 if ($row['show'] || $mine) { 1165 // don't display it 1166 $images_ordered[] = $row['filename']; 1167 } 1168 } 1169 return $images_ordered; 1170 } 1171 1172 /** 1173 * changes the parent of an album for move/copy 1174 * 1175 * @param string $newfolder The folder name of the new parent 1176 */ 1177 protected function updateParent($newfolder) { 1178 $this->name = $newfolder; 1179 $parentname = dirname($newfolder); 1180 if ($parentname == '/' || $parentname == '.') 1181 $parentname = ''; 1182 if (empty($parentname)) { 1183 $this->set('parentid', NULL); 1184 } else { 1185 $parent = newAlbum($parentname); 1186 $this->set('parentid', $parent->getID()); 1187 } 1188 $this->setUpdatedDateParents(); 1189 $this->save(); 1190 } 1191 1192 /** 1193 * Simply creates objects of all the images and sub-albums in this album to 1194 * load accurate values into the database. 1195 */ 1196 function preLoad() { 1197 $images = $this->getImages(0); 1198 $subalbums = $this->getAlbums(0); 1199 foreach ($subalbums as $dir) { 1200 $album = newAlbum($dir); 1201 $album->preLoad(); 1202 } 1203 } 1204 1205 /** 1206 * Returns the album following the current album 1207 * 1208 * @return object 1209 */ 1210 function getNextAlbum() { 1211 global $_zp_gallery; 1212 if (is_null($parent = $this->getParent())) { 1213 $albums = $_zp_gallery->getAlbums(0); 1214 } else { 1215 $albums = $parent->getAlbums(0); 1216 } 1217 $inx = array_search($this->name, $albums) + 1; 1218 if ($inx >= 0 && $inx < count($albums)) { 1219 return newAlbum($albums[$inx]); 1220 } 1221 return null; 1222 } 1223 1224 /** 1225 * Returns the album prior to the current album 1226 * 1227 * @return object 1228 */ 1229 function getPrevAlbum() { 1230 global $_zp_gallery; 1231 if (is_null($parent = $this->getParent())) { 1232 $albums = $_zp_gallery->getAlbums(0); 1233 } else { 1234 $albums = $parent->getAlbums(0); 1235 } 1236 $inx = array_search($this->name, $albums) - 1; 1237 if ($inx >= 0 && $inx < count($albums)) { 1238 return newAlbum($albums[$inx]); 1239 } 1240 return null; 1241 } 1242 1243 /** 1244 * Returns the page number in the gallery or the parent album of this album 1245 * 1246 * @return int 1247 */ 1248 function getGalleryPage() { 1249 global $_zp_gallery; 1250 if ($this->index == null) { 1251 if (is_null($parent = $this->getParent())) { 1252 $albums = $_zp_gallery->getAlbums(0); 1253 } else { 1254 $albums = $parent->getAlbums(0); 1255 } 1256 $this->index = array_search($this->name, $albums); 1257 } 1258 return floor(($this->index / galleryAlbumsPerPage()) + 1); 1259 } 1260 1261} 1262 1263/** 1264 * Album Class 1265 * @package core 1266 * @subpackage classes\objects 1267 */ 1268class Album extends AlbumBase { 1269 1270 /** 1271 * Constructor for albums 1272 * 1273 * @param object $gallery The parent gallery: deprecated 1274 * @param string $folder8 folder name (UTF8) of the album 1275 * @param bool $cache load from cache if present 1276 * @return Album 1277 */ 1278 function __construct($folder8, $cache = true, $quiet = false) { 1279 $folder8 = trim($folder8, '/'); 1280 $folderFS = internalToFilesystem($folder8); 1281 $localpath = ALBUM_FOLDER_SERVERPATH . $folderFS . "/"; 1282 $this->linkname = $this->name = $folder8; 1283 $this->localpath = $localpath; 1284 if (!$this->_albumCheck($folder8, $folderFS, $quiet)) 1285 return; 1286 1287 $new = $this->instantiate('albums', array('folder' => $this->name), 'folder', $cache, empty($folder8)); 1288 1289 if ($new) { 1290 $this->setUpdatedDateParents(); 1291 $this->save(); 1292 zp_apply_filter('new_album', $this); 1293 } 1294 zp_apply_filter('album_instantiate', $this); 1295 } 1296 1297 /** 1298 * album validity check 1299 * @return boolean 1300 */ 1301 protected function _albumCheck($folder8, $folderFS, $quiet) { 1302 $msg = false; 1303 if (empty($folder8)) { 1304 $msg = gettext('Invalid album instantiation: No album name'); 1305 } else if (filesystemToInternal($folderFS) != $folder8) { 1306 // an attempt to spoof the album name. 1307 $msg = sprintf(gettext('Invalid album instantiation: %1$s!=%2$s'), html_encode(filesystemToInternal($folderFS)), html_encode($folder8)); 1308 } else if (!file_exists($this->localpath) || !(is_dir($this->localpath)) || $folder8[0] == '.' || preg_match('~/\.*/~', $folder8)) { 1309 $msg = sprintf(gettext('Invalid album instantiation: %s does not exist.'), html_encode($folder8)); 1310 } 1311 if ($msg) { 1312 $this->exists = false; 1313 if (!$quiet) { 1314 trigger_error($msg, E_USER_ERROR); 1315 } 1316 return false; 1317 } 1318 return true; 1319 } 1320 1321 /** 1322 * Sets default values for a new album 1323 * 1324 * @return bool 1325 */ 1326 protected function setDefaults() { 1327 global $_zp_gallery; 1328 // Set default data for a new Album (title and parent_id) 1329 parent::setDefaults(); 1330 $parentalbum = $this->getParent(); 1331 $this->set('mtime', filemtime($this->localpath)); 1332 if (!$_zp_gallery->getAlbumUseImagedate()) { 1333 $this->setDateTime(strftime('%Y-%m-%d %H:%M:%S', $this->get('mtime'))); 1334 } 1335 $title = trim($this->name); 1336 if (!is_null($parentalbum)) { 1337 $this->set('parentid', $parentalbum->getID()); 1338 $title = substr($title, strrpos($title, '/') + 1); 1339 } 1340 $this->set('title', sanitize($title, 2)); 1341 return true; 1342 } 1343 1344 /** 1345 * Guts of fetching the subalbums 1346 * @return array 1347 */ 1348 protected function _getAlbums() { 1349 $dirs = $this->loadFileNames(true); 1350 $subalbums = array(); 1351 foreach ($dirs as $dir) { 1352 $dir = $this->name . '/' . $dir; 1353 $subalbums[] = $dir; 1354 } 1355 return $subalbums; 1356 } 1357 1358 /** 1359 * Returns all folder names for all the subdirectories. 1360 * 1361 * @param string $page Which page of subalbums to display. 1362 * @param string $sorttype The sort strategy 1363 * @param string $sortdirection The direction of the sort 1364 * @param bool $care set to false if the order does not matter 1365 * @param bool $mine set true/false to override ownership 1366 * @return array 1367 */ 1368 function getAlbums($page = 0, $sorttype = null, $sortdirection = null, $care = true, $mine = NULL) { 1369 global $_zp_gallery; 1370 if (!$this->exists) 1371 return array(); 1372 if ($mine || is_null($this->subalbums) || $care && $sorttype . $sortdirection !== $this->lastsubalbumsort) { 1373 if (is_null($sorttype)) { 1374 $sorttype = $this->getSortType('album'); 1375 } 1376 if (is_null($sortdirection)) { 1377 if ($this->getSortDirection('album')) { 1378 $sortdirection = 'DESC'; 1379 } 1380 } 1381 $dirs = $this->loadFileNames(true); 1382 $subalbums = array(); 1383 foreach ($dirs as $dir) { 1384 $dir = $this->name . '/' . $dir; 1385 $subalbums[] = $dir; 1386 } 1387 $key = $this->getAlbumSortKey($sorttype); 1388 $this->subalbums = $_zp_gallery->sortAlbumArray($this, $subalbums, $key, $sortdirection, $mine); 1389 $this->lastsubalbumsort = $sorttype . $sortdirection; 1390 } 1391 return parent::getAlbums($page); 1392 } 1393 1394 /** 1395 * Returns a of a slice of the images for this album. They will 1396 * also be sorted according to the sort type of this album, or by filename if none 1397 * has been set. 1398 * 1399 * @param int $page Which page of images should be returned. If zero, all images are returned. 1400 * @param int $firstPageCount count of images that go on the album/image transition page 1401 * @param string $sorttype optional sort type 1402 * @param string $sortdirection optional sort direction 1403 * @param bool $care set to false if the order of the images does not matter 1404 * @param bool $mine set true/false to override ownership 1405 * 1406 * @return array 1407 */ 1408 function getImages($page = 0, $firstPageCount = 0, $sorttype = null, $sortdirection = null, $care = true, $mine = NULL) { 1409 if (!$this->exists) 1410 return array(); 1411 if ($mine || is_null($this->images) || $care && $sorttype . $sortdirection !== $this->lastimagesort) { 1412 if (is_null($sorttype)) { 1413 $sorttype = $this->getSortType(); 1414 } 1415 if (is_null($sortdirection)) { 1416 if ($this->getSortDirection('image')) { 1417 $sortdirection = 'DESC'; 1418 } 1419 } 1420 $images = $this->loadFileNames(); 1421 $this->images = $this->sortImageArray($images, $sorttype, $sortdirection, $mine); 1422 $this->lastimagesort = $sorttype . $sortdirection; 1423 } 1424 return parent::getImages($page, $firstPageCount); 1425 } 1426 1427 /** 1428 * Delete the entire album PERMANENTLY. Be careful! This is unrecoverable. 1429 * Returns true if successful 1430 * 1431 * @return bool 1432 */ 1433 function remove() { 1434 $rslt = false; 1435 if (PersistentObject::remove()) { 1436 foreach ($this->getImages() as $filename) { 1437 $image = newImage($this, $filename); 1438 $image->remove(); 1439 } 1440 foreach ($this->getAlbums() as $folder) { 1441 $subalbum = newAlbum($folder); 1442 $subalbum->remove(); 1443 } 1444 $curdir = getcwd(); 1445 chdir($this->localpath); 1446 $filelist = safe_glob('*'); 1447 foreach ($filelist as $file) { 1448 if (($file != '.') && ($file != '..')) { 1449 @chmod($file, 0777); 1450 unlink($this->localpath . $file); // clean out any other files in the folder 1451 } 1452 } 1453 chdir($curdir); 1454 clearstatcache(); 1455 query("DELETE FROM " . prefix('options') . "WHERE `ownerid`=" . $this->id); 1456 query("DELETE FROM " . prefix('comments') . "WHERE `type`='albums' AND `ownerid`=" . $this->id); 1457 query("DELETE FROM " . prefix('obj_to_tag') . "WHERE `type`='albums' AND `objectid`=" . $this->id); 1458 $success = true; 1459 $filestoremove = safe_glob(substr($this->localpath, 0, strrpos($this->localpath, '.')) . '.*'); 1460 foreach ($filestoremove as $file) { 1461 if (in_array(strtolower(getSuffix($file)), $this->sidecars)) { 1462 @chmod($file, 0777); 1463 $success = $success && unlink($file); 1464 } 1465 } 1466 @chmod($this->localpath, 0777); 1467 $rslt = @rmdir($this->localpath) && $success; 1468 $cachepath = SERVERCACHE . '/' . pathurlencode($this->name) . '/'; 1469 @chmod($cachepath, 0777); 1470 @rmdir($cachepath); 1471 $this->setUpdatedDateParents(); 1472 } 1473 clearstatcache(); 1474 return $rslt; 1475 } 1476 1477 /** 1478 * Move this album to the location specified by $newfolder, copying all 1479 * metadata, subalbums, and subalbums' metadata with it. 1480 * @param $newfolder string the folder to move to, including the name of the current folder (possibly renamed). 1481 * @return int 0 on success and error indicator on failure. 1482 * 1483 */ 1484 function move($newfolder) { 1485 $oldfolder = $this->name; 1486 $rslt = $this->_move($newfolder); 1487 if (!$rslt) { 1488 // Then: go through the db and change the album (and subalbum) paths. No ID changes are necessary for a move. 1489 // Get the subalbums. 1490 $sql = "SELECT id, folder FROM " . prefix('albums') . " WHERE folder LIKE " . db_quote(db_LIKE_escape($oldfolder) . '/%'); 1491 $result = query($sql); 1492 if ($result) { 1493 while ($subrow = db_fetch_assoc($result)) { 1494 $newsubfolder = $subrow['folder']; 1495 $newsubfolder = $newfolder . substr($newsubfolder, strlen($oldfolder)); 1496 $sql = "UPDATE " . prefix('albums') . " SET folder=" . db_quote($newsubfolder) . " WHERE id=" . $subrow['id']; 1497 query($sql); 1498 } 1499 } 1500 db_free_result($result); 1501 return 0; 1502 } 1503 return $rslt; 1504 } 1505 1506 protected function succeed($dest) { 1507 return mkdir_recursive($dest, FOLDER_MOD) === TRUE; 1508 } 1509 1510 /** 1511 * Copy this album to the location specified by $newfolder, copying all 1512 * metadata, subalbums, and subalbums' metadata with it. 1513 * @param $newfolder string the folder to copy to, including the name of the current folder (possibly renamed). 1514 * @return int 0 on success and error indicator on failure. 1515 * 1516 */ 1517 function copy($newfolder) { 1518 $rslt = parent::copy($newfolder); 1519 if (!$rslt) { 1520 $newfolder .= '/' . basename($this->name); 1521 $success = true; 1522 //copy the images 1523 $images = $this->getImages(0); 1524 foreach ($images as $imagename) { 1525 $image = newImage($this, $imagename); 1526 if ($rslt = $image->copy($newfolder)) { 1527 $success = false; 1528 } 1529 } 1530 // copy the subalbums. 1531 $subalbums = $this->getAlbums(0); 1532 foreach ($subalbums as $subalbumname) { 1533 $subalbum = newAlbum($subalbumname); 1534 if ($rslt = $subalbum->copy($newfolder)) { 1535 $success = false; 1536 } 1537 } 1538 if ($success) { 1539 return 0; 1540 } 1541 return 1; 1542 } 1543 return $rslt; 1544 } 1545 1546 /** 1547 * For every image in the album, look for its file. Delete from the database 1548 * if the file does not exist. Same for each sub-directory/album. 1549 * 1550 * @param bool $deep set to true for a thorough cleansing 1551 */ 1552 function garbageCollect($deep = false) { 1553 $set_updateddate = false; 1554 if (is_null($this->images)) 1555 $this->getImages(); 1556 $result = query("SELECT `id`, `filename` FROM " . prefix('images') . " WHERE `albumid` = '" . $this->id . "'"); 1557 $dead = array(); 1558 $live = array(); 1559 1560 $files = $this->loadFileNames(); 1561 1562 // Does the filename from the db row match any in the files on disk? 1563 while ($row = db_fetch_assoc($result)) { 1564 if (!in_array($row['filename'], $files)) { 1565 // In the database but not on disk. Kill it. 1566 $dead[] = $row['id']; 1567 } else if (in_array($row['filename'], $live)) { 1568 // Duplicate in the database. Kill it. 1569 $dead[] = $row['id']; 1570 // Do something else here? Compare titles/descriptions/metadata/update dates to see which is the latest? 1571 } else { 1572 $live[] = $row['filename']; 1573 } 1574 } 1575 db_free_result($result); 1576 1577 if (count($dead) > 0) { 1578 $sql = "DELETE FROM " . prefix('images') . " WHERE `id` IN(" . implode(',', $dead) . ")"; 1579 $sql2 = "DELETE FROM " . prefix('comments') . " WHERE `type`='albums' AND `ownerid` IN(" . implode(',', $dead) . ")"; 1580 query($sql); 1581 query($sql2); 1582 $set_updateddate = true; 1583 } 1584 1585 // Get all sub-albums and make sure they exist. 1586 $result = query("SELECT `id`, `folder` FROM " . prefix('albums') . " WHERE `folder` LIKE " . db_quote(db_LIKE_escape($this->name) . '%')); 1587 $dead = array(); 1588 $live = array(); 1589 // Does the dirname from the db row exist on disk? 1590 while ($row = db_fetch_assoc($result)) { 1591 if (!is_dir(ALBUM_FOLDER_SERVERPATH . internalToFilesystem($row['folder'])) || in_array($row['folder'], $live) || substr($row['folder'], -1) == '/' || substr($row['folder'], 0, 1) == '/') { 1592 $dead[] = $row['id']; 1593 } else { 1594 $live[] = $row['folder']; 1595 } 1596 } 1597 db_free_result($result); 1598 if (count($dead) > 0) { 1599 $sql = "DELETE FROM " . prefix('albums') . " WHERE `id` IN(" . implode(',', $dead) . ")"; 1600 $sql2 = "DELETE FROM " . prefix('comments') . " WHERE `type`='albums' AND `ownerid` IN(" . implode(',', $dead) . ")"; 1601 query($sql); 1602 query($sql2); 1603 $set_updateddate = true; 1604 } 1605 if($set_updateddate) { 1606 $this->setUpdateddate(); 1607 $this->save(); 1608 $this->setUpdatedDateParents(); 1609 } 1610 1611 if ($deep) { 1612 foreach ($this->getAlbums(0) as $dir) { 1613 $subalbum = newAlbum($dir); 1614 // Could have been deleted if it didn't exist above... 1615 if ($subalbum->exists) 1616 $subalbum->garbageCollect($deep); 1617 } 1618 } 1619 } 1620 1621 /** 1622 * Load all of the filenames that are found in this Albums directory on disk. 1623 * Returns an array with all the names. 1624 * 1625 * @param $dirs Whether or not to return directories ONLY with the file array. 1626 * @return array 1627 */ 1628 protected function loadFileNames($dirs = false) { 1629 clearstatcache(); 1630 $albumdir = $this->localpath; 1631 $dir = @opendir($albumdir); 1632 if (!$dir) { 1633 if (is_dir($albumdir)) { 1634 $msg = sprintf(gettext("Error: The album %s is not readable."), html_encode($this->name)); 1635 } else { 1636 $msg = sprintf(gettext("Error: The album named %s cannot be found."), html_encode($this->name)); 1637 } 1638 trigger_error($msg, E_USER_NOTICE); 1639 return array(); 1640 } 1641 1642 $files = array(); 1643 $others = array(); 1644 1645 while (false !== ($file = readdir($dir))) { 1646 $file8 = filesystemToInternal($file); 1647 if (@$file8[0] != '.') { 1648 if ($dirs && (is_dir($albumdir . $file) || hasDynamicAlbumSuffix($file))) { 1649 $files[] = $file8; 1650 } else if (!$dirs && is_file($albumdir . $file)) { 1651 if (Gallery::validImageAlt($file)) { 1652 $files[] = $file8; 1653 $others[] = $file8; 1654 } else if (Gallery::validImage($file)) { 1655 $files[] = $file8; 1656 } 1657 } 1658 } 1659 } 1660 closedir($dir); 1661 if (count($others) > 0) { 1662 $others_thumbs = array(); 1663 foreach ($others as $other) { 1664 $others_root = substr($other, 0, strrpos($other, ".")); 1665 foreach ($files as $image) { 1666 if ($image != $other) { 1667 $image_root = substr($image, 0, strrpos($image, ".")); 1668 if ($image_root == $others_root && Gallery::validImage($image)) { 1669 $others_thumbs[] = $image; 1670 } 1671 } 1672 } 1673 } 1674 $files = array_diff($files, $others_thumbs); 1675 } 1676 1677 if ($dirs) { 1678 return zp_apply_filter('album_filter', $files); 1679 } else { 1680 return zp_apply_filter('image_filter', $files); 1681 } 1682 } 1683 1684 1685} 1686 1687/** 1688 * Dynamic Album Class for "saved searches" 1689 * @package core 1690 * @subpackage classes\objects 1691 */ 1692class dynamicAlbum extends AlbumBase { 1693 1694 public $searchengine; // cache the search engine for dynamic albums 1695 1696 function __construct($folder8, $cache = true, $quiet = false) { 1697 $folder8 = trim($folder8, '/'); 1698 $folderFS = internalToFilesystem($folder8); 1699 $localpath = ALBUM_FOLDER_SERVERPATH . $folderFS . "/"; 1700 $this->linkname = $this->name = $folder8; 1701 $this->localpath = $localpath; 1702 if (!$this->_albumCheck($folder8, $folderFS, $quiet)) 1703 return; 1704 $this->instantiate('albums', array('folder' => $this->name), 'folder', $cache, empty($folder8)); 1705 $this->exists = true; 1706 if (!is_dir(stripSuffix($this->localpath))) { 1707 $this->linkname = stripSuffix($folder8); 1708 } 1709 $new = !$this->get('search_params'); 1710 if ($new || (filemtime($this->localpath) > $this->get('mtime'))) { 1711 $constraints = ''; 1712 $data = file_get_contents($this->localpath); 1713 while (!empty($data)) { 1714 $data1 = trim(substr($data, 0, $i = strpos($data, "\n"))); 1715 if ($i === false) { 1716 $data1 = $data; 1717 $data = ''; 1718 } else { 1719 $data = substr($data, $i + 1); 1720 } 1721 if (strpos($data1, 'WORDS=') !== false) { 1722 $words = "words=" . urlencode(substr($data1, 6)); 1723 } 1724 if (strpos($data1, 'THUMB=') !== false) { 1725 $thumb = trim(substr($data1, 6)); 1726 $this->set('thumb', $thumb); 1727 } 1728 if (strpos($data1, 'FIELDS=') !== false) { 1729 $fields = "&searchfields=" . trim(substr($data1, 7)); 1730 } 1731 if (strpos($data1, 'CONSTRAINTS=') !== false) { 1732 $constraint = trim(substr($data1, 12)); 1733 $constraints = '&' . $constraint; 1734 } 1735 } 1736 if (!empty($words)) { 1737 if (empty($fields)) { 1738 $fields = '&searchfields=tags'; 1739 } 1740 $this->set('search_params', $words . $fields . $constraints); 1741 } 1742 $this->set('mtime', filemtime($this->localpath)); 1743 if ($new) { 1744 $title = $this->get('title'); 1745 $this->set('title', stripSuffix($title)); // Strip the suffix 1746 $this->save(); 1747 zp_apply_filter('new_album', $this); 1748 } 1749 } 1750 zp_apply_filter('album_instantiate', $this); 1751 } 1752 1753 /** 1754 * album validity check 1755 * @param type $folder8 1756 * @return boolean 1757 */ 1758 protected function _albumCheck($folder8, $folderFS, $quiet) { 1759 $this->localpath = rtrim($this->localpath, '/'); 1760 1761 $msg = false; 1762 if (empty($folder8)) { 1763 $msg = gettext('Invalid album instantiation: No album name'); 1764 } else if (filesystemToInternal($folderFS) != $folder8) { 1765 // an attempt to spoof the album name. 1766 $msg = sprintf(gettext('Invalid album instantiation: %1$s!=%2$s'), html_encode(filesystemToInternal($folderFS)), html_encode($folder8)); 1767 } else if (!file_exists($this->localpath) || is_dir($this->localpath)) { 1768 $msg = sprintf(gettext('Invalid album instantiation: %s does not exist.'), html_encode($folder8)); 1769 } 1770 if ($msg) { 1771 $this->exists = false; 1772 if (!$quiet) { 1773 trigger_error($msg, E_USER_ERROR); 1774 } 1775 return false; 1776 } 1777 return true; 1778 } 1779 1780 /** 1781 * Returns all folder names for all the subdirectories. 1782 * 1783 * @param string $page Which page of subalbums to display. 1784 * @param string $sorttype The sort strategy 1785 * @param string $sortdirection The direction of the sort 1786 * @param bool $care set to false if the order does not matter 1787 * @param bool $mine set true/false to override ownership 1788 * @return array 1789 */ 1790 function getAlbums($page = 0, $sorttype = null, $sortdirection = null, $care = true, $mine = NULL) { 1791 global $_zp_gallery; 1792 if (!$this->exists) 1793 return array(); 1794 if ($mine || is_null($this->subalbums) || $care && $sorttype . $sortdirection !== $this->lastsubalbumsort) { 1795 if (is_null($sorttype)) { 1796 $sorttype = $this->getSortType('album'); 1797 } 1798 if (is_null($sortdirection)) { 1799 if ($this->getSortDirection('album')) { 1800 $sortdirection = 'DESC'; 1801 } else { 1802 $sortdirection = ''; 1803 } 1804 } 1805 $searchengine = $this->getSearchEngine(); 1806 $subalbums = $searchengine->getAlbums(0, $sorttype, $sortdirection, $care, $mine); 1807 $key = $this->getAlbumSortKey($sorttype); 1808 $this->subalbums = $_zp_gallery->sortAlbumArray($this, $subalbums, $key, $sortdirection, $mine); 1809 $this->lastsubalbumsort = $sorttype . $sortdirection; 1810 } 1811 return parent::getAlbums($page); 1812 } 1813 1814 /** 1815 * Returns the search parameters for a dynamic album 1816 * 1817 * @return string 1818 */ 1819 function getSearchParams() { 1820 return $this->get('search_params'); 1821 } 1822 1823 /** 1824 * Sets the search parameters of a dynamic album 1825 * 1826 * @param string $params The search string to produce the dynamic album 1827 */ 1828 function setSearchParams($params) { 1829 $this->set('search_params', $params); 1830 } 1831 1832 /** 1833 * Returns the search engine for a dynamic album 1834 * 1835 * @return object 1836 */ 1837 function getSearchEngine() { 1838 if (!is_null($this->searchengine)) 1839 return $this->searchengine; 1840 $this->searchengine = new SearchEngine(true); 1841 $params = $this->get('search_params'); 1842 $params .= '&albumname=' . $this->name; 1843 $this->searchengine->setSearchParams($params); 1844 return $this->searchengine; 1845 } 1846 1847 /** 1848 * Returns a of a slice of the images for this album. They will 1849 * also be sorted according to the sort type of this album, or by filename if none 1850 * has been set. 1851 * 1852 * @param int $page Which page of images should be returned. If zero, all images are returned. 1853 * @param int $firstPageCount count of images that go on the album/image transition page 1854 * @param string $sorttype optional sort type 1855 * @param string $sortdirection optional sort direction 1856 * @param bool $care set to false if the order of the images does not matter 1857 * @param bool $mine set true/false to override ownership 1858 * 1859 * @return array 1860 */ 1861 function getImages($page = 0, $firstPageCount = 0, $sorttype = null, $sortdirection = null, $care = true, $mine = NULL) { 1862 if (!$this->exists) 1863 return array(); 1864 if ($mine || is_null($this->images) || $care && $sorttype . $sortdirection !== $this->lastimagesort) { 1865 if (is_null($sorttype)) { 1866 $sorttype = $this->getSortType(); 1867 } 1868 if (is_null($sortdirection)) { 1869 if ($this->getSortDirection('image')) { 1870 $sortdirection = 'DESC'; 1871 } 1872 } 1873 $searchengine = $this->getSearchEngine(); 1874 $this->images = $searchengine->getImages(0, 0, $sorttype, $sortdirection, $care, $mine); 1875 $this->lastimagesort = $sorttype . $sortdirection; 1876 } 1877 return parent::getImages($page, $firstPageCount); 1878 } 1879 1880 /** 1881 * Delete the entire album PERMANENTLY. Be careful! This is unrecoverable. 1882 * Returns true if successful 1883 * 1884 * @return bool 1885 */ 1886 function remove() { 1887 if ($rslt = parent::remove()) { 1888 @chmod($this->localpath, 0777); 1889 $rslt = @unlink($this->localpath); 1890 clearstatcache(); 1891 } 1892 return $rslt; 1893 } 1894 1895 /** 1896 * Move this album to the location specified by $newfolder, copying all 1897 * metadata, subalbums, and subalbums' metadata with it. 1898 * @param $newfolder string the folder to move to, including the name of the current folder (possibly renamed). 1899 * @return int 0 on success and error indicator on failure. 1900 * 1901 */ 1902 function move($newfolder,$oldfolder="") { 1903 return $this->_move($newfolder); 1904 } 1905 1906 protected function succeed($dest) { 1907 return @copy($this->localpath, $dest); 1908 } 1909 1910 /** 1911 * Copy this album to the location specified by $newfolder, copying all 1912 * metadata, subalbums, and subalbums' metadata with it. 1913 * @param $newfolder string the folder to copy to, including the name of the current folder (possibly renamed). 1914 * @return int 0 on success and error indicator on failure. 1915 * 1916 */ 1917 function copy($newfolder) { 1918 return parent::copy($newfolder); 1919 } 1920 1921 /** 1922 * Simply creates objects of all the images and sub-albums in this album to 1923 * load accurate values into the database. 1924 */ 1925 function preLoad() { 1926 return; // nothing to do 1927 } 1928 1929 protected function loadFileNames($dirs = false) { 1930 return array(); 1931 } 1932 1933 function isDynamic() { 1934 return 'alb'; 1935 } 1936 1937 /** 1938 * Sets default values for a new album 1939 * 1940 * @return bool 1941 */ 1942 protected function setDefaults() { 1943 global $_zp_gallery; 1944 // Set default data for a new Album (title and parent_id) 1945 parent::setDefaults(); 1946 $parentalbum = $this->getParent(); 1947 $this->set('mtime', filemtime($this->localpath)); 1948 if (!$_zp_gallery->getAlbumUseImagedate()) { 1949 $this->setDateTime(strftime('%Y-%m-%d %H:%M:%S', $this->get('mtime'))); 1950 } 1951 $title = trim($this->name); 1952 if (!is_null($parentalbum)) { 1953 $this->set('parentid', $parentalbum->getID()); 1954 $title = substr($title, strrpos($title, '/') + 1); 1955 } 1956 $this->set('title', sanitize($title, 2)); 1957 return true; 1958 } 1959 1960} 1961 1962?> 1963