Overview

Packages

  • Ansel
  • None

Classes

  • Ansel
  • Ansel_Ajax_Application
  • Ansel_Ajax_Imple_EditCaption
  • Ansel_Ajax_Imple_EditFaces
  • Ansel_Ajax_Imple_EditGalleryFaces
  • Ansel_Ajax_Imple_Embed
  • Ansel_Ajax_Imple_GallerySlugCheck
  • Ansel_Ajax_Imple_ImageSaveGeotag
  • Ansel_Ajax_Imple_LocationAutoCompleter
  • Ansel_Ajax_Imple_MapLayerSelect
  • Ansel_Ajax_Imple_TagActions
  • Ansel_Ajax_Imple_ToggleGalleryActions
  • Ansel_Ajax_Imple_ToggleOtherGalleries
  • Ansel_Ajax_Imple_UploadNotification
  • Ansel_Api
  • Ansel_Exception
  • Ansel_Faces
  • Ansel_Faces_Base
  • Ansel_Faces_Facedetect
  • Ansel_Faces_User
  • Ansel_Factory_Faces
  • Ansel_Factory_Storage
  • Ansel_Factory_Styles
  • Ansel_Form_Ecard
  • Ansel_Form_Image
  • Ansel_Form_ImageDate
  • Ansel_Form_Upload
  • Ansel_Gallery
  • Ansel_Gallery_Decorator_Date
  • Ansel_GalleryMode_Base
  • Ansel_GalleryMode_Date
  • Ansel_GalleryMode_Normal
  • Ansel_Image
  • Ansel_ImageGenerator
  • Ansel_ImageGenerator_Mini
  • Ansel_ImageGenerator_PolaroidThumb
  • Ansel_ImageGenerator_PolaroidThumbStack
  • Ansel_ImageGenerator_RoundedThumb
  • Ansel_ImageGenerator_RoundedThumbStack
  • Ansel_ImageGenerator_Screen
  • Ansel_ImageGenerator_ShadowThumb
  • Ansel_ImageGenerator_ShadowThumbStack
  • Ansel_ImageGenerator_SquareThumb
  • Ansel_ImageGenerator_Thumb
  • Ansel_LoginTasks_SystemTask_Upgrade
  • Ansel_Report
  • Ansel_Report_letter
  • Ansel_Report_mail
  • Ansel_Report_tickets
  • Ansel_Search
  • Ansel_Search_exif
  • Ansel_Search_Tag
  • Ansel_Storage
  • Ansel_Style
  • Ansel_Tagger
  • Ansel_Test
  • Ansel_Tile_DateGallery
  • Ansel_Tile_Gallery
  • Ansel_Tile_Image
  • Ansel_View_Ansel
  • Ansel_View_Base
  • Ansel_View_EmbeddedRenderer_GalleryLink
  • Ansel_View_EmbeddedRenderer_Mini
  • Ansel_View_EmbeddedRenderer_Slideshow
  • Ansel_View_Gallery
  • Ansel_View_GalleryProperties
  • Ansel_View_GalleryRenderer_Base
  • Ansel_View_GalleryRenderer_Gallery
  • Ansel_View_GalleryRenderer_GalleryLightbox
  • Ansel_View_Image
  • Ansel_View_List
  • Ansel_View_Results
  • Ansel_View_Slideshow
  • Ansel_View_Upload
  • Ansel_Widget
  • Ansel_Widget_Actions
  • Ansel_Widget_Base
  • Ansel_Widget_GalleryFaces
  • Ansel_Widget_Geotag
  • Ansel_Widget_ImageFaces
  • Ansel_Widget_Links
  • Ansel_Widget_OtherGalleries
  • Ansel_Widget_OwnerFaces
  • Ansel_Widget_SimilarPhotos
  • Ansel_Widget_Tags
  • Ansel_XPPublisher
  • Overview
  • Package
  • Class
  • Tree
   1: <?php
   2: /**
   3:  * Class to encapsulate a single gallery. Implemented as an extension of
   4:  * the Horde_Share_Object class.
   5:  *
   6:  * Copyright 2001-2012 Horde LLC (http://www.horde.org/)
   7:  *
   8:  * See the enclosed file COPYING for license information (GPL). If you
   9:  * did not receive this file, see http://www.horde.org/licenses/gpl.
  10:  *
  11:  * @author  Michael J. Rubinsky <mrubinsk@horde.org>
  12:  * @package Ansel
  13:  */
  14: class Ansel_Gallery implements Serializable
  15: {
  16:     /**
  17:      * Serializable version constant
  18:      */
  19:     const VERSION = 3;
  20: 
  21:     /**
  22:      * The share object for this gallery.
  23:      *
  24:      * @var Horde_Share_Object
  25:      */
  26:     protected $_share;
  27: 
  28:     /**
  29:      * The gallery mode helper
  30:      *
  31:      * @var Ansel_GalleryMode_* object
  32:      */
  33:     protected $_modeHelper;
  34: 
  35:     /**
  36:      * The Ansel_Gallery constructor.
  37:      *
  38:      * @param Horde_Share_Object  The share representing this gallery.
  39:      *
  40:      * @return Ansel_Gallery
  41:      */
  42:     public function __construct(Horde_Share_Object $share)
  43:     {
  44:         $this->_share = $share;
  45: 
  46:         $this->_setModeHelper(
  47:             $share->get('view_mode') ? $share->get('view_mode') : 'Normal');
  48:     }
  49: 
  50:     /**
  51:      * Helper for accessing the gallery id
  52:      *
  53:      * @param string $property The property
  54:      *
  55:      * @return mixed
  56:      */
  57:     public function __get($property)
  58:     {
  59:         switch ($property) {
  60:         case 'id':
  61:             return $this->_share->getId();
  62:         default:
  63:             return null;
  64:         }
  65:     }
  66: 
  67:     /**
  68:      * Get a gallery property
  69:      *
  70:      * @param string $property  The property to return.
  71:      *
  72:      * @return mixed  The value.
  73:      */
  74:     public function get($property)
  75:     {
  76:         $value = $this->_share->get($property);
  77:         if ($property == 'style') {
  78:             $value = unserialize($value);
  79:         }
  80: 
  81:         return $value;
  82:     }
  83: 
  84:     /**
  85:      *
  86:      * @return array  An array of Ansel_Gallery objects.
  87:      */
  88:     public function getParents()
  89:     {
  90:         $p = $this->_share->getParents();
  91:         if (!empty($p)) {
  92:             return $GLOBALS['injector']
  93:                 ->getInstance('Ansel_Storage')
  94:                 ->buildGalleries($this->_share->getParents());
  95:         } else {
  96:             return array();
  97:         }
  98:     }
  99: 
 100:     /**
 101:      *
 102:      * @return Ansel_Gallery
 103:      */
 104:     public function getParent()
 105:     {
 106:         $p = $this->_share->getParent();
 107:         if (!empty($p)) {
 108:             return $GLOBALS['injector']
 109:                 ->getInstance('Ansel_Storage')
 110:                 ->buildGallery($this->_share->getParent());
 111:         } else {
 112:             return null;
 113:         }
 114:     }
 115: 
 116:     public function setPermission(Horde_Perms_Permision$permission, $update = true)
 117:     {
 118:         $this->_share->setPermission($permission, $update);
 119:     }
 120: 
 121:     /**
 122:      * Get the gallery's share object.
 123:      *
 124:      * @return Horde_Share_Object
 125:      */
 126:     public function getShare()
 127:     {
 128:         return $this->_share;
 129:     }
 130: 
 131:     /**
 132:      * Check for special capabilities of this gallery.
 133:      *
 134:      * @param string $feature  The feature to check for.
 135:      *
 136:      * @return boolean
 137:      */
 138:     public function hasFeature($feature)
 139:     {
 140:         // First check for purely Ansel_Gallery features
 141:         // Currently we have none of these.
 142: 
 143:         // Delegate to the modeHelper
 144:         return $this->_modeHelper->hasFeature($feature);
 145:     }
 146: 
 147:     /**
 148:      * Simple factory to set the proper mode object.
 149:      *
 150:      * @TODO: Use DI
 151:      * @param string $type  The mode to use
 152:      *
 153:      * @return Ansel_Gallery_Mode object
 154:      */
 155:     protected function _setModeHelper($type = 'Normal')
 156:     {
 157:         $type = basename($type);
 158:         $class = 'Ansel_GalleryMode_' . $type;
 159:         $this->_modeHelper = new $class($this);
 160:     }
 161: 
 162:     /**
 163:      * Checks if the user can download the full photo
 164:      *
 165:      * @return boolean  Whether or not user can download full photos
 166:      */
 167:     public function canDownload()
 168:     {
 169:         if ($GLOBALS['registry']->getAuth() &&
 170:             ($GLOBALS['registry']->getAuth() == $this->get('owner') ||
 171:              $GLOBALS['registry']->isAdmin(array('permission' => 'ansel:admin')))) {
 172:             return true;
 173:         }
 174: 
 175:         switch ($this->_share->get('download')) {
 176:         case 'all':
 177:             return true;
 178: 
 179:         case 'authenticated':
 180:             return $GLOBALS['registry']->isAuthenticated();
 181: 
 182:         case 'edit':
 183:             return $this->_share->hasPermission(
 184:                 $GLOBALS['registry']->getAuth(), Horde_Perms::EDIT);
 185: 
 186:         case 'hook':
 187:             try {
 188:                 return Horde::callHook('can_download', array($this->id));
 189:             } catch (Horde_Exception_HookNotSet $e) {}
 190: 
 191:         default:
 192:             return false;
 193:         }
 194:     }
 195: 
 196:     /**
 197:      * Saves any changes to this object to the backend permanently.
 198:      *
 199:      * @throws Ansel_Exception
 200:      */
 201:     public function save()
 202:     {
 203:         // Check for invalid characters in the slug.
 204:         $slug = $this->get('slug');
 205:         if ($slug && preg_match('/[^a-zA-Z0-9_@]/', $slug)) {
 206:             throw new InvalidArgumentException(
 207:                 sprintf(_("Could not save gallery, the slug, \"%s\", contains invalid characters."),
 208:                         $slug));
 209:         }
 210: 
 211:         // Check for slug uniqueness
 212:         if (!empty($this->_oldSlug) && $slug != $this->_oldSlug) {
 213:             if ($GLOBALS['injector']->getInstance('Ansel_Storage')->galleryExists(null, $slug)) {
 214:                 throw InvalidArgumentException(
 215:                     sprintf(_("Could not save gallery, the slug, \"%s\", already exists."),
 216:                             $slug));
 217:             }
 218:         }
 219: 
 220:         if ($GLOBALS['conf']['ansel_cache']['usecache']) {
 221:             $GLOBALS['injector']->getInstance('Horde_Cache')
 222:                 ->expire('Ansel_Gallery' . $this->id);
 223:         }
 224: 
 225:         try {
 226:             $this->_share->save();
 227:         } catch (Horde_Share_Exception $e) {
 228:             Horde::logMessage($e->getMessage(), 'ERR');
 229:             throw new Ansel_Exception($e);
 230:         }
 231:     }
 232: 
 233:     /**
 234:      * Update the gallery image count.
 235:      *
 236:      * @param integer $images      Number of images in action
 237:      * @param boolean $add         True if adding, false if removing
 238:      *
 239:      * @throws Ansel_Exception
 240:      */
 241:     public function updateImageCount($images, $add = true)
 242:     {
 243:         /* Updating self */
 244:         if ($add) {
 245:             $this->set('images',  $this->get('images') + $images);
 246:         } else {
 247:             $this->set('images',  $this->_share->get('images') - $images);
 248:         }
 249:         $this->save();
 250: 
 251:         /* Make sure we get rid of key image/stacks if no more images */
 252:         if (!$this->get('images')) {
 253:             $this->resetKeyImage();
 254:         }
 255: 
 256:         /* Need to expire the cache for the gallery that was changed */
 257:         if ($GLOBALS['conf']['ansel_cache']['usecache']) {
 258:             $GLOBALS['injector']->getInstance('Horde_Cache')
 259:                 ->expire('Ansel_Gallery' . $this->id);
 260:         }
 261:     }
 262: 
 263:     /**
 264:      * Adds an Ansel_Image object to this gallery.
 265:      *
 266:      * @param Ansel_Image $image  The ansel image object to add
 267:      * @param boolean $default    Set this image as the gallery's key image.
 268:      *
 269:      * @return integer  The new image id
 270:      */
 271:     public function addImageObject(Ansel_Image $image, $default = false)
 272:     {
 273:         /* Make sure it's taken as a new image */
 274:         $image->id = null;
 275:         $image->gallery = $this->id;
 276:         $image->sort = $this->countImages();
 277:         $image->save();
 278:         $this->updateImageCount(1);
 279: 
 280:         /* Should this be the key image? */
 281:         if ($default) {
 282:             $this->set('default', $image->id);
 283:             $this->clearStacks();
 284:         }
 285: 
 286:         /* Save all changes to the gallery */
 287:         $this->save();
 288: 
 289:         return $image->id;
 290:     }
 291: 
 292:     /**
 293:      * Add an image to this gallery.
 294:      *
 295:      * @param array $image_data  The image to add. Keys include:
 296:      *  <pre>
 297:      *    image_filename   - The filename of the image [REQUIRED].
 298:      *    data             - The binary image data [REQUIRED]
 299:      *    image_caption    - The caption/description. Defaults to filename.
 300:      *    image_type       - The MIME type of the image. Attempts to detect.
 301:      *  </pre>
 302:      *                           'image_caption', and 'data'. Optional keys
 303:      *                           include 'image_filename' and 'image_type'
 304:      *
 305:      * @param boolean $default   Make this image the new default tile image.
 306:      *
 307:      * @return integer  The id of the new image.
 308:      */
 309:     public function addImage(array $image_data, $default = false)
 310:     {
 311:         global $conf;
 312: 
 313:         /* Normal is the only view mode that can accurately update counts */
 314:         $vMode = $this->get('view_mode');
 315:         if ($vMode != 'Normal') {
 316:             $this->_setModeHelper('Normal');
 317:         }
 318: 
 319:         $resetStack = false;
 320:         if (empty($image_data['image_caption'])) {
 321:             $image_data['image_caption'] = $image_data['image_filename'];
 322:         }
 323:         $image_data['gallery_id'] = $this->id;
 324:         $image_data['image_sort'] = $this->countImages();
 325: 
 326:         /* Create the image object */
 327:         $image = new Ansel_Image($image_data);
 328: 
 329:         /* Check for a supported multi-page image */
 330:         if ($image->isMultiPage() === true) {
 331:             $params['name'] = $image->getImagePageCount() . ' page image: '
 332:                 . $image->filename;
 333:             $mGallery = $GLOBALS['injector']->getInstance('Ansel_Storage')
 334:                 ->createGallery($params, $this->_share->getPermission(), $this);
 335:             $i = 1;
 336:             foreach ($image as $page) {
 337:                 $page->caption = sprintf(_("Page %d"), $i++);
 338:                 $mGallery->addImageObject($page);
 339:             }
 340:             $mGallery->save();
 341: 
 342:             return $page->id;
 343:         }
 344: 
 345:         /* If this was a single, normal image, continue */
 346:         $result = $image->save();
 347:         if (empty($image_data['image_id'])) {
 348:             $this->updateImageCount(1);
 349:             if ($this->countImages() < 5) {
 350:                 $resetStack = true;
 351:             }
 352:         }
 353: 
 354:         /* Should this be the key image? */
 355:         if (!$default && $this->get('default_type') == 'auto') {
 356:             $this->set('default', $image->id);
 357:             $resetStack = true;
 358:         } elseif ($default) {
 359:             $this->set('default', $image->id);
 360:             $this->set('type', 'manual');
 361:         }
 362: 
 363:         /* Reset the gallery key image stacks if needed. */
 364:         if ($resetStack) {
 365:             $this->clearStacks();
 366:         }
 367: 
 368:         /* Update the modified flag and save gallery changes */
 369:         $this->set('last_modified', time());
 370: 
 371:         /* Save all changes to the gallery */
 372:         $this->save();
 373: 
 374:         /* Return to the proper view mode */
 375:         if ($vMode != 'Normal') {
 376:             $this->_setModeHelper($vMode);
 377:         }
 378: 
 379:         /* Return the ID of the new image. */
 380:         return $image->id;
 381:     }
 382: 
 383:     /**
 384:      * Clear all of this gallery's key image stacks from the VFS and the
 385:      * gallery's data store.
 386:      *
 387:      * @throws Ansel_Exception
 388:      */
 389:     public function clearStacks()
 390:     {
 391:         $ids = @unserialize($this->get('default_prettythumb'));
 392:         if (is_array($ids)) {
 393:             try {
 394:                 foreach ($ids as $imageId) {
 395:                     $this->removeImage($imageId, true);
 396:                 }
 397:             } catch (Horde_Exception_NotFound $e) {
 398:                 throw new Ansel_Exception($e);
 399:             }
 400:         }
 401: 
 402:         // Using the set function here so we can efficently update the db
 403:         $this->set('default_prettythumb', '', true);
 404:     }
 405: 
 406:     /**
 407:      * Removes all generated and cached thumbnails for this gallery.
 408:      */
 409:     public function clearThumbs()
 410:     {
 411:         $images = $this->listImages();
 412:         foreach ($images as $id) {
 413:             $image = $this->getImage($id);
 414:             $image->deleteCache('thumb');
 415:         }
 416:     }
 417: 
 418:     /**
 419:      * Removes all generated and cached views for this gallery.
 420:      */
 421:     public function clearViews()
 422:     {
 423:         $images = $this->listImages();
 424:         foreach ($images as $id) {
 425:             $image = $this->getImage($id);
 426:             $image->deleteCache('all');
 427:         }
 428:     }
 429: 
 430:     /**
 431:      * Reset the gallery's key image. This will force Ansel to attempt to fetch
 432:      * a new key image the next time one is requested.
 433:      */
 434:     public function resetKeyImage()
 435:     {
 436:         $this->clearStacks();
 437:         $this->set('default', 0);
 438:         $this->set('default_type', 'auto');
 439:         $this->save();
 440:     }
 441: 
 442:     /**
 443:      * Move images from this gallery to a new gallery.
 444:      *
 445:      * @param array $images          An array of image ids.
 446:      * @param Ansel_Gallery $gallery The gallery to move the images to.
 447:      *
 448:      * @return integer  The number of images moved.
 449:      */
 450:     public function moveImagesTo(array $images, Ansel_Gallery $gallery)
 451:     {
 452:         return $this->_modeHelper->moveImagesTo($images, $gallery);
 453:     }
 454: 
 455:     /**
 456:      * Copy image and related data to specified gallery.
 457:      *
 458:      * @param array $images           An array of image ids.
 459:      * @param Ansel_Gallery $gallery  The gallery to copy images to.
 460:      *
 461:      * @return integer The number of images copied
 462:      * @throws Ansel_Exception
 463:      */
 464:     public function copyImagesTo(array $images, Ansel_Gallery $gallery)
 465:     {
 466:         if (!$gallery->hasPermission($GLOBALS['registry']->getAuth(), Horde_Perms::EDIT)) {
 467:             throw new Horde_Exception_PermissionDenied(
 468:                 sprintf(_("Access denied copying photos to \"%s\"."),
 469:                         $gallery->get('name')));
 470:         }
 471: 
 472:         $imgCnt = 0;
 473:         foreach ($images as $imageId) {
 474:             $img = $this->getImage($imageId);
 475:             // Note that we don't pass the tags when adding the image..see below
 476:             $newId = $gallery->addImage(array(
 477:                                'image_caption' => $img->caption,
 478:                                'data' => $img->raw(),
 479:                                'image_filename' => $img->filename,
 480:                                'image_type' => $img->getType(),
 481:                                'image_uploaded_date' => $img->uploaded));
 482:             /* Copy any tags */
 483:             $tags = $img->getTags();
 484:             $GLOBALS['injector']->getInstance('Ansel_Tagger')
 485:                 ->tag($newId, $tags, $gallery->get('owner'), 'image');
 486: 
 487:             // Check that new image_id doesn't have existing attributes,
 488:             // throw exception if it does.
 489:             $newAttributes = $GLOBALS['injector']
 490:                 ->getInstance('Ansel_Storage')
 491:                 ->getImageAttributes($newId);
 492:             if (count($newAttributes)) {
 493:                 throw new Ansel_Exception(_("Image already has existing attributes."));
 494:             }
 495: 
 496:             $exif = $GLOBALS['injector']
 497:                 ->getInstance('Ansel_Storage')
 498:                 ->getImageAttributes($imageId);
 499: 
 500:             if (is_array($exif) && count($exif) > 0) {
 501:                 foreach ($exif as $name => $value){
 502:                     $GLOBALS['injector']
 503:                         ->getInstance('Ansel_Storage')
 504:                         ->saveImageAttribute($newId, $name, $value);
 505:                 }
 506:             }
 507: 
 508:             ++$imgCnt;
 509:         }
 510: 
 511:         return $imgCnt;
 512:     }
 513: 
 514:     /**
 515:      * Set the order of an image in this gallery.
 516:      *
 517:      * @param integer $imageId The image to sort.
 518:      * @param integer $pos     The sort position of the image.
 519:      *
 520:      * @throws Ansel_Exception
 521:      */
 522:     public function setImageOrder($imageId, $pos)
 523:     {
 524:         $GLOBALS['injector']
 525:             ->getInstance('Ansel_Storage')
 526:             ->setImageSortOrder($imageId, $pos);
 527:     }
 528: 
 529:     /**
 530:      * Remove the given image from this gallery.
 531:      *
 532:      * @param mixed $image   Image to delete. Can be an Ansel_Image
 533:      *                       or an image ID.
 534:      *
 535:      * @param boolean $isStack  Indicates if this image represents a stack image.
 536:      * @throws Horde_Exception_NotFound, Ansel_Exception
 537:      */
 538:     public function removeImage($image, $isStack = false)
 539:     {
 540:         $this->_modeHelper->removeImage($image, $isStack);
 541:     }
 542: 
 543:     /**
 544:      * Returns this share's owner's Identity object.
 545:      *
 546:      * @return Horde_Prefs_Identity object for the owner of this gallery.
 547:      */
 548:     public function getIdentity()
 549:     {
 550:         return $GLOBALS['injector']
 551:             ->getInstance('Horde_Core_Factory_Identity')
 552:             ->create($this->get('owner'));
 553:     }
 554: 
 555:     /**
 556:      * Output the HTML for this gallery's tile.
 557:      *
 558:      * @param Ansel_Gallery $parent  The parent Ansel_Gallery object
 559:      * @param Ansel_Style $style     A style object to use.
 560:      * @param boolean $mini          Force the use of a mini thumbnail?
 561:      * @param array $params          Any additional parameters the Ansel_Tile
 562:      *                               object may need.
 563:      *
 564:      * @return Ansel_Tile_Gallery
 565:      */
 566:     public function getTile(Ansel_Gallery $parent = null,
 567:                             Ansel_Style $style = null,
 568:                             $mini = false,
 569:                             array $params = array())
 570:     {
 571:         if (!is_null($parent) && is_null($style)) {
 572:             $style = $parent->getStyle();
 573:         }
 574: 
 575:         return Ansel_Tile_Gallery::getTile($this, $style, $mini, $params);
 576:     }
 577: 
 578:     /**
 579:      * Get all children of this share.
 580:      *
 581:      * @param string $user        The user to use for checking perms
 582:      * @param integer $perm       Horde_Perms::* constant. If NULL will return
 583:      *                            all shares regardless of permissions.
 584:      * @param boolean $allLevels  Return all levels.
 585:      *
 586:      * @return array  An array of Ansel_Gallery objects
 587:      * @throws Ansel_Exception
 588:      */
 589:     public function getChildren($user, $perm = Horde_Perms::SHOW, $allLevels = true)
 590:     {
 591:         try {
 592:             return $GLOBALS['injector']
 593:                 ->getInstance('Ansel_Storage')
 594:                 ->buildGalleries($this->_share->getChildren($user, $perm, $allLevels));
 595:         } catch (Horde_Share_Exception $e) {
 596:             throw new Ansel_Exception($e);
 597:         }
 598:     }
 599: 
 600:     /**
 601:      * Get the children of this gallery.
 602:      *
 603:      * @param integer $perm    The permissions to limit to.
 604:      * @param integer $from    The child to start at.
 605:      * @param integer $to      The child to end with.
 606:      * @param boolean $noauto  Prevent auto
 607:      *
 608:      * @return A mixed array of Ansel_Gallery and Ansel_Image objects that are
 609:      *         children of this gallery.
 610:      */
 611:     public function getGalleryChildren($perm = Horde_Perms::SHOW,
 612:                                        $from = 0,
 613:                                        $to = 0,
 614:                                        $noauto = true)
 615:     {
 616:         return $this->_modeHelper->getGalleryChildren($perm, $from, $to, $noauto);
 617:     }
 618: 
 619:     /**
 620:      * Return the count of this gallery's children
 621:      *
 622:      * @param integer $perm            The permissions to require.
 623:      * @param boolean $galleries_only  Only include galleries, no images.
 624:      * @param boolean $noauto          Do not auto drill down into gallery tree.
 625:      *
 626:      * @return integer The count of this gallery's children.
 627:      */
 628:     public function countGalleryChildren($perm = Horde_Perms::SHOW,
 629:                                          $galleries_only = false,
 630:                                          $noauto = true)
 631:     {
 632:         return $this->_modeHelper->countGalleryChildren(
 633:             $perm, $galleries_only, $noauto);
 634:     }
 635: 
 636:     public function countChildren($user, $perm = Horde_Perms::SHOW, $allLevels = true)
 637:     {
 638:         return $this->_share->CountChildren($user, $perm, $allLevels);
 639:     }
 640: 
 641:     /**
 642:      * Lists a slice of the image ids in this gallery.
 643:      *
 644:      * @param integer $from  The image to start listing.
 645:      * @param integer $count The numer of images to list.
 646:      *
 647:      * @return array  An array of image_ids
 648:      */
 649:     public function listImages($from = 0, $count = 0)
 650:     {
 651:         return $this->_modeHelper->listImages($from, $count);
 652:     }
 653: 
 654:     /**
 655:      * Gets a slice of the images in this gallery.
 656:      *
 657:      * @param integer $from  The image to start fetching.
 658:      * @param integer $count The numer of images to return.
 659:      *
 660:      * @param array  An array of Ansel_Image objects
 661:      */
 662:     public function getImages($from = 0, $count = 0)
 663:     {
 664:         return $this->_modeHelper->getImages($from, $count);
 665:     }
 666: 
 667:     /**
 668:      * Return the most recently added images in this gallery.
 669:      *
 670:      * @param integer $limit  The maximum number of images to return.
 671:      *
 672:      * @return array  An array of Ansel_Image objects
 673:      */
 674:     public function getRecentImages($limit = 10)
 675:     {
 676:         return $GLOBALS['injector']->getInstance('Ansel_Storage')
 677:             ->getRecentImages(array($this->id), $limit);
 678:     }
 679: 
 680:     /**
 681:      * Returns the image in this gallery corresponding to the given id.
 682:      *
 683:      * @param integer $id  The ID of the image to retrieve.
 684:      *
 685:      * @return Ansel_Image  The image object corresponding to the given id.
 686:      */
 687:     public function &getImage($id)
 688:     {
 689:         return $GLOBALS['injector']->getInstance('Ansel_Storage')->getImage($id);
 690:     }
 691: 
 692:     /**
 693:      * Checks if the gallery has any subgallery
 694:      *
 695:      * @return boolean
 696:      */
 697:     public function hasSubGalleries()
 698:     {
 699:         return $this->_modeHelper->hasSubGalleries();
 700:     }
 701: 
 702:     /**
 703:      * Returns the number of images in this gallery and, optionally, all
 704:      * sub-galleries.
 705:      *
 706:      * @param boolean $subgalleries  Determines whether subgalleries should
 707:      *                               be counted or not.
 708:      *
 709:      * @return integer number of images in this gallery
 710:      */
 711:     public function countImages($subgalleries = false)
 712:     {
 713:         return $this->_modeHelper->countImages($subgalleries);
 714:     }
 715: 
 716:     /**
 717:      * Returns the key image for this gallery.
 718:      *
 719:      * @param Ansel_Style $style  Force the use of this style, if it's available
 720:      *                            otherwise use whatever style is choosen for
 721:      *                            this gallery. If prettythumbs are not
 722:      *                            available then we always use ansel_default
 723:      *                            style.
 724:      *
 725:      * @return mixed  The image_id of the key image or false.
 726:      */
 727:     public function getKeyImage(Ansel_Style $style = null)
 728:     {
 729:         if (is_null($style)) {
 730:             $style = $this->getStyle();
 731:         }
 732: 
 733:         if ($style->keyimage_type != 'Thumb') {
 734:             $thumbstyle = $style->keyimage_type;
 735:             $styleHash = $style->getHash($thumbstyle);
 736: 
 737:             // First check for the existence of a key image in the specified style
 738:             if ($this->get('default_prettythumb')) {
 739:                 $thumbs = @unserialize($this->get('default_prettythumb'));
 740:             }
 741:             if (!isset($thumbs) || !is_array($thumbs)) {
 742:                 $thumbs = array();
 743:             }
 744:             if (!empty($thumbs[$styleHash])) {
 745:                 return $thumbs[$styleHash];
 746:             }
 747: 
 748:             // Don't already have one, must generate it.
 749:             $params = array('gallery' => $this, 'style' => $style);
 750:             try {
 751:                 $iview = Ansel_ImageGenerator::factory($style->keyimage_type, $params);
 752:                 $img = $iview->create();
 753: 
 754:                 // Note the gallery_id is negative for generated stacks
 755:                 $iparams = array(
 756:                     'image_filename' => $this->get('name'),
 757:                     'image_caption' => $this->get('name'),
 758:                     'data' => $img->raw(),
 759:                     'image_sort' => 0,
 760:                     'gallery_id' => -$this->id);
 761:                 $newImg = new Ansel_Image($iparams);
 762:                 $newImg->save();
 763:                 $prettyData = serialize(
 764:                     array_merge($thumbs, array($styleHash => $newImg->id)));
 765:                 $this->set('default_prettythumb', $prettyData, true);
 766: 
 767:                 // Make sure the hash is saved since it might be different then
 768:                 // the gallery's
 769:                 $GLOBALS['injector']
 770:                     ->getInstance('Ansel_Storage')
 771:                     ->ensureHash($styleHash);
 772: 
 773:                 return $newImg->id;
 774:             } catch (Horde_Exception $e) {
 775:                 // Might not support the requested style...try ansel_default
 776:                 // but protect against infinite recursion.
 777:                 Horde::logMessage($e->getMessage(), 'DEBUG');
 778:                 if ($style->keyimage_type != 'plain') {
 779:                     return $this->getKeyImage(Ansel::getStyleDefinition('ansel_default'));
 780:                 }
 781:             }
 782:         } else {
 783:             // We are just using an image thumbnail.
 784:             if ($this->countImages()) {
 785:                 if ($default = $this->get('default')) {
 786:                     return $default;
 787:                 }
 788:                 $keys = $this->listImages();
 789:                 $this->set('default', $keys[count($keys) - 1]);
 790:                 $this->set('default_type', 'auto');
 791:                 $this->save();
 792:                 return $keys[count($keys) - 1];
 793:             }
 794: 
 795:             if ($this->hasSubGalleries()) {
 796:                 // Fall through to a key image of a sub gallery.
 797:                 try {
 798:                     $galleries = $GLOBALS['injector']
 799:                         ->getInstance('Ansel_Storage')
 800:                         ->listGalleries(array('parent' => $this->id, 'all_levels' => false));
 801: 
 802:                     foreach ($galleries as $gallery) {
 803:                         if ($default_img = $gallery->getKeyImage($style)) {
 804:                             return $default_img;
 805:                         }
 806:                     }
 807:                 } catch (Horde_Exception $e) {
 808:                     return false;
 809:                 }
 810:             }
 811:         }
 812: 
 813:         // Could not find a key image
 814:         return false;
 815:     }
 816: 
 817:     /**
 818:      * Returns this gallery's tags.
 819:      *
 820:      * @return array of tag info
 821:      * @throws Horde_Exception
 822:      */
 823:     public function getTags()
 824:     {
 825:         if ($this->_share->hasPermission($GLOBALS['registry']->getAuth(), Horde_Perms::READ)) {
 826:             return $GLOBALS['injector']->getInstance('Ansel_Tagger')
 827:                 ->getTags($this->id, 'gallery');
 828:         } else {
 829:             throw new Horde_Exception(_("Access denied viewing this gallery."));
 830:         }
 831:     }
 832: 
 833:     /**
 834:      * Set/replace this gallery's tags.
 835:      *
 836:      * @param array $tags  An array of tag names to associate with this image.
 837:      *
 838:      * @throws Horde_Exception_PermissionDenied
 839:      */
 840:     public function setTags(array $tags, $replace = true)
 841:     {
 842:         if ($this->hasPermission($GLOBALS['registry']->getAuth(), Horde_Perms::EDIT)) {
 843: 
 844:             if ($replace) {
 845:                 $GLOBALS['injector']
 846:                     ->getInstance('Ansel_Tagger')
 847:                     ->replaceTags(
 848:                         $this->id,
 849:                         $tags,
 850:                         $this->get('owner'),
 851:                         'gallery');
 852:             } else {
 853:                 $GLOBALS['injector']
 854:                     ->getInstance('Ansel_Tagger')
 855:                     ->tag(
 856:                         $this->id,
 857:                         $tags,
 858:                         $this->get('owner'),
 859:                         'gallery');
 860:             }
 861:         } else {
 862:             throw new Horde_Exception_PermissionDenied(_("Access denied adding tags to this gallery."));
 863:         }
 864:     }
 865: 
 866:     /**
 867:      * Remove a single tag from this gallery's tag collection
 868:      *
 869:      * @param string $tag  The tag name to remove.
 870:      */
 871:     public function removeTag($tag)
 872:     {
 873:         if ($this->hasPermission($GLOBALS['registry']->getAuth(), Horde_Perms::EDIT)) {
 874:             $GLOBALS['injector']
 875:                 ->getInstance('Ansel_Tagger')
 876:                 ->untag(
 877:                     (string)$this->id,
 878:                     $tag,
 879:                     'gallery');
 880:         }
 881:     }
 882: 
 883:     /**
 884:      * Return the style definition for this gallery.
 885:      *
 886:      * @return Ansel_Style  The style definition array.
 887:      */
 888:     public function getStyle()
 889:     {
 890:         // No styles allowed per admin.
 891:         if (!$GLOBALS['conf']['image']['prettythumbs']) {
 892:             return Ansel::getStyleDefinition('ansel_default');
 893:         }
 894: 
 895:         if (!$this->get('style')) {
 896:             // No style configured, use user's prefered default
 897:             $style = Ansel::getStyleDefinition(
 898:                 $GLOBALS['prefs']->getValue('default_gallerystyle'));
 899:         } else {
 900:             // Explicitly defined style
 901:             $style = $this->get('style');
 902:         }
 903: 
 904:         // Check browser requirements. If we require PNG support, and do not
 905:         // have it, revert to the basic ansel_default style.
 906:         if ($style->requiresPng() &&
 907:             ($GLOBALS['browser']->hasQuirk('png_transparency') ||
 908:              $GLOBALS['conf']['image']['type'] != 'png')) {
 909: 
 910:             $style = Ansel::getStyleDefinition('ansel_default');
 911:         }
 912: 
 913:         return $style;
 914:     }
 915: 
 916:     /**
 917:      * Checks to see if a user has a given permission.
 918:      *
 919:      * @param string $userid       The userid of the user.
 920:      * @param integer $permission  A Horde_Perms::* constant to test for.
 921:      * @param string $creator      The creator of the event.
 922:      *
 923:      * @return boolean  Whether or not $userid has $permission.
 924:      */
 925:     public function hasPermission($userid, $permission, $creator = null)
 926:     {
 927:         if ($userid == $this->get('owner') ||
 928:             $GLOBALS['registry']->isAdmin(array('permission' => 'ansel:admin'))) {
 929: 
 930:             return true;
 931:         }
 932: 
 933:         return $this->_share->hasPermission($userid, $permission, $creator);
 934:     }
 935: 
 936:     /**
 937:      * Returns the permission of this share.
 938:      *
 939:      * @return Horde_Perms_Permission  Permission object that represents the
 940:      *                                 permissions on this share.
 941:      */
 942:     public function getPermission()
 943:     {
 944:         return $this->_share->getPermission();
 945:     }
 946: 
 947:     /**
 948:      * Check user age limtation
 949:      *
 950:      * @return boolean
 951:      */
 952:     public function isOldEnough()
 953:     {
 954:         global $session;
 955: 
 956:         if (($GLOBALS['registry']->getAuth() &&
 957:              $this->get('owner') == $GLOBALS['registry']->getAuth()) ||
 958:             empty($GLOBALS['conf']['ages']['limits']) ||
 959:             !$this->get('age')) {
 960: 
 961:             return true;
 962:         }
 963: 
 964:         // Do we have the user age already cheked?
 965:         if (!$session->exists('ansel', 'user_age')) {
 966:             $session->set('ansel', 'user_age', 0);
 967:             $user_age = 0;
 968:         } else {
 969:             $user_age = $session->get('ansel', 'user_age');
 970:             if ($user_age >= $this->get('age')) {
 971:                 return true;
 972:             }
 973:         }
 974: 
 975:         // Can we hook user's age?
 976:         if ($GLOBALS['conf']['ages']['hook'] &&
 977:             $GLOBALS['registry']->isAuthenticated()) {
 978:             try {
 979:                 $result = Horde::callHook('user_age', array(), 'ansel');
 980:             } catch (Horde_Exception_HookNotSet $e) {}
 981:             if (is_int($result)) {
 982:                 $session->set('ansel', 'user_age', $result);
 983:                 $user_age = $result;
 984:             }
 985:         }
 986: 
 987:         return ($user_age >= $this->get('age'));
 988:     }
 989: 
 990:     /**
 991:      * Determine if we need to unlock a password protected gallery
 992:      *
 993:      * @return boolean
 994:      */
 995:     public function hasPasswd()
 996:     {
 997:         if ($GLOBALS['registry']->getAuth() &&
 998:             ($GLOBALS['registry']->getAuth() == $this->get('owner') ||
 999:              $GLOBALS['registry']->isAdmin(array('permission' => 'ansel:admin')))) {
1000:             return false;
1001:         }
1002: 
1003:         $passwd = $this->get('passwd');
1004:         if (empty($passwd)) {
1005:             return false;
1006:         } elseif ($GLOBALS['session']->get('ansel', 'passwd/' . $this->id)) {
1007:             $GLOBALS['session']->set('ansel', 'passwd/' . $this->id, hash('md5', $this->get('passwd')));
1008:             return false;
1009:         }
1010: 
1011:         return true;
1012:     }
1013: 
1014:     /**
1015:      * Sets this gallery's parent gallery.
1016:      *
1017:      * @param mixed $parent  An Ansel_Gallery or a gallery_id.
1018:      *
1019:      * @throws Ansel_Exception
1020:      */
1021:     public function setParent($parent)
1022:     {
1023:         /* Make sure we have a gallery object */
1024:         if (!is_null($parent) && !($parent instanceof Ansel_Gallery)) {
1025:             $parent = $GLOBALS['injector']
1026:                 ->getInstance('Ansel_Storage')
1027:                 ->getGallery($parent);
1028:         }
1029: 
1030:         /* Check this now since we don't know if we are updating the DB or not */
1031:         $old = $this->getParent();
1032:         $reset_has_subgalleries = false;
1033:         if (!is_null($old)) {
1034:             $vMode = $old->get('view_mode');
1035:             if ($vMode != 'Normal') {
1036:                 $old->set('view_mode', 'Normal');
1037:             }
1038:             $cnt = $old->countGalleryChildren(Horde_Perms::READ, true);
1039:             if ($vMode != 'Normal') {
1040:                 $old->set('view_mode', $vMode);
1041:             }
1042:             if ($cnt == 1) {
1043:                 /* Count is 1, and we are about to delete it */
1044:                 $reset_has_subgalleries = true;
1045:                 if (!$old->countImages()) {
1046:                     $old->resetKeyImage();
1047:                 }
1048:             }
1049:         }
1050: 
1051:         /* Call the parent class method */
1052:         try {
1053:             $this->_share->setParent(!is_null($parent) ? $parent->getShare() : null);
1054:         } catch (Horde_Share_Exception $e) {
1055:             throw new Ansel_Exception($e);
1056:         }
1057:         /* Tell the parent the good news */
1058:         if (!is_null($parent) && !$parent->get('has_subgalleries')) {
1059:             $parent->set('has_subgalleries', '1', true);
1060:         }
1061:         Horde::logMessage('Ansel_Gallery parent successfully set', 'DEBUG');
1062: 
1063:        /* Gallery parent changed, safe to change the parent's attributes */
1064:        if ($reset_has_subgalleries) {
1065:            $old->set('has_subgalleries', 0, true);
1066:        }
1067:     }
1068: 
1069:     /**
1070:      * Sets an attribute value in this object.
1071:      *
1072:      * @param string $attribute  The attribute to set.
1073:      * @param mixed $value       The value for $attribute.
1074:      * @param boolean $update    Commit only this change to storage.
1075:      *
1076:      * @throws Ansel_Exception
1077:      */
1078:     public function set($attribute, $value, $update = false)
1079:     {
1080:         if ($attribute == 'slug') {
1081:             $this->_oldSlug = $this->get('slug');
1082:         }
1083: 
1084:         /* Need to serialize the style object */
1085:         if ($attribute == 'style') {
1086:             $value = serialize($value);
1087:         }
1088: 
1089:         if ($attribute == 'view_mode' && $this->get('view_mode') != $value) {
1090:             //$mode = isset($attributes['attribute_view_mode']) ? $attributes['attribute_view_mode'] : 'Normal';
1091:             $this->_setModeHelper($value);
1092:         }
1093: 
1094:         try {
1095:             $this->_share->set($attribute, $value, $update);
1096:         } catch (Horde_Share_Exception $e) {
1097:             throw new Ansel_Exception($e);
1098:         }
1099:     }
1100: 
1101:     public function setDate($date)
1102:     {
1103:         $this->_modeHelper->setDate($date);
1104:     }
1105: 
1106:     public function getDate()
1107:     {
1108:         return $this->_modeHelper->getDate();
1109:     }
1110: 
1111:     /**
1112:      * Get an array describing where this gallery is in a breadcrumb trail.
1113:      *
1114:      * @return  An array of 'title' and 'navdata' hashes with the [0] element
1115:      *          being the deepest part.
1116:      */
1117:     public function getGalleryCrumbData()
1118:     {
1119:         return $this->_modeHelper->getGalleryCrumbData();
1120:     }
1121: 
1122:     /**
1123:      * Serialize this object.
1124:      *
1125:      * @return string  The serialized data.
1126:      */
1127:     public function serialize()
1128:     {
1129:         $data = array(
1130:             self::VERSION,
1131:             $this->_share
1132:         );
1133: 
1134:         return serialize($data);
1135:     }
1136: 
1137:     public function unserialize($data)
1138:     {
1139:         $data = @unserialize($data);
1140:         if (!is_array($data) ||
1141:             !isset($data[0]) ||
1142:             ($data[0] != self::VERSION)) {
1143:             throw new Exception('Cache version change');
1144:         }
1145:         $this->_share = $data[1];
1146:         $this->_setModeHelper($this->get('view_mode'));
1147:     }
1148: 
1149:     /**
1150:      * Returns a json representation of this gallery.
1151:      *
1152:      * @param boolean $full  Return all information (subgalleries and images)?
1153:      *
1154:      * @return StdClass  An object describing the gallery
1155:      * <pre>
1156:      * 'id' - gallery id
1157:      * 'p'  - gallery's parent's id (null if top level)
1158:      * 'pn' - gallery's parent's name (null if top level)
1159:      * 'n'  - gallery name
1160:      * 'dc' - date created
1161:      * 'dm' - date modified
1162:      * 'd'  - description
1163:      * 'ki' - key image
1164:      * 'sg' - an object with the following properties:
1165:      *      'n'  - gallery name
1166:      *      'dc' - date created
1167:      *      'dm' - date modified
1168:      *      'd'  - description
1169:      *      'ki' - key image
1170:      *
1171:      *  'imgs' - an array of image objects with the following properties:
1172:      *      'id'  - the image id
1173:      *      'url' - the image url
1174:      * </pre>
1175:      */
1176:     public function toJson($full = false)
1177:     {
1178:         // @TODO: Support date grouped galleries
1179:         $vMode = $this->get('view_mode');
1180:         if ($vMode != 'Normal') {
1181:             $this->_setModeHelper('Normal');
1182:         }
1183:         $style = Ansel::getStyleDefinition('ansel_mobile');
1184: 
1185:         $json = new StdClass();
1186:         $json->id = $this->id;
1187:         $json->n = $this->get('name');
1188:         $json->dc = $this->get('date_created');
1189:         $json->dm = $this->get('last_modified');
1190:         $json->d = $this->get('desc');
1191:         $json->ki = Ansel::getImageUrl($this->getKeyImage($style), 'thumb', false, $style)->toString(true);
1192:         $json->imgs = array();
1193: 
1194:         // Parent
1195:         $parents = $this->getParents();
1196:         if (empty($parents)) {
1197:             $json->p = null;
1198:             $json->pn = null;
1199:         } else {
1200:             $p = array_pop($parents);
1201:             $json->p =$p->id;
1202:             $json->pn = $p->get('name');
1203:         }
1204: 
1205:         if ($full) {
1206:             $json->tiny = ($GLOBALS['conf']['image']['tiny'] &&
1207:                            ($GLOBALS['conf']['vfs']['src'] == 'direct' || $this->_share->hasPermission('', Horde_Perms::READ)));
1208:             $json->sg = array();
1209:             if ($this->hasSubGalleries()) {
1210:                 $sgs = $this->getChildren(
1211:                     $GLOBALS['registry']->getAuth(),
1212:                     Horde_Perms::READ,
1213:                     false);//GLOBALS['injector']->getInstance('Ansel_Storage')->listGalleries(array('parent' => $this->id, 'all_levels' => false));
1214:                 foreach ($sgs as $g) {
1215:                     $json->sg[] = $g->toJson();
1216:                 }
1217:             }
1218: 
1219:             $images = $this->getImages();
1220:             foreach ($images as $img) {
1221:                 $i = new StdClass();
1222:                 $i->id = $img->id;
1223:                 $i->url = Ansel::getImageUrl($img->id, 'thumb', false, $style)->toString(true);
1224:                 $i->screen = Ansel::getImageUrl($img->id, 'screen', $json->tiny, Ansel::getStyleDefinition('ansel_default'))->toString(true);
1225:                 $i->fn = $img->filename;
1226:                 $json->imgs[] = $i;
1227:             }
1228:         }
1229: 
1230:         if ($vMode != 'Normal') {
1231:             $this->_setModeHelper($vMode);
1232:         }
1233:         return $json;
1234:     }
1235: 
1236:     public function toArray()
1237:     {
1238:         $fields = array(
1239:             'date_created', 'last_modified', 'owner', 'name', 'desc', 'default',
1240:              'default_type', 'default_prettythumb', 'images', 'has_subgalleries',
1241:              'slug', 'age', 'download', 'passwd', 'faces', 'view_mode');
1242:         $gallery = array();
1243:         foreach ($fields as $field) {
1244:             $gallery[$field] = $this->get($field);
1245:         }
1246:         $gallery['id'] = $this->id;
1247: 
1248:         return $gallery;
1249:     }
1250: 
1251: }
1252: 
API documentation generated by ApiGen