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 for interfacing with back end data storage.
   4:  *
   5:  * Copyright 2001-2012 Horde LLC (http://www.horde.org/)
   6:  *
   7:  * See the enclosed file COPYING for license information (GPL). If you
   8:  * did not receive this file, see http://www.horde.org/licenses/gpl.
   9:  *
  10:  * @author  Michael J. Rubinsky <mrubinsk@horde.org>
  11:  * @package Ansel
  12:  */
  13: class Ansel_Storage
  14: {
  15:     /**
  16:      * database handle
  17:      *
  18:      * @var Horde_Db_Adapter
  19:      */
  20:     private $_db;
  21: 
  22:     /**
  23:      * The Horde_Shares object to use for this scope.
  24:      *
  25:      * @var Horde_Share
  26:      */
  27:     private $_shares;
  28: 
  29:     /**
  30:      * Local cache of retrieved images
  31:      *
  32:      * @var array
  33:      */
  34:     private $_images = array();
  35: 
  36:     /**
  37:      * Const'r
  38:      *
  39:      * @param Horde_Core_Share_Driver  The share object
  40:      *
  41:      * @return Ansel_Storage
  42:      */
  43:     public function __construct(Horde_Core_Share_Driver $shareOb)
  44:     {
  45:         $this->_shares = $shareOb;
  46:     }
  47: 
  48:     /**
  49:      * Property accessor
  50:      *
  51:      * @param string $property  The property to access.
  52:      */
  53:     public function __get($property)
  54:     {
  55:         switch ($property) {
  56:         case 'shares':
  57:             return $this->{'_' . $property};
  58:         default: // Just for now until everything is refactored.
  59:             return null;
  60:         }
  61:     }
  62: 
  63:     /**
  64:      * Backend setter
  65:      *
  66:      * @param mixed  $storage  The backend storage driver.
  67:      */
  68:     public function setStorage($storage)
  69:     {
  70:         $this->_db = $storage;
  71:     }
  72: 
  73:    /**
  74:     * Create and initialise a new gallery object.
  75:     *
  76:     * @param array $attributes             The gallery attributes.
  77:     * @param Horde_Perms_Permission $perm  The permissions for the gallery if
  78:     *                                      the defaults are not desirable.
  79:     * @param integer $parent               The id of the parent gallery (if any)
  80:     *
  81:     * @return Ansel_Gallery  A new gallery object.
  82:     * @throws Ansel_Exception
  83:     */
  84:     public function createGallery(array $attributes = array(),
  85:                                   Horde_Perms_Permission $perm = null,
  86:                                   $parent = null)
  87:     {
  88:         // Required values.
  89:         if (empty($attributes['owner'])) {
  90:             $attributes['owner'] = $GLOBALS['registry']->getAuth();
  91:         }
  92:         if (empty($attributes['name'])) {
  93:             $attributes['name'] = _("Unnamed");
  94:         }
  95:         if (empty($attributes['desc'])) {
  96:             $attributes['desc'] = '';
  97:         }
  98: 
  99:         // Default values
 100:         $attributes['default_type'] = isset($attributes['default_type']) ?
 101:             $attributes['default_type'] :
 102:             'auto';
 103:         $attributes['default'] = isset($attributes['default']) ?
 104:             (int)$attributes['default'] :
 105:             0;
 106:         $attributes['default_prettythumb'] = isset($attributes['default_prettythumb']) ?
 107:             $attributes['default_prettythumb'] :
 108:             '';
 109: 
 110:         // No value for style now means to use the 'default_ansel' style as
 111:         // defined in styles.php
 112:         $attributes['style'] = isset($attributes['style']) ? $attributes['style'] : '';
 113:         $attributes['date_created'] = time();
 114:         $attributes['last_modified'] = $attributes['date_created'];
 115:         $attributes['images'] = isset($attributes['images']) ?
 116:             (int)$attributes['images'] :
 117:             0;
 118:         $attributes['slug'] = isset($attributes['slug']) ? $attributes['slug'] : '';
 119:         $attributes['age'] = isset($attributes['age']) ? (int)$attributes['age'] : 0;
 120:         $attributes['download'] = isset($attributes['download']) ?
 121:             $attributes['download'] :
 122:             $GLOBALS['prefs']->getValue('default_download');
 123:         $attributes['view_mode'] = isset($attributes['view_mode']) ?
 124:             $attributes['view_mode'] :
 125:             'Normal';
 126:         $attributes['passwd'] = isset($attributes['passwd']) ?
 127:             $attributes['passwd'] :
 128:             '';
 129: 
 130:         // Don't pass tags to the share creation method.
 131:         if (isset($attributes['tags'])) {
 132:             $tags = $attributes['tags'];
 133:             unset($attributes['tags']);
 134:         } else {
 135:             $tags = array();
 136:         }
 137: 
 138:         // Check for slug uniqueness
 139:         if (!empty($attributes['slug']) &&
 140:             $this->galleryExists(null, $attributes['slug'])) {
 141:             throw new Horde_Exception(
 142:                 sprintf(_("The slug \"%s\" already exists."), $attributes['slug']));
 143:         }
 144: 
 145:         // Create the gallery's share, and then the gallery.
 146:         try {
 147:             $gallery_share = $this->_shares->newShare(
 148:                 $GLOBALS['registry']->getAuth(),
 149:                 strval(new Horde_Support_Randomid()),
 150:                 $attributes['name']);
 151:         } catch (Horde_Share_Exception $e) {
 152:             Horde::logMessage($e->getMessage, 'ERR');
 153:             throw new Ansel_Exception($e);
 154:         }
 155:         $gallery = $this->buildGallery($gallery_share);
 156: 
 157:         // Set the gallery's parent if needed, and clear the parent's cache
 158:         if (!is_null($parent)) {
 159:             $gallery->setParent($parent);
 160:             if ($GLOBALS['conf']['ansel_cache']['usecache']) {
 161:                 $GLOBALS['injector']->getInstance('Horde_Cache')
 162:                     ->expire('Ansel_Gallery' . $parent);
 163:             }
 164:         }
 165: 
 166:         // Fill up the new gallery
 167:         foreach ($attributes as $key => $value) {
 168:             if ($key != 'name') {
 169:                 $gallery->set($key, $value);
 170:             }
 171:         }
 172: 
 173:         // Save it to storage
 174:         try {
 175:             $result = $this->_shares->addShare($gallery_share);
 176:         } catch (Horde_Share_Exception $e) {
 177:             $error = sprintf(_("The gallery \"%s\" could not be created: %s"),
 178:                              $attributes['name'], $e->getMessage());
 179:             Horde::logMessage($error, 'ERR');
 180:             throw new Ansel_Exception($error);
 181:         }
 182: 
 183:         // Add default permissions.
 184:         if (empty($perm)) {
 185:             $perm = $gallery->getPermission();
 186: 
 187:             // Default permissions for logged in users
 188:             switch ($GLOBALS['prefs']->getValue('default_permissions')) {
 189:             case 'read':
 190:                 $perms = Horde_Perms::SHOW | Horde_Perms::READ;
 191:                 break;
 192:             case 'edit':
 193:                 $perms = Horde_Perms::SHOW | Horde_Perms::READ | Horde_Perms::EDIT;
 194:                 break;
 195:             case 'none':
 196:                 $perms = 0;
 197:                 break;
 198:             }
 199:             $perm->addDefaultPermission($perms, false);
 200: 
 201:             // Default guest permissions
 202:             switch ($GLOBALS['prefs']->getValue('guest_permissions')) {
 203:             case 'read':
 204:                 $perms = Horde_Perms::SHOW | Horde_Perms::READ;
 205:                 break;
 206:             case 'none':
 207:             default:
 208:                 $perms = 0;
 209:                 break;
 210:             }
 211:             $perm->addGuestPermission($perms, false);
 212: 
 213:             // Default user groups permissions
 214:             switch ($GLOBALS['prefs']->getValue('group_permissions')) {
 215:             case 'read':
 216:                 $perms = Horde_Perms::SHOW | Horde_Perms::READ;
 217:                 break;
 218:             case 'edit':
 219:                 $perms = Horde_Perms::SHOW | Horde_Perms::READ | Horde_Perms::EDIT;
 220:                 break;
 221:             case 'delete':
 222:                 $perms = Horde_Perms::SHOW | Horde_Perms::READ | Horde_Perms::EDIT | Horde_Perms::DELETE;
 223:                 break;
 224:             case 'none':
 225:             default:
 226:                 $perms = 0;
 227:                 break;
 228:             }
 229: 
 230:             if ($perms) {
 231:                 $group_list = $GLOBALS['injector']
 232:                     ->getInstance('Horde_Group')
 233:                     ->listGroups($GLOBALS['registry']->getAuth());
 234:                 if (count($group_list)) {
 235:                     foreach ($group_list as $group_id => $group_name) {
 236:                         $perm->addGroupPermission($group_id, $perms, false);
 237:                     }
 238:                 }
 239:             }
 240:         }
 241:         $gallery->setPermission($perm);
 242: 
 243:         // Initial tags
 244:         if (count($tags)) {
 245:             $gallery->setTags($tags);
 246:         }
 247: 
 248:         return $gallery;
 249:     }
 250: 
 251:     /**
 252:      * Retrieve an Ansel_Gallery given the gallery's slug
 253:      *
 254:      * @param string $slug      The gallery slug
 255:      * @param array $overrides  An array of attributes that should be overridden
 256:      *                          when the gallery is returned.
 257:      *
 258:      * @return Ansel_Gallery The gallery object
 259:      * @throws Horde_Exception_NotFound
 260:      */
 261:     public function getGalleryBySlug($slug, array $overrides = array())
 262:     {
 263:         $shares = $this->buildGalleries(
 264:             $this->_shares->listShares(
 265:                 $GLOBALS['registry']->getAuth(),
 266:                 array('attributes' => array('slug' => $slug))));
 267:         if (!count($shares)) {
 268:             throw new Horde_Exception_NotFound(sprintf(_("Gallery %s not found."), $slug));
 269:         }
 270: 
 271:         return current($shares);
 272:      }
 273: 
 274:     /**
 275:      * Retrieve an Ansel_Gallery given the share id
 276:      *
 277:      * @param integer $gallery_id  The gallery_id to fetch
 278:      * @param array $overrides     An array of attributes that should be
 279:      *                             overridden when the gallery is returned.
 280:      *
 281:      * @return Ansel_Gallery
 282:      * @throws Ansel_Exception
 283:      */
 284:     public function getGallery($gallery_id, array $overrides = array())
 285:     {
 286:         if (!count($overrides) && $GLOBALS['conf']['ansel_cache']['usecache'] &&
 287:             ($gallery = $GLOBALS['injector']->getInstance('Horde_Cache')->get('Ansel_Gallery' . $gallery_id, $GLOBALS['conf']['cache']['default_lifetime'])) !== false) {
 288: 
 289:             if ($cached_gallery = unserialize($gallery)) {
 290:                 return $cached_gallery;
 291:             }
 292:         }
 293: 
 294:         try {
 295:             $result = $this->buildGallery(
 296:                 $this->_shares->getShareById($gallery_id)
 297:             );
 298:         } catch (Horde_Share_Exception $e) {
 299:             throw new Ansel_Exception($e);
 300:         }
 301:         // Don't cache if we have overridden anything
 302:         if (!count($overrides)) {
 303:             if ($GLOBALS['conf']['ansel_cache']['usecache']) {
 304:                 $GLOBALS['injector']->getInstance('Horde_Cache')
 305:                     ->set('Ansel_Gallery' . $gallery_id, serialize($result));
 306:             }
 307:         } else {
 308:             foreach ($overrides as $key => $value) {
 309:                 $result->set($key, $value, false);
 310:             }
 311:         }
 312: 
 313:         return $result;
 314:     }
 315: 
 316:     /**
 317:      * Retrieve an array of Ansel_Gallery objects for the given slugs.
 318:      *
 319:      * @param array $slugs  The gallery slugs.
 320:      *
 321:      * @return array  An array of Ansel_Gallery objects.
 322:      * @throws Ansel_Exception
 323:      */
 324:     public function getGalleriesBySlugs(array $slugs, $perms = Horde_Perms::SHOW)
 325:     {
 326:         try {
 327:             return $this->buildGalleries(
 328:                 $this->_shares->listShares(
 329:                     $GLOBALS['registry']->getAuth(),
 330:                     array(
 331:                         'perm' => $perms,
 332:                         'attribtues' => array('slugs' => $slugs))));
 333:         } catch (Horde_Share_Exception $e) {
 334:             throw new Ansel_Exception($e);
 335:         }
 336:     }
 337: 
 338:     /**
 339:      * Retrieve an array of Ansel_Gallery objects for the requested ids
 340:      *
 341:      * @param array $ids      Gallery ids to fetch
 342:      * @param integer $perms  Horde_Perms constant for the perms required.
 343:      *
 344:      * @return array  An array of Ansel_Gallery objects
 345:      * @throws Ansel_Exception
 346:      */
 347:     public function getGalleries(array $ids, $perms = Horde_Perms::SHOW)
 348:     {
 349:         try {
 350:             $shares = $this->buildGalleries(
 351:                 $this->_shares->getShares($ids));
 352:         } catch (Horde_Share_Exception $e) {
 353:             throw new Ansel_Exception($e);
 354:         }
 355:         $galleries = array();
 356:         foreach ($shares as $gallery) {
 357:             if ($gallery->hasPermission($GLOBALS['registry']->getAuth(), $perms)) {
 358:                 $galleries[] = $gallery;
 359:             }
 360:         }
 361: 
 362:         return $galleries;
 363:     }
 364: 
 365:     /**
 366:      * Empties a gallery of all images.
 367:      *
 368:      * @param Ansel_Gallery $gallery  The ansel gallery to empty.
 369:      *
 370:      * @throws Ansel_Exception
 371:      */
 372:     public function emptyGallery(Ansel_Gallery $gallery)
 373:     {
 374:         $gallery->clearStacks();
 375:         $images = $gallery->listImages();
 376:         foreach ($images as $image) {
 377:             // Pretend we are a stack so we don't update the images count
 378:             // for every image deletion, since we know the end result will
 379:             // be zero.
 380:             try {
 381:                 $gallery->removeImage($image, true);
 382:             } catch (Horde_Exception_NotFound $e) {
 383:                 throw new Ansel_Exception($e);
 384:             }
 385:         }
 386:         $gallery->set('images', 0, true);
 387: 
 388:         // Clear the OtherGalleries widget cache
 389:         if ($GLOBALS['conf']['ansel_cache']['usecache']) {
 390:             $GLOBALS['injector']
 391:                 ->getInstance('Horde_Cache')
 392:                 ->expire('Ansel_OtherGalleries' . $gallery->get('owner'));
 393:             $GLOBALS['injector']
 394:                 ->getInstance('Horde_Cache')
 395:                 ->expire('Ansel_Gallery' . $gallery->id);
 396:         }
 397:     }
 398: 
 399:     /**
 400:      * Removes an Ansel_Gallery.
 401:      *
 402:      * @param Ansel_Gallery $gallery  The gallery to delete
 403:      *
 404:      * @throws Ansel_Exception
 405:      */
 406:     public function removeGallery(Ansel_Gallery $gallery)
 407:     {
 408:         // Get any children and empty them
 409:         $children = $gallery->getChildren(null, null, true);
 410:         foreach ($children as $child) {
 411:             $this->emptyGallery($child);
 412:             $child->setTags(array());
 413:         }
 414: 
 415:         // Now empty the selected gallery of images
 416:         $this->emptyGallery($gallery);
 417: 
 418:         // Clear all the tags.
 419:         $gallery->setTags(array());
 420: 
 421:         // Get the parent, if it exists, before we delete the gallery.
 422:         $parent = $gallery->getParent();
 423:         $id = $gallery->id;
 424: 
 425:         // Delete the gallery from storage
 426:         try {
 427:             $this->_shares->removeShare($gallery->getShare());
 428:         } catch (Horde_Share_Exception $e) {
 429:             throw new Ansel_Exception($e);
 430:         }
 431: 
 432:         // Expire the cache
 433:         if ($GLOBALS['conf']['ansel_cache']['usecache']) {
 434:             $GLOBALS['injector']->getInstance('Horde_Cache')
 435:                 ->expire('Ansel_Gallery' . $id);
 436:         }
 437: 
 438:         // See if we need to clear the has_subgalleries field
 439:         if ($parent instanceof Ansel_Gallery) {
 440:             if (!$parent->countChildren($GLOBALS['registry']->getAuth(), Horde_Perms::SHOW, false)) {
 441:                 $parent->set('has_subgalleries', 0, true);
 442:                 if ($GLOBALS['conf']['ansel_cache']['usecache']) {
 443:                     $GLOBALS['injector']
 444:                         ->getInstance('Horde_Cache')
 445:                         ->expire('Ansel_Gallery' . $parent->id);
 446:                 }
 447:             }
 448:         }
 449:     }
 450: 
 451:     /**
 452:      * Returns the image corresponding to the given id.
 453:      *
 454:      * @param integer $id  The image_id of the image to retrieve.
 455:      *
 456:      * @return Ansel_Image  The image object requested..
 457:      * @throws Ansel_Exception, Horde_Exception_NotFound
 458:      */
 459:     public function &getImage($id)
 460:     {
 461:         if (isset($this->_images[$id])) {
 462:             return $this->_images[$id];
 463:         }
 464: 
 465:         $q = 'SELECT ' . $this->_getImageFields()
 466:             . ' FROM ansel_images WHERE image_id = ?';
 467:         try {
 468:             $image = $this->_db->selectOne($q, array((int)$id));
 469:         } catch (Horde_Db_Exception $e) {
 470:             throw new Ansel_Exception($e);
 471:         }
 472: 
 473:         if (!$image) {
 474:             throw new Horde_Exception_NotFound(_("Photo not found"));
 475:         } else {
 476:             $image['image_filename'] = Horde_String::convertCharset(
 477:                 $image['image_filename'],
 478:                 $GLOBALS['conf']['sql']['charset'],
 479:                 'UTF-8');
 480:             $image['image_caption'] = Horde_String::convertCharset(
 481:                 $image['image_caption'],
 482:                 $GLOBALS['conf']['sql']['charset'],
 483:                 'UTF-8');
 484:             $this->_images[$id] = new Ansel_Image($image);
 485: 
 486:             return $this->_images[$id];
 487:         }
 488:     }
 489: 
 490:     /**
 491:      * Save image details to storage. Does NOT update the cached image files.
 492:      *
 493:      * @param Ansel_Image $image  The image to save.
 494:      *
 495:      * @return integer The image id
 496:      * @throws Ansel_Exception
 497:      */
 498:     public function saveImage(Ansel_Image $image)
 499:     {
 500:         // If we have an id, then it's an existing image.
 501:         if ($image->id) {
 502:             $update = 'UPDATE ansel_images SET image_filename = ?, '
 503:                 . 'image_type = ?, image_caption = ?, image_sort = ?, '
 504:                 . 'image_original_date = ?, image_latitude = ?, '
 505:                 . 'image_longitude = ?, image_location = ?, '
 506:                 . 'image_geotag_date = ? WHERE image_id = ?';
 507:             try {
 508:                return $this->_db->update(
 509:                    $update,
 510:                    array(Horde_String::convertCharset($image->filename, 'UTF-8', $GLOBALS['conf']['sql']['charset']),
 511:                          $image->type,
 512:                          Horde_String::convertCharset($image->caption, 'UTF-8', $GLOBALS['conf']['sql']['charset']),
 513:                          $image->sort,
 514:                          $image->originalDate,
 515:                          $image->lat,
 516:                          $image->lng,
 517:                          $image->location,
 518:                          $image->geotag_timestamp,
 519:                          $image->id));
 520:             } catch (Horde_Db_Exception $e) {
 521:                 throw new Ansel_Exception($e);
 522:             }
 523:         }
 524: 
 525:         // Saving a new Image
 526:         if (!$image->gallery || !strlen($image->filename) || !$image->type) {
 527:             throw new Ansel_Exception('Incomplete photo');
 528:         }
 529: 
 530:         // Prepare the SQL statement
 531:         $insert = 'INSERT INTO ansel_images (gallery_id, image_filename, '
 532:             . 'image_type, image_caption, image_uploaded_date, image_sort, '
 533:             . 'image_original_date, image_latitude, image_longitude, '
 534:             . 'image_location, image_geotag_date) VALUES '
 535:             . '(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
 536: 
 537:         try {
 538:             $image->id = $this->_db->insert(
 539:                 $insert,
 540:                 array($image->gallery,
 541:                       Horde_String::convertCharset(
 542:                           $image->filename,
 543:                           'UTF-8',
 544:                           $GLOBALS['conf']['sql']['charset']),
 545:                       $image->type,
 546:                       Horde_String::convertCharset(
 547:                           $image->caption,
 548:                           'UTF-8',
 549:                           $GLOBALS['conf']['sql']['charset']),
 550:                       $image->uploaded,
 551:                       $image->sort,
 552:                       $image->originalDate,
 553:                       $image->lat,
 554:                       $image->lng,
 555:                       $image->location,
 556:                      (empty($image->lat) ? 0 : $image->uploaded)));
 557:         } catch (Horde_Db_Exception $e) {
 558:             throw new Ansel_Exception($e);
 559:         }
 560: 
 561:         return $image->id;
 562:     }
 563: 
 564:     /**
 565:      * Store an image attribute to storage
 566:      *
 567:      * @param integer $image_id    The image id
 568:      * @param string  $attributes  The attribute name
 569:      * @param string  $value       The attrbute value
 570:      *
 571:      * @throws Ansel_Exception
 572:      */
 573:     public function saveImageAttribute($image_id, $attribute, $value)
 574:     {
 575:         try {
 576:             $this->_db->insert(
 577:                 'INSERT INTO ansel_image_attributes '
 578:                 . '(image_id, attr_name, attr_value) VALUES (?, ?, ?)',
 579:                 array(
 580:                     $image_id,
 581:                     $attribute,
 582:                     Horde_String::convertCharset($value, 'UTF-8', $GLOBALS['conf']['sql']['charset'])));
 583:         } catch (Horde_Db_Exception $e) {
 584:             throw new Ansel_Exception($e);
 585:         }
 586:     }
 587: 
 588:     /**
 589:      * Clears an image's attributes from storage.
 590:      *
 591:      * @param integer $image_id  The image to clear
 592:      *
 593:      * @throws Ansel_Exception
 594:      */
 595:     public function clearImageAttributes($image_id)
 596:     {
 597:         try {
 598:             $this->_db->delete('DELETE FROM ansel_image_attributes WHERE image_id = ' . (int)$image_id);
 599:         } catch (Horde_Db_Exception $e) {
 600:             throw new Ansel_Exception($e);
 601:         }
 602:     }
 603: 
 604:     /**
 605:      * Get image's attribtues from storage
 606:      *
 607:      * @param int $image_id  The image id
 608:      *
 609:      * @return array  A image attribute hash
 610:      * @throws Horde_Exception
 611:      */
 612:     public function getImageAttributes($image_id)
 613:     {
 614:         try {
 615:             return $this->_db->selectAssoc(
 616:                 'SELECT attr_name, attr_value FROM ansel_image_attributes WHERE '
 617:                 . ' image_id = ' . (int)$image_id);
 618:         } catch (Horde_Db_Exception $e) {
 619:             throw new Ansel_Exception($e);
 620:         }
 621: 
 622:         return $results;
 623:     }
 624: 
 625:     /**
 626:      * Set image sort order
 627:      *
 628:      * @param integer $imageId  The image id
 629:      * @param integer $pos      The new sort order position
 630:      *
 631:      * @throws Ansel_Exception
 632:      */
 633:     public function setImageSortOrder($imageId, $pos)
 634:     {
 635:          try {
 636:            $this->_db->update(
 637:                'UPDATE ansel_images SET image_sort = '
 638:                . (int)$pos . ' WHERE image_id = ' . (int)$imageId);
 639:         } catch (Horde_Db_Exception $e) {
 640:             Horde::logMessage($e->getMessage(), 'ERR');
 641:             throw new Horde_Exception($e);
 642:         }
 643:     }
 644: 
 645:     /**
 646:      * Return the images corresponding to the given ids.
 647:      *
 648:      * @param array $params function parameters:
 649:      *  <pre>
 650:      *    'ids'        - An array of image ids to fetch.
 651:      *    'preserve'   - Preserve the order of the image ids when returned.
 652:      *    'gallery_id' - Return all images from requested gallery (ignores 'ids').
 653:      *    'from'       - If passing a gallery, start at this image.
 654:      *    'count'      - If passing a gallery, return this many images.
 655:      *  </pre>
 656:      *
 657:      * @return array An array of Ansel_Image objects.
 658:      * @throws Ansel_Exception, Horde_Exception_NotFound, InvalidArgumentException
 659:      */
 660:     public function getImages(array $params = array())
 661:     {
 662:         // First check if we want a specific gallery or a list of images
 663:         if (!empty($params['gallery_id'])) {
 664:             $sql = 'SELECT ' . $this->_getImageFields()
 665:                 . ' FROM ansel_images WHERE gallery_id = '
 666:                 . $params['gallery_id'] . ' ORDER BY image_sort';
 667:         } elseif (!empty($params['ids']) && is_array($params['ids']) && count($params['ids']) > 0) {
 668:             $sql = 'SELECT ' . $this->_getImageFields() . ' FROM ansel_images WHERE image_id IN (';
 669:             $i = 1;
 670:             $cnt = count($params['ids']);
 671:             foreach ($params['ids'] as $id) {
 672:                 $sql .= (int)$id . (($i++ < $cnt) ? ',' : ');');
 673:             }
 674:         } else {
 675:             throw new InvalidArgumentException('Ansel_Storage::getImages requires either a gallery_id or an array of image ids');
 676:         }
 677: 
 678:         // Limit the query?
 679:         if (isset($params['count']) && isset($params['from'])) {
 680:             $sql = $this->_db->addLimitOffset($sql, array('limit' => $params['count'], 'offset' => $params['from']));
 681:         }
 682:         try {
 683:             $images = $this->_db->select($sql);
 684:         } catch (Horde_Db_Exception $e) {
 685:             throw new Ansel_Exception($images);
 686:         }
 687:         // Throw exception if we asked for specific image ids and not found.
 688:         if (empty($images) && empty($params['gallery_id'])) {
 689:             throw new Horde_Exception_NotFound(_("Images not found"));
 690:         } elseif (empty($images)) {
 691:             return array();
 692:         }
 693: 
 694:         $return = array();
 695:         foreach ($images as $image) {
 696:             $image['image_filename'] = Horde_String::convertCharset($image['image_filename'], $GLOBALS['conf']['sql']['charset'], 'UTF-8');
 697:             $image['image_caption'] = Horde_String::convertCharset($image['image_caption'], $GLOBALS['conf']['sql']['charset'], 'UTF-8');
 698:             $return[$image['image_id']] = new Ansel_Image($image);
 699:             $this->_images[(int)$image['image_id']] = &$return[$image['image_id']];
 700:         }
 701: 
 702:         // Need to get comment counts if comments are enabled
 703:         $ccounts = $this->_getImageCommentCounts(array_keys($return));
 704:         if (count($ccounts)) {
 705:             foreach ($return as $key => $image) {
 706:                 $return[$key]->commentCount = (!empty($ccounts[$key]) ? $ccounts[$key] : 0);
 707:             }
 708:         }
 709: 
 710:         // Preserve the order the images_ids were passed in
 711:         if (empty($params['gallery_id']) && !empty($params['preserve'])) {
 712:             foreach ($params['ids'] as $id) {
 713:                 $ordered[$id] = $return[$id];
 714:             }
 715:             return $ordered;
 716:         }
 717: 
 718:         return $return;
 719:     }
 720: 
 721:     /**
 722:      * Get the total number of comments for an image.
 723:      *
 724:      * @param array $ids  Array of image ids
 725:      *
 726:      * @return array of results. @see forums/numMessagesBatch api call
 727:      */
 728:     protected function _getImageCommentCounts(array $ids)
 729:     {
 730:         global $conf, $registry;
 731: 
 732:         // Need to get comment counts if comments are enabled
 733:         if (($conf['comments']['allow'] == 'all' || ($conf['comments']['allow'] == 'authenticated' && $GLOBALS['registry']->getAuth())) &&
 734:             $registry->hasMethod('forums/numMessagesBatch')) {
 735: 
 736:             return $registry->call('forums/numMessagesBatch', array($ids, 'ansel'));
 737:         }
 738: 
 739:         return array();
 740:     }
 741: 
 742:     /**
 743:      * Returns a list of Ansel_Images of the most recently added images for the
 744:      * current user.
 745:      *
 746:      * @param array $galleries  An array of gallery ids to search in. If
 747:      *                          left empty, will search all galleries
 748:      *                          with Horde_Perms::SHOW.
 749:      * @param integer $limit    The maximum number of images to return
 750:      * @param string $slugs     An array of gallery slugs.
 751:      * @param string $where     Additional where clause
 752:      *
 753:      * @return array An array of Ansel_Image objects
 754:      * @throws Ansel_Exception
 755:      */
 756:     public function getRecentImages(array $galleries = array(), $limit = 10, array $slugs = array())
 757:     {
 758:         $results = array();
 759: 
 760:         if (!count($galleries) && !count($slugs)) {
 761:             // Don't need the Ansel_Gallery object, so save some resources and
 762:             // only query the share system.
 763:             foreach ($this->_shares->listShares($GLOBALS['registry']->getAuth()) as $share) {
 764:                 $galleries[] = $share->getId();
 765:             }
 766:             if (empty($galleries)) {
 767:                 return array();
 768:             }
 769:         }
 770:         if (!count($slugs)) {
 771:             // Searching by gallery_id
 772:             $sql = 'SELECT ' . $this->_getImageFields() . ' FROM ansel_images '
 773:                    . 'WHERE gallery_id IN ('
 774:                    . str_repeat('?, ', count($galleries) - 1) . '?) ';
 775:             $criteria = $galleries;
 776:         } elseif (count($slugs)) {
 777:             // Searching by gallery_slug so we need to join the share table
 778:             $sql = 'SELECT ' . $this->_getImageFields() . ' FROM ansel_images LEFT JOIN '
 779:                 . $this->_shares->getTable() . ' ON ansel_images.gallery_id = '
 780:                 . $this->_shares->getTable() . '.share_id ' . 'WHERE attribute_slug IN ('
 781:                 . str_repeat('?, ', count($slugs) - 1) . '?) ';
 782:             $criteria = $slugs;
 783:         }
 784: 
 785:         $sql .= ' ORDER BY image_uploaded_date DESC';
 786:         if ($limit > 0) {
 787:             $sql = $this->_db->addLimitOffset($sql, array('limit' => (int)$limit));
 788:         }
 789:         try {
 790:             $images = $this->_db->selectAll($sql, $criteria);
 791:         } catch (Horde_Db_Exception $e) {
 792:             throw new Ansel_Exception($e);
 793:         }
 794: 
 795:         foreach($images as $image) {
 796:             $image['image_filename'] = Horde_String::convertCharset($image['image_filename'], $GLOBALS['conf']['sql']['charset'], 'UTF-8');
 797:             $image['image_caption'] = Horde_String::convertCharset($image['image_caption'], $GLOBALS['conf']['sql']['charset'], 'UTF-8');
 798:             $results[] = new Ansel_Image($image);
 799:         }
 800: 
 801:         return $results;
 802:     }
 803: 
 804:     /**
 805:      * Check if a gallery exists. Need to do this here so we can also check by
 806:      * gallery slug.
 807:      *
 808:      * @param integer $gallery_id  The gallery id
 809:      * @param string  $slug        The gallery slug
 810:      *
 811:      * @return boolean
 812:      * @throws Ansel_Exception
 813:      */
 814:     public function galleryExists($gallery_id = null, $slug = null)
 815:     {
 816:         if (empty($slug)) {
 817:             $results = $this->_shares->idExists($gallery_id);
 818:         } else {
 819:             $results = $this->_shares->countShares($GLOBALS['registry']->getAuth(), Horde_Perms::READ, array('slug' => $slug));
 820:         }
 821: 
 822:         return (bool)$results;
 823:     }
 824: 
 825:    /**
 826:     * Return the count of galleries that the user has specified permissions to
 827:     * and that match any of the requested attributes.
 828:     *
 829:     * @param string userid  The user to check access for.
 830:     * @param array $params  Parameter array:
 831:     *<pre>
 832:     *  (integer)perm          The level of permissions to require for a
 833:     *                         gallery to return it [Horde_Perms::SHOW]
 834:     *  (mixed)attributes      Restrict the galleries counted to those
 835:     *                         matching $attributes. An array of
 836:     *                         attribute/values pairs or a gallery owner
 837:     *                         username.
 838:     * (Ansel_Gallery)parent   The parent share to start counting at.
 839:     * (boolean)all_levels      Return all levels, or just the direct children of
 840:     *                         $parent? [true]
 841:     * (array)tags             Filter results by galleries tagged with tags.
 842:     *</pre>
 843:     *
 844:     * @return integer  The count
 845:     * @throws Ansel_Exception
 846:     */
 847:     public function countGalleries($userid, array $params = array())
 848:     {
 849:         static $counts;
 850: 
 851:         $oparams = new Horde_Support_Array($params);
 852:         if ($oparams->parent) {
 853:             $parent_id = $oparams->parent->id;
 854:         } else {
 855:             $parent_id = null;
 856:         }
 857:         $perm = $oparams->get('perm', Horde_Perms::SHOW);
 858:         $key = "$userid,$perm,$parent_id,{$oparams->all_levels}" . serialize($oparams->get('attributes', array())) . serialize($oparams->get('tags', array()));
 859:         if (isset($counts[$key])) {
 860:             return $counts[$key];
 861:         }
 862: 
 863:         // Unfortunately, we need to go the long way around to count shares if
 864:         // we are filtering by tags.
 865:         if ($oparams->tags) {
 866:             $count = count($this->listGalleries($params));
 867:         } else {
 868:             try {
 869:                 $count = $this->_shares->countShares(
 870:                     $userid,
 871:                     $perm, $oparams->get('attributes', array()),
 872:                     $parent_id,
 873:                     $oparams->get('all_levels', true));
 874:             } catch (Horde_Share_Exception $e) {
 875:                 throw new Ansel_Exception($e);
 876:             }
 877:         }
 878:         $counts[$key] = $count;
 879: 
 880:         return $count;
 881:     }
 882: 
 883:    /**
 884:     * Retrieves the current user's gallery list from storage.
 885:     *
 886:     * @param array $params  Optional parameters:
 887:     *   <pre>
 888:     *     (integer)perm      The permissions filter to use [Horde_Perms::SHOW]
 889:     *     (mixed)attributes  Restrict the galleries returned to those matching
 890:     *                        the filters. Can be an array of attribute/values
 891:     *                        pairs or a gallery owner username.
 892:     *     (integer)parent    The parent share to start listing at.
 893:     *     (boolean)all_levels If set, return all levels below parent, not just
 894:     *                        direct children [TRUE]
 895:     *     (integer)from      The gallery to start listing at.
 896:     *     (integer)count     The number of galleries to return.
 897:     *     (string)sort_by    Attribute to sort by.
 898:     *     (integer)direction The direction to sort by [Ansel::SORT_ASCENDING]
 899:     *     (array)tags        An array of tags to limit results by.
 900:     *   </pre>
 901:     *
 902:     * @return array An array of Ansel_Gallery objects
 903:     * @throws Ansel_Exception
 904:     */
 905:     public function listGalleries($params = array())
 906:     {
 907:         $galleries = array();
 908:         try {
 909:             if (!empty($params['tags'])) {
 910:                 $count = !empty($params['count']) ? $params['count'] : null;
 911:                 $from = !empty($params['from']) ? $params['from'] : null;
 912:                 unset($params['count'], $params['from']);
 913:                 $shares = $this->_shares->listShares($GLOBALS['registry']->getAuth(), $params);
 914:                 if (!empty($params['attributes']) && !is_array($params['attributes'])) {
 915:                     $user = $params['attributes'];
 916:                 } elseif (!empty($params['attributes']['owner'])) {
 917:                     $user = $params['attributes']['owner'];
 918:                 } else {
 919:                     $user = null;
 920:                 }
 921:                 $tagged = $GLOBALS['injector']
 922:                     ->getInstance('Ansel_Tagger')
 923:                     ->search(
 924:                         $params['tags'],
 925:                         array(
 926:                             'type' => 'gallery',
 927:                             'user' => $user));
 928: 
 929:                 foreach ($shares as $share) {
 930:                     if (in_array($share->getId(), $tagged['galleries'])) {
 931:                         $galleries[] = $share;
 932:                     }
 933:                 }
 934:                 $galleries = array_slice($galleries, $from, $count);
 935:             } else {
 936:                 $galleries = $this->_shares->listShares($GLOBALS['registry']->getAuth(), $params);
 937:             }
 938:             $shares = $this->buildGalleries($galleries);
 939:         } catch (Horde_Share_Exception $e) {
 940:             throw new Ansel_Exception($e);
 941:         }
 942: 
 943:         return $shares;
 944:     }
 945: 
 946:     /**
 947:      * Returns a list of ALL galleries, regardless of permissions.
 948:      *
 949:      * @return array
 950:      */
 951:     public function listAllGalleries()
 952:     {
 953:         return $this->buildGalleries($this->_shares->listAllShares());
 954:     }
 955: 
 956:     /**
 957:      * Retrieve json data for an arbitrary list of image ids, not necessarily
 958:      * from the same gallery.
 959:      *
 960:      * @param array $images        An array of image ids
 961:      * @param Ansel_Style $style   A gallery style to force if requesting
 962:      *                             pretty thumbs.
 963:      * @param boolean $full        Generate full urls
 964:      * @param string $image_view   Which image view to use? screen, thumb etc..
 965:      * @param boolean $view_links  Include links to the image view
 966:      *
 967:      * @return string  The json data
 968:      */
 969:     public function getImageJson(array $images, Ansel_Style $style = null,
 970:         $full = false, $image_view = 'mini', $view_links = false)
 971:     {
 972:         $galleries = array();
 973:         if (is_null($style)) {
 974:             $style = Ansel::getStyleDefinition('ansel_default');
 975:         }
 976: 
 977:         $json = array();
 978: 
 979:         foreach ($images as $id) {
 980:             $image = $this->getImage($id);
 981:             $gallery_id = abs($image->gallery);
 982:             if (empty($galleries[$gallery_id])) {
 983:                 $galleries[$gallery_id]['gallery'] = $GLOBALS['injector']->getInstance('Ansel_Storage')->getGallery($gallery_id);
 984:             }
 985: 
 986:             // Any authentication that needs to take place for any of the
 987:             // images included here MUST have already taken place or the
 988:             // image will not be incldued in the output.
 989:             if (!isset($galleries[$gallery_id]['perm'])) {
 990:                 $galleries[$gallery_id]['perm'] =
 991:                     ($galleries[$gallery_id]['gallery']->hasPermission($GLOBALS['registry']->getAuth(), Horde_Perms::READ) &&
 992:                      $galleries[$gallery_id]['gallery']->isOldEnough() &&
 993:                      !$galleries[$gallery_id]['gallery']->hasPasswd());
 994:             }
 995: 
 996:             if ($galleries[$gallery_id]['perm']) {
 997:                 $data = array((string)Ansel::getImageUrl($image->id, $image_view, $full, $style),
 998:                     htmlspecialchars($image->filename),
 999:                     $GLOBALS['injector']->getInstance('Horde_Core_Factory_TextFilter')->filter($image->caption, 'text2html', array('parselevel' => Horde_Text_Filter_Text2html::MICRO_LINKURL)),
1000:                     $image->id,
1001:                     0);
1002: 
1003:                 if ($view_links) {
1004:                     $data[] = (string)Ansel::getUrlFor('view',
1005:                         array('gallery' => $image->gallery,
1006:                               'image' => $image->id,
1007:                               'view' => 'Image',
1008:                               'slug' => $galleries[$gallery_id]['gallery']->get('slug')),
1009:                         $full);
1010: 
1011:                     $data[] = (string)Ansel::getUrlFor('view',
1012:                         array('gallery' => $image->gallery,
1013:                               'slug' => $galleries[$gallery_id]['gallery']->get('slug'),
1014:                               'view' => 'Gallery'),
1015:                         $full);
1016:                 }
1017: 
1018:                 $json[] = $data;
1019:             }
1020: 
1021:         }
1022: 
1023:         return Horde_Serialize::serialize($json, Horde_Serialize::JSON);
1024:     }
1025: 
1026:     /**
1027:      * Returns a random Ansel_Gallery from a list fitting the search criteria.
1028:      *
1029:      * @see Ansel_Storage::listGalleries()
1030:      */
1031:     public function getRandomGallery(array $params = array())
1032:     {
1033:         $galleries = $this->listGalleries($params);
1034:         if (!$galleries) {
1035:             return false;
1036:         }
1037: 
1038:         return $galleries[array_rand($galleries)];
1039:     }
1040: 
1041:     /**
1042:      * Lists a slice of the image ids in the given gallery.
1043:      *
1044:      * @param array $params  Filter parameters.
1045:      *<pre>
1046:      *  integer|array 'gallery_id'  - A gallery id to list images from
1047:      *  integer 'offset'            - The image to start listing from
1048:      *  integer 'limit'             - How many images to return
1049:      *  array|string 'fields'       - The fields to return
1050:      *  string 'sort'               - The field to sort by.
1051:      *  array  'filter'             - Additional filters. Each element is an
1052:      *                                array containing 'property', 'op', and
1053:      *                                'value' keys. Passing 'IN' as the 'op'
1054:      *                                and an array as 'value' will produce a
1055:      *                                SQL IN conditional.
1056:      *</pre>
1057:      *
1058:      * @return array  An array of images. Either an array of ids, or an array
1059:      *                of field values, keyed by id.
1060:      * @throws Ansel_Exception, InvalidArgumentException
1061:      */
1062:     public function listImages(array $params = array())
1063:     {
1064:         $params = new Horde_Support_Array($params);
1065:         if (is_array($params['fields'])) {
1066:             $field_count = count($params['fields']);
1067:             $params['fields'] = implode(', ', $params['fields']);
1068:         } elseif ($params['fields'] == '*') {
1069:             // The count is not important, as long as it's > 1
1070:             $field_count = 2;
1071:         } else {
1072:             $field_count = substr_count($params->get('fields', 'image_id'), ',') + 1;
1073:         }
1074: 
1075:         if (is_array($params['sort'])) {
1076:             $params['sort'] = implode(', ', $params['sort']);
1077:         }
1078: 
1079:         if (is_array($params['gallery_id'])) {
1080:             $query_where = 'WHERE gallery_id IN (' . implode(',', $params['gallery_id']) . ')';
1081:         } elseif ($params['gallery_id']) {
1082:             $query_where = 'WHERE gallery_id = ' . $params['gallery_id'];
1083:         } else {
1084:             $query_where = '';
1085:         }
1086:         if ($params['filter']) {
1087:             foreach ($params['filter'] as $filter) {
1088:                 $query_where .= (!empty($query_where) ? ' AND ' : ' WHERE ')
1089:                     . $this->_toImageDriverName($filter['property'])
1090:                     . ' ' . $filter['op'] . ' ' .
1091:                     (is_array($filter['value']) ? '(' . implode(',', $filter['value']) . ')' : $filter['value']);
1092:             }
1093:         }
1094:         $sql = 'SELECT ' . $params->get('fields', 'image_id')
1095:             . ' FROM ansel_images ' . $query_where
1096:             . ' ORDER BY ' . $params->get('sort', 'image_sort');
1097:         $sql = $this->_db->addLimitOffset(
1098:             $sql,
1099:             array(
1100:                 'limit' => $params->get('limit', 0),
1101:                 'offset' => $params->get('offset', 0))
1102:         );
1103:         try {
1104:             if ($field_count > 1) {
1105:                 $results = $this->_db->selectAll($sql);
1106:                 $images = array();
1107:                 foreach ($results as $image) {
1108:                     $images[$image['image_id']] = $image;
1109:                 }
1110:                 return $images;
1111:             } else {
1112:                 return $this->_db->selectValues($sql);
1113:             }
1114:         } catch (Horde_Db_Exception $e) {
1115:             throw new Ansel_Exception($e);
1116:         }
1117:     }
1118: 
1119:     /**
1120:      * Return images' geolocation data.
1121:      *
1122:      * @param array $image_ids  An array of image_ids to look up.
1123:      * @param integer $gallery  A gallery id. If this is provided, will return
1124:      *                          all images in the gallery that have geolocation
1125:      *                          data ($image_ids would be ignored).
1126:      *
1127:      * @return array of geodata
1128:      */
1129:     public function getImagesGeodata(array $image_ids = array(), $gallery = null)
1130:     {
1131:         if ((!is_array($image_ids) || count($image_ids) == 0) && empty($gallery)) {
1132:             return array();
1133:         }
1134:         $params = array(
1135:             'fields' => array(
1136:                 'image_id as id',
1137:                 'image_id',
1138:                 'image_latitude',
1139:                 'image_longitude',
1140:                 'image_location'),
1141:             'filter' => array(
1142:                 array(
1143:                     'property' => 'latitude',
1144:                     'op' => '!=',
1145:                     'value' => "''"))
1146:         );
1147:         if (!empty($gallery)) {
1148:             $params['gallery_id'] = (int)$gallery;
1149:         } elseif (count($image_ids) > 0) {
1150:             $params['filter'][] = array(
1151:                 'property' => 'id',
1152:                 'op' => 'IN',
1153:                 'value' => $image_ids);
1154:         } else {
1155:             return array();
1156:         }
1157: 
1158:         return $this->listImages($params);
1159:     }
1160: 
1161:     /**
1162:      * Like getRecentImages, but returns geotag data for the most recently added
1163:      * images from the current user. Useful for providing images to help locate
1164:      * images at the same place.
1165:      *
1166:      * @param string $user    Limit images to this user
1167:      * @param integer $start  Start a slice at this image number
1168:      * @param integer $count  Include this many images
1169:      *
1170:      * @return array An array of image ids
1171:      *
1172:      */
1173:     public function getRecentImagesGeodata($user = null, $start = 0, $count = 8)
1174:     {
1175:         $galleries = $this->listGalleries(
1176:             array(
1177:                 'perm' => Horde_Perms::EDIT,
1178:                 'attributes' => $user
1179:             )
1180:         );
1181:         $ids = array();
1182:         foreach ($galleries as $gallery) {
1183:             $ids[] = $gallery->id;
1184:         }
1185:         if (empty($ids)) {
1186:             return array();
1187:         }
1188: 
1189:         $params = array(
1190:             'offset' => $start,
1191:             'limit' => $count,
1192:             'fields' => array(
1193:                 'image_id as id',
1194:                 'image_id',
1195:                 'gallery_id',
1196:                 'image_latitude',
1197:                 'image_longitude',
1198:                 'image_location'),
1199:             'gallery_id' => $ids,
1200:             'filter' => array(
1201:                 array(
1202:                     'property' => 'latitude',
1203:                     'op' => '!=',
1204:                     'value' => "''")
1205:                 ),
1206:             'sort' => 'image_geotag_date DESC');
1207: 
1208:         return $this->listImages($params);
1209:     }
1210: 
1211:     /**
1212:      * Search for a textual location string from the passed in search token.
1213:      * Used for location autocompletion.
1214:      *
1215:      * @param string $search  Search fragment for autocompleting location strings
1216:      *
1217:      * @return array  The results
1218:      * @throws Ansel_Exception
1219:      */
1220:     public function searchLocations($search = '')
1221:     {
1222:         $sql = 'SELECT DISTINCT image_location, image_latitude, image_longitude FROM ansel_images WHERE LENGTH(image_location) > 0';
1223:         if (strlen($search)) {
1224:             $sql .= ' AND image_location LIKE ' . $this->_db->quoteString("$search%");
1225:         }
1226:         try {
1227:             return $this->_db->selectAll($sql);
1228:         } catch (Horde_Db_Exception $e) {
1229:             throw new Ansel_Exception($e);
1230:         }
1231:     }
1232: 
1233:     /**
1234:      * Set the gallery id for a set of images. Useful for bulk updating images
1235:      * when moving from one gallery to another.
1236:      *
1237:      * @param array $image_ids     An array of image ids
1238:      * @param integer $gallery_id  The gallery id to move the images to.
1239:      *
1240:      * @throws Ansel_Exception
1241:      */
1242:     public function setImagesGallery(array $image_ids, $gallery_id)
1243:     {
1244:         try {
1245:             $this->_db->update('UPDATE ansel_images SET gallery_id = ' . $gallery_id . ' WHERE image_id IN (' . implode(',', $image_ids) . ')');
1246:         } catch (Horde_Db_Exception $e) {
1247:             Horde::logMessage($e->getMessage(), 'ERR');
1248:             throw new Ansel_Exception($e);
1249:         }
1250:     }
1251: 
1252:     /**
1253:      * Deletes an Ansel_Image from data storage.
1254:      *
1255:      * @param integer $image_id  The image id(s) to remove.
1256:      *
1257:      * @throws Ansel_Exception
1258:      */
1259:     public function removeImage($image_id)
1260:     {   try {
1261:             $this->_db->delete('DELETE FROM ansel_images WHERE image_id = ' . (int)$image_id);
1262:             $this->_db->delete('DELETE FROM ansel_image_attributes WHERE image_id = ' . (int)$image_id);
1263:         } catch (Horde_Db_Exception $e) {
1264:             throw new Ansel_Exception($e);
1265:         }
1266:     }
1267: 
1268:     /**
1269:      * Helper function to get a string of field names
1270:      *
1271:      * @return string
1272:      */
1273:     protected function _getImageFields($alias = '')
1274:     {
1275:         $fields = array(
1276:             'image_id', 'gallery_id', 'image_filename', 'image_type',
1277:             'image_caption', 'image_uploaded_date', 'image_sort',
1278:             'image_faces', 'image_original_date', 'image_latitude',
1279:             'image_longitude', 'image_location', 'image_geotag_date');
1280:         if (!empty($alias)) {
1281:             foreach ($fields as $field) {
1282:                 $new[] = $alias . '.' . $field;
1283:             }
1284:             return implode(', ', $new);
1285:         }
1286: 
1287:         return implode(', ', $fields);
1288:     }
1289: 
1290:     /**
1291:      * Ensure the style hash is recorded in the database.
1292:      *
1293:      * @param string $hash  The hash to record.
1294:      */
1295:     public function ensureHash($hash)
1296:     {
1297:         $query = 'SELECT COUNT(*) FROM ansel_hashes WHERE style_hash = ?';
1298:         try {
1299:             $results = $this->_db->selectValue($query, array($hash));
1300:         } catch (Horde_Db_Exception $e) {
1301:             throw new Ansel_Exception($e);
1302:         }
1303:         if (!$results) {
1304:             try {
1305:                 $this->_db->insert('INSERT INTO ansel_hashes (style_hash) VALUES(?)', array($hash));
1306:             } catch (Horde_Db_Exception $e) {
1307:                 throw new Ansel_Exception($e);
1308:             }
1309:         }
1310:     }
1311: 
1312:     /**
1313:      * Get a list of all known styleHashes.
1314:      *
1315:      * @return array  An array of style hashes.
1316:      */
1317:     public function getHashes()
1318:     {
1319:         try {
1320:             return $this->_db->selectValues('SELECT style_hash FROM ansel_hashes');
1321:         } catch (Horde_Db_Exception $e) {
1322:             throw new Ansel_Exception($e);
1323:         }
1324:     }
1325: 
1326:     /**
1327:      * Build a single Ansel_Gallery object from a Horde_Share_Object
1328:      *
1329:      * @param Horde_Share_Object $share  The share
1330:      *
1331:      * @return Ansel_Gallery
1332:      */
1333:     public function buildGallery(Horde_Share_Object $share)
1334:     {
1335:         return current($this->buildGalleries(array($share)));
1336:     }
1337: 
1338:     /**
1339:      * Build an array of Ansel_Gallery objects from an array of
1340:      * Horde_Share_Object objects.
1341:      *
1342:      * @param array $shares  An array of Horde_Share_Object objects.
1343:      *
1344:      * @return array Ansel_Gallery objects.
1345:      */
1346:     public function buildGalleries(array $shares)
1347:     {
1348:         $results = array();
1349:         foreach ($shares as $share) {
1350:             $results[] = new Ansel_Gallery($share);
1351:         }
1352: 
1353:         return $results;
1354:     }
1355: 
1356:     /**
1357:      * Convert an Ansel_Image property to it's backend storage field name.
1358:      *
1359:      * @param string $field  The field name
1360:      *
1361:      * @return string  The converted field name suitable for use in backend.
1362:      */
1363:     protected function _toImageDriverName($field)
1364:     {
1365:         switch ($field) {
1366:             case 'id':
1367:             case 'filename':
1368:             case 'type':
1369:             case 'caption':
1370:             case 'sort':
1371:             case 'faces':
1372:             case 'latitude':
1373:             case 'longitude':
1374:             case 'location':
1375:                 return 'image_' . $field;
1376:             case 'uploadedDate':
1377:                 return 'image_uploaded_date';
1378:             case 'originalDate':
1379:                 return 'image_original_date';
1380:             case 'geotagDate':
1381:                 return 'image_geotag_date';
1382:         }
1383:     }
1384: 
1385: }
1386: 
API documentation generated by ApiGen