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:  * Ansel Base Class.
  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  Chuck Hagenbuch <chuck@horde.org>
 11:  * @author  Michael J. Rubinsky <mrubinsk@horde.org>
 12:  * @package Ansel
 13:  */
 14: class Ansel
 15: {
 16:     // Sort constants
 17:     const SORT_ASCENDING = 0;
 18:     const SORT_DESCENDING = 1;
 19: 
 20:     /**
 21:      * Return a string containing an <option> listing of the given
 22:      * gallery array.
 23:      *
 24:      * @param array $params  An array of options:
 25:      *   <pre>
 26:      *     (integer)selected  The gallery_id of the gallery that is selected
 27:      *     (integer)perm      The permissions filter to use [Horde_Perms::SHOW]
 28:      *     (mixed)attributes  Restrict the galleries returned to those matching
 29:      *                        the filters. Can be an array of attribute/values
 30:      *                        pairs or a gallery owner username.
 31:      *     (boolean)all_levels
 32:      *     (integer)from      The gallery to start listing at.
 33:      *     (integer)count     The number of galleries to return.
 34:      *     (integer)ignore    An Ansel_Gallery id to ignore when building the tree.
 35:      *   </pre>
 36:      *
 37:      * @return string  The HTML to display the option list.
 38:      */
 39:     static public function selectGalleries($params = array())
 40:     {
 41:         $galleries = $GLOBALS['injector']
 42:             ->getInstance('Ansel_Storage')
 43:             ->listGalleries($params);
 44: 
 45:         $params = new Horde_Support_Array($params);
 46: 
 47:         $tree = $GLOBALS['injector']
 48:             ->getInstance('Horde_Core_Factory_Tree')
 49:             ->create('gallery_tree', 'Select');
 50: 
 51:         // Remove the ignored gallery, make sure it's also not the selected
 52:         if ($params->ignore) {
 53:            if ($params->selected == $params->ignore) {
 54:                $params->selected = null;
 55:            }
 56:         }
 57: 
 58:         foreach ($galleries as $gallery) {
 59:             $gallery_id = $gallery->id;
 60:             $gallery_name = $gallery->get('name');
 61:             $label = Horde_String::abbreviate($gallery_name);
 62:             $len = Horde_String::length($gallery_name);
 63:             $treeparams = array();
 64:             $treeparams['selected'] = $gallery_id == $params->selected;
 65:             $parent = $gallery->getParent();
 66:             $parent = empty($parent) ? null : $parent->id;
 67:             $tree->addNode($gallery->id, $parent, $label, null, true, $treeparams);
 68:         }
 69: 
 70:         return $tree->getTree();
 71:     }
 72: 
 73:     /**
 74:      * This photo should be used as a placeholder if the correct photo can't
 75:      * be retrieved
 76:      *
 77:      * @param string $view  The view ('screen', 'thumb', or 'full') to show.
 78:      *                      Defaults to 'screen'.
 79:      *
 80:      * @return string  The image path.
 81:      */
 82:     static public function getErrorImage($view = 'screen')
 83:     {
 84:         return Horde_Themes::img($view . '-error.png');
 85:     }
 86: 
 87:     /**
 88:      * Return a properly formatted link depending on the global pretty url
 89:      * configuration
 90:      *
 91:      * @param string $controller       The controller to generate a URL for.
 92:      * @param array $data              The data needed to generate the URL.
 93:      * @param boolean $full            Generate a full URL.
 94:      * @param integer $append_session  0 = only if needed, 1 = always, -1 = never.
 95:      *
 96:      * @return Horde_Url The generated URL
 97:      */
 98:     static public function getUrlFor($controller, $data, $full = false, $append_session = 0)
 99:     {
100:         global $prefs;
101: 
102:         $rewrite = isset($GLOBALS['conf']['urls']['pretty']) &&
103:                    $GLOBALS['conf']['urls']['pretty'] == 'rewrite';
104: 
105:         switch ($controller ) {
106:         case 'view':
107:             if ($rewrite && (empty($data['special']))) {
108:                 $url = '';
109: 
110:                 // Viewing a List
111:                 if ($data['view'] == 'List') {
112: 
113:                     $groupby = isset($data['groupby'])
114:                         ? $data['groupby']
115:                         : $prefs->getValue('groupby');
116:                     if ($groupby == 'owner' && !empty($data['owner'])) {
117:                         $url = 'user/' . urlencode($data['owner']) . '/';
118:                     } elseif ($groupby == 'owner') {
119:                         $url = 'user/';
120:                     } elseif ($groupby == 'none') {
121:                        $url = 'all/';
122:                     }
123:                     $url = Horde::url($url, $full, $append_session);
124:                     //  don't append the page number if it's zero
125:                     if (!empty($data['page'])) {
126:                         $url->add('page', $data['page']);
127:                     }
128:                     return $url;
129:                 }
130: 
131:                 // Viewing a Gallery or Image
132:                 if ($data['view'] == 'Gallery' || $data['view'] == 'Image') {
133: 
134:                      // @TODO: This is needed to correctly generate URLs in
135:                      // places that are not specifically requested by the user,
136:                      // for instance, in a gallery block. Otherwise, the proper
137:                      // date variables would not be attached to the url, since we
138:                      // don't know them ahead of time.  This is a slight hack and
139:                      // needs to be corrected, probably by delegating at least
140:                      // some of the URL generation to the gallery/image/view
141:                      // object...most likely when we move to PHP5.
142: 
143:                     if (empty($data['year']) && $data['view'] == 'Image') {
144:                         // Getting these objects is not ideal, but at this point
145:                         // they should already be locally cached so the cost
146:                         // is minimized.
147:                         $i = $GLOBALS['injector']
148:                             ->getInstance('Ansel_Storage')
149:                             ->getImage($data['image']);
150:                         $g = $GLOBALS['injector']
151:                             ->getInstance('Ansel_Storage')
152:                             ->getGallery($data['gallery']);
153:                         if ($g->get('view_mode') == 'Date') {
154:                             $imgDate = new Horde_Date($i->originalDate);
155:                             $data['year'] = $imgDate->year;
156:                             $data['month'] = $imgDate->month;
157:                             $data['day'] = $imgDate->mday;
158:                         }
159:                     }
160: 
161:                     $url = 'gallery/'
162:                         . (!empty($data['slug'])
163:                            ? $data['slug']
164:                            : 'id/' . (int)$data['gallery'])
165:                         . '/';
166: 
167:                     // See comments below about lightbox
168:                     if ($data['view'] == 'Image' &&
169:                         (empty($data['gallery_view']) ||
170:                          (!empty($data['gallery_view']) &&
171:                          $data['gallery_view'] != 'GalleryLightbox'))) {
172: 
173:                         $url .= (int)$data['image'] . '/';
174:                     }
175: 
176:                     $extras = array();
177:                     // We may have a value of zero here, but it's the default,
178:                     // so ignore it if it's empty.
179:                     if (!empty($data['havesearch'])) {
180:                         $extras['havesearch'] = $data['havesearch'];
181:                     }
182: 
183:                     // Block any auto navigation (for date views)
184:                     if (!empty($data['force_grouping'])) {
185:                         $extras['force_grouping'] = $data['force_grouping'];
186:                     }
187: 
188:                     $url = new Horde_Url($url);
189:                     if (count($extras)) {
190:                         $url->add($extras);
191:                     }
192: 
193:                     //Slight hack until we delegate at least some of the url
194:                     // generation to the gallery/image/view object.
195:                     if ($data['view'] == 'Image' &&
196:                         !empty($data['gallery_view']) &&
197:                         $data['gallery_view'] == 'GalleryLightbox') {
198:                         $url->setAnchor($data['image']);
199:                     }
200: 
201:                 } elseif ($data['view'] == 'Results')  {
202:                     $url = new Horde_Url('tag/' . (!empty($data['tag'])
203:                                      ? urlencode($data['tag']) . '/'
204:                                      : ''));
205: 
206:                     if (!empty($data['actionID'])) {
207:                         $url->add(array('actionID' => $data['actionID']));
208:                     }
209: 
210:                     if (!empty($data['owner'])) {
211:                         $url->add('owner', $data['owner']);
212:                     }
213:                 }
214: 
215:                 // Keep the URL as clean as possible - don't append the page
216:                 // number if it's zero, which would be the default.
217:                 if (!empty($data['page'])) {
218:                     $url->add('page', $data['page']);
219:                 }
220: 
221:                 if (!empty($data['year'])) {
222:                     $url->add(
223:                         array(
224:                             'year' => $data['year'],
225:                             'month' => (empty($data['month']) ? 0 : $data['month']),
226:                             'day' => (empty($data['day']) ? 0 : $data['day'])));
227:                 }
228: 
229:                 return Horde::url($url, $full, $append_session);
230: 
231:             } else {
232:                 $url = Horde::url('view.php', $full, $append_session);
233: 
234:                 // See note above about delegating url generation to gallery/view
235:                 if ($data['view'] == 'Image' &&
236:                     !empty($data['gallery_view']) &&
237:                     $data['gallery_view'] == 'GalleryLightbox') {
238:                     $data['view'] = 'Gallery';
239:                     $url->setAnchor($data['image']);
240:                 }
241: 
242:                 return $url->add($data)->setRaw(true);
243:             }
244: 
245:         case 'group':
246:             if ($rewrite) {
247:                 if (empty($data['groupby'])) {
248:                     $data['groupby'] = $prefs->getValue('groupby');
249:                 }
250:                 if ($data['groupby'] == 'owner') {
251:                     $url = 'user/';
252:                 } elseif ($data['groupby'] == 'none') {
253:                     $url = 'all/';
254:                 }
255:                 unset($data['groupby']);
256: 
257:                 $url = Horde::url($url, $full, $append_session);
258:                 if (count($data)) {
259:                     $url->add($data);
260:                 }
261:                 return $url;
262:             } else {
263:                 return Horde::url('group.php', $full, $append_session)->add($data);
264:             }
265: 
266:         case 'rss_user':
267:             if ($rewrite) {
268:                 return Horde::url(
269:                     'user/' . urlencode($data['owner']) . '/rss',
270:                     $full,
271:                     $append_session);
272:             } else {
273:                 $url = Horde::url(
274:                     new Horde_Url('rss.php'), $full, $append_session);
275:                 return $url->add(
276:                     array('stream_type' => 'user', 'id' => $data['owner']));
277:             }
278: 
279:         case 'rss_gallery':
280:             if ($rewrite) {
281:                 $id = (!empty($data['slug'])) ?
282:                     $data['slug'] :
283:                     'id/' . (int)$data['gallery'];
284:                 return Horde::url(
285:                     'gallery/' . $id . '/rss',
286:                     $full,
287:                     $append_session);
288:             } else {
289:                 return Horde::url(
290:                     'rss.php',
291:                     $full,
292:                     $append_session)->add(
293:                         array(
294:                             'stream_type' => 'gallery',
295:                             'id' => (int)$data['gallery']));
296:             }
297: 
298:         case 'default_view':
299:             switch ($prefs->getValue('defaultview')) {
300:             case 'browse':
301:                 return Horde::url(new Horde_Url('browse.php'), $full, $append_session);
302: 
303:             case 'galleries':
304:                 $url = Ansel::getUrlFor('view', array('view' => 'List'), true);
305:                 break;
306: 
307:             case 'mygalleries':
308:             default:
309:                $url = Ansel::getUrlFor(
310:                    'view',
311:                    array(
312:                         'view' => 'List',
313:                         'owner' => $GLOBALS['registry']->getAuth(),
314:                         'groupby' => 'owner'),
315:                    true);
316:                break;
317:             }
318: 
319:             return $url;
320:         }
321:     }
322: 
323:     /**
324:      * Return a link to an image, suitable for use in an <img/> tag
325:      * Takes into account $conf['vfs']['direct'] and other
326:      * factors.
327:      *
328:      * @param string $imageId     The id of the image.
329:      * @param string $view        The view ('screen', 'thumb', 'full', 'mini')
330:      *                            to show.
331:      * @param boolean $full       Return a path that includes the server name?
332:      * @param Ansel_Style $style  Use this gallery style
333:      *
334:      * @return Horde_Url The image path.
335:      */
336:     static public function getImageUrl(
337:         $imageId, $view = 'screen', $full = false, Ansel_Style $style = null)
338:     {
339:         global $conf;
340: 
341:         if (empty($imageId)) {
342:             return Horde::url((string)Ansel::getErrorImage($view), $full);
343:         }
344: 
345:         // Default to ansel_default
346:         if (is_null($style)) {
347:             $style = Ansel::getStyleDefinition('ansel_default');
348:         }
349: 
350:         // Don't load the image if the view exists
351:         $viewHash = Ansel_Image::viewExists($imageId, $view, $style);
352:         if ($conf['vfs']['src'] != 'php' && $viewHash === false) {
353:             // We have to make sure the image exists first, since we won't
354:             // be going through img/*.php to auto-create it.
355:             try {
356:                 $image = $GLOBALS['injector']
357:                     ->getInstance('Ansel_Storage')
358:                     ->getImage($imageId);
359:             } catch (Ansel_Exception $e) {
360:                 Horde::logMessage($e, 'ERR');
361:                 return Horde::url((string)Ansel::getErrorImage($view), $full);
362:             }
363:             try {
364:                 $image->createView($view, $style);
365:             } catch (Ansel_Exception $e) {
366:                 return Horde::url((string)Ansel::getErrorImage($view), $full);
367:             }
368:             $viewHash = $image->getViewHash($view, $style) . '/' . $image->getVFSName($view);
369:         }
370: 
371:         // First check for vfs-direct. If we are not using it, pass this off to
372:         // the img/*.php files, and check for sendfile support there.
373:         if ($conf['vfs']['src'] != 'direct') {
374:             $params = array('image' => $imageId);
375:             if (!is_null($style)) {
376:                 $params['t'] = $style->thumbstyle;
377:                 $params['b'] = $style->background;
378:                 if ($style->width) {
379:                     $params['w'] = $style->width;
380:                 }
381:                 if ($style->height) {
382:                     $params['h'] = $style->height;
383:                 }
384:             }
385: 
386:             return Horde::url('img/' . $view . '.php', $full)->add($params);
387:         }
388: 
389:         // Using vfs-direct
390:         $path = substr(str_pad($imageId, 2, 0, STR_PAD_LEFT), -2) . '/' . $viewHash;
391:         if ($full && substr($conf['vfs']['path'], 0, 7) != 'http://') {
392:             return Horde::url($conf['vfs']['path'] . $path, true, -1);
393:         } else {
394:             return new Horde_Url($conf['vfs']['path'] . htmlspecialchars($path));
395:         }
396:     }
397: 
398:     /**
399:      * Obtain a Horde_Image object
400:      *
401:      * @param array $params  Any additional parameters
402:      *
403:      * @return Horde_Image object
404:      */
405:     static public function getImageObject($params = array())
406:     {
407:         return $GLOBALS['injector']
408:             ->getInstance('Horde_Core_Factory_Image')
409:             ->create(array('type' => $GLOBALS['conf']['image']['type']));
410:     }
411: 
412:     /**
413:      * Read an image from the filesystem.
414:      *
415:      * @param string $file     The filename of the image.
416:      * @param array $override  Overwrite the file array with these values.
417:      *
418:      * @return array  The image data of the file as an array
419:      * @throws Horde_Exception_NotFound
420:      */
421:     static public function getImageFromFile($file, $override = array())
422:     {
423:         if (!file_exists($file)) {
424:             throw new Horde_Exception_NotFound(
425:                 sprintf(_("The file \"%s\" doesn't exist."), $file));
426:         }
427: 
428:         global $conf;
429: 
430:         // Get the mime type of the file (and make sure it's an image).
431:         $mime_type = Horde_Mime_Magic::analyzeFile(
432:             $file,
433:             isset($conf['mime']['magic_db']) ? $conf['mime']['magic_db'] : null);
434:         if (strpos($mime_type, 'image') === false) {
435:             throw new Horde_Exception_NotFound(
436:                 sprintf(_("Can't get unknown file type \"%s\"."), $file));
437:         }
438: 
439:         $image = array(
440:             'image_filename' => basename($file),
441:             'image_caption' => '',
442:             'image_type' => $mime_type,
443:             'data' => file_get_contents($file));
444: 
445:         // Override the array e.g., if we're changing filename to something else.
446:         if (count($override)) {
447:             $image = array_merge($image, $override);
448:         }
449: 
450:         return $image;
451:     }
452: 
453:     /**
454:      * Check to see if a particular image manipulation function is
455:      * available.
456:      *
457:      * @param string $feature  The name of the function.
458:      *
459:      * @return boolean  True if the function is available.
460:      */
461:     static public function isAvailable($feature)
462:     {
463:         static $capabilities;
464: 
465:         // If the administrator locked auto watermark on, disable user
466:         // intervention
467:         if ($feature == 'text_watermark' &&
468:             $GLOBALS['prefs']->getValue('watermark_auto') &&
469:             $GLOBALS['prefs']->isLocked('watermark_auto')) {
470: 
471:             return false;
472:         }
473: 
474:         if (!isset($capabilities)) {
475:             $im = Ansel::getImageObject();
476:             $capabilities = array_merge($im->getCapabilities(),
477:                                         $im->getLoadedEffects());
478:         }
479: 
480:         return in_array($feature, $capabilities);
481:     }
482: 
483:     /**
484:      * Generate a list of breadcrumbs showing where we are in the gallery
485:      * tree.
486:      *
487:      * @param Ansel_Gallery $gallery  The gallery the bread crumbs are for.
488:      * @param stirng $separator       The separator text to use between crumbs.
489:      *
490:      * @return string
491:      */
492:     static public function getBreadCrumbs($gallery = null, $separator = ' &raquo; ')
493:     {
494:         global $prefs;
495: 
496:         $ansel_storage = $GLOBALS['injector']->getInstance('Ansel_Storage');
497:         $groupby = Horde_Util::getFormData('groupby', $prefs->getValue('groupby'));
498:         $owner = Horde_Util::getFormData('owner');
499:         $image_id = (int)Horde_Util::getFormData('image');
500:         $actionID = Horde_Util::getFormData('actionID');
501:         $page = Horde_Util::getFormData('page', 0);
502:         $haveSearch = Horde_Util::getFormData('havesearch', 0);
503: 
504:         if (is_null($gallery)) {
505:             $gallery_id = (int)Horde_Util::getFormData('gallery');
506:             $gallery_slug = Horde_Util::getFormData('slug');
507:             try {
508:                 if (!empty($gallery_slug)) {
509:                     $gallery = $ansel_storage->getGalleryBySlug($gallery_slug);
510:                 } elseif (!empty($gallery_id)) {
511:                     $gallery = $ansel_storage->getGallery($gallery_id);
512:                 }
513:             } catch (Ansel_Exception $e) {}
514:         }
515: 
516:         if ($gallery) {
517:             $owner = $gallery->get('owner');
518:         }
519: 
520:         if (!empty($image_id)) {
521:             $image = $ansel_storage->getImage($image_id);
522:             if (empty($gallery)) {
523:                 $gallery = $ansel_storage->getGallery($image->gallery);
524:             }
525:         }
526:         if (isset($gallery)) {
527:             $owner = $gallery->get('owner');
528:         }
529:         if (!empty($owner)) {
530:             if (!$owner) {
531:                 $owner_title = _("System Galleries");
532:             } elseif ($owner == $GLOBALS['registry']->getAuth()) {
533:                 $owner_title = _("My Galleries");
534:             } elseif (!empty($GLOBALS['conf']['gallery']['customlabel'])) {
535:                 $uprefs = $GLOBALS['injector']
536:                     ->getInstance('Horde_Core_Factory_Prefs')
537:                     ->create(
538:                         'ansel',
539:                         array(
540:                             'cache' => false,
541:                             'user' => $owner)
542:                 );
543:                 $fullname = $uprefs->getValue('grouptitle');
544:                 if (!$fullname) {
545:                     $identity = $GLOBALS['injector']
546:                         ->getInstance('Horde_Core_Factory_Identity')
547:                         ->create($owner);
548:                     $fullname = $identity->getValue('fullname');
549:                     if (!$fullname) {
550:                         $fullname = $owner;
551:                     }
552:                     $owner_title = sprintf(_("%s's Galleries"), $fullname);
553:                 } else {
554:                     $owner_title = $fullname;
555:                 }
556:             } else {
557:                 $owner_title = sprintf(_("%s's Galleries"), $owner);
558:             }
559:         }
560: 
561:         // Construct the breadcrumbs backward, from where we are now up through
562:         // the path back to the top.  By constructing it backward we can treat
563:         // the last element (the current page) specially.
564:         $levels = 0;
565:         $nav = '';
566:         $urlFlags = array(
567:             'havesearch' => $haveSearch,
568:             'force_grouping' => true);
569: 
570:         // Check for an active image
571:         if (!empty($image_id)) {
572:             $text = '<span class="thiscrumb" id="PhotoName">'
573:                 . htmlspecialchars($image->filename) . '</span>';
574:             $nav = $separator . $text . $nav;
575:             $levels++;
576:         }
577: 
578:         if ($gallery) {
579:             $trails = $gallery->getGalleryCrumbData();
580:             foreach ($trails as $trail) {
581:                 $title = $trail['title'];
582:                 $navdata = $trail['navdata'];
583:                 if ($levels++ > 0) {
584:                     if ((empty($image_id) && $levels == 1) ||
585:                         (!empty($image_id) && $levels == 2)) {
586:                         $urlParameters = array_merge($urlFlags, array('page' => $page));
587:                     } else {
588:                         $urlParameters = $urlFlags;
589:                     }
590:                     $nav = $separator
591:                         . Ansel::getUrlFor('view', array_merge($navdata, $urlParameters))->link()
592:                         . $title . '</a>' . $nav;
593:                 } else {
594:                     $nav = $separator . '<span class="thiscrumb">' . $title
595:                         . '</span>' . $nav;
596:                 }
597:             }
598:         }
599: 
600:         if (!empty($owner_title)) {
601:             $owner_title = htmlspecialchars($owner_title);
602:             $levels++;
603:             if ($gallery) {
604:                 $nav = $separator
605:                     . Ansel::getUrlFor(
606:                         'view',
607:                         array(
608:                             'view' => 'List',
609:                             'groupby' => 'owner',
610:                             'owner' => $owner,
611:                             'havesearch' => $haveSearch))->link()
612:                     . $owner_title . '</a>' . $nav;
613:             } else {
614:                 $nav = $separator . $owner_title . $nav;
615:             }
616:         }
617: 
618:         if ($haveSearch == 0) {
619:             $text = _("Galleries");
620:             $link = Ansel::getUrlFor('view', array('view' => 'List'))->link();
621:         } else {
622:             $text = _("Browse Tags");
623:             $link = Ansel::getUrlFor(
624:                 'view', array('view' => 'Results'), true)->link();
625:         }
626:         if ($levels > 0) {
627:             $nav = $link . $text . '</a>' . $nav;
628:         } else {
629:             $nav = $text . $nav;
630:         }
631: 
632:         return '<span class="breadcrumbs">' . $nav . '</span>';
633:     }
634: 
635:     /**
636:      * Build a HTML <select> element containing all the available
637:      * gallery styles.
638:      *
639:      * @param string $element_name  The element's id/name attribute.
640:      * @param string $selected      Mark this element as currently selected.
641:      *
642:      * @return string  The HTML for the <select> element.
643:      */
644:     static public function getStyleSelect($element_name, $selected = '')
645:     {
646:         $styles = $GLOBALS['injector']->getInstance('Ansel_Styles');
647: 
648:         // Build the available styles, but don't show hidden styles
649:         foreach ($styles as $key => $style) {
650:             if (empty($style['hide'])) {
651:                 $options[$key] = $style['title'];
652:             }
653:         }
654: 
655:         // Nothing explicitly selected, use the global pref
656:         if ($selected == '') {
657:             $selected = $GLOBALS['prefs']->getValue('default_gallerystyle');
658:         }
659: 
660:         $html = '<select id="' . $element_name . '" name="' . $element_name . '">';
661:         foreach ($options as $key => $option) {
662:             $html .= '  <option value="' . $key . '"' . (($selected == $key) ? 'selected="selected"' : '') . '>'
663:                 . $option . '</option>';
664:         }
665: 
666:         return $html .= '</select>';
667:     }
668: 
669:     /**
670:      * Get a pre-defined style definition for the requested named style
671:      *
672:      * @param string $style  The name of the style to fetch
673:      *
674:      * @return Ansel_Style   The definition of the requested style if it's
675:      *                       available, otherwise, the ansel_default style is
676:      *                       returned.
677:      */
678:     static public function getStyleDefinition($style)
679:     {
680:         $styles = $GLOBALS['injector']->getInstance('Ansel_Styles');
681:         if (isset($styles[$style])) {
682:             return new Ansel_Style($styles[$style]);
683:         } else {
684:             return  new Ansel_Style($styles['ansel_default']);
685:         }
686:     }
687: 
688:     /**
689:      * Get a date parts array containing only enough date parts for the depth
690:      * we are at. If an empty array is passed, attempt to get the parts from
691:      * url parametrs. Any missing date parts must be set to 0.
692:      *
693:      * @param array $date  A full date parts array or an empty array.
694:      *
695:      * @return array A trimmed down (if necessary) date parts array.
696:      */
697:     static public function getDateParameter($date = array())
698:     {
699:         if (!count($date)) {
700:             $date = array(
701:                 'year' => Horde_Util::getFormData('year', 0),
702:                 'month' => Horde_Util::getFormData('month', 0),
703:                 'day' => Horde_Util::getFormData('day', 0));
704:         }
705:         $return = array();
706:         $return['year'] = !empty($date['year']) ? $date['year'] : 0;
707:         $return['month'] = !empty($date['month']) ? $date['month'] : 0;
708:         $return['day'] = !empty($date['day']) ? $date['day'] : 0;
709: 
710:         return $return;
711:     }
712: 
713:     /**
714:      * Downloads all requested images as a zip file.  Assumes all permissions
715:      * have been checked on the requested resource.  Can request either a
716:      * single gallery of images, OR an array of individual image ids.
717:      *
718:      * @param Ansel_Gallery $gallery  The galleries to download
719:      * @param array $images           The images to download
720:      */
721:     static public function downloadImagesAsZip($gallery = null, $images = array())
722:     {
723: 
724:         if (empty($GLOBALS['conf']['gallery']['downloadzip'])) {
725:             $GLOBALS['notification']->push(
726:                 _("Downloading zip files is not enabled. Talk to your server administrator."));
727:             Horde::url('view.php?view=List', true)->redirect();
728:             exit;
729:         }
730: 
731:         // Requested a gallery
732:         if (!is_null($gallery)) {
733:             // We can name the zip file with the slug if we have it
734:             $slug = $gallery->get('slug');
735: 
736:             // Set the date in case we are viewing in date mode
737:             $gallery->setDate(Ansel::getDateParameter());
738:             $images = $gallery->listImages();
739:         }
740: 
741:         // At this point, we should always have a list of images
742:         if (!count($images)) {
743:             $notification->push(
744:                 sprintf(_("There are no photos in %s to download."),
745:                 $gallery->get('name')),
746:                 'horde.message');
747: 
748:             Horde::url('view.php?view=List', true)->redirect();
749:             exit;
750:         }
751: 
752:         // Try to close off the current session to avoid locking it while the
753:         // gallery is downloading.
754:         @session_write_close();
755: 
756:         if (!is_null($gallery)) {
757:             // Check full photo permissions
758:             if ($gallery->canDownload()) {
759:                 $view = 'full';
760:             } else {
761:                 $view = 'screen';
762:             }
763:         }
764: 
765:         $zipfiles = array();
766:         foreach ($images as $id) {
767:             $image = $GLOBALS['injector']
768:                 ->getInstance('Ansel_Storage')
769:                 ->getImage($id);
770: 
771:             // If we didn't select an entire gallery, check the download
772:             // size for each image.
773:             if (!isset($view)) {
774:                 $g = $GLOBALS['injector']
775:                     ->getInstance('Ansel_Storage')
776:                     ->getGallery($image->gallery);
777:                 $v = $g->canDownload() ? 'full' : 'screen';
778:             } else {
779:                 $v = $view;
780:             }
781: 
782:             $zipfiles[] = array('data' => $image->raw($v),
783:                                 'name' => $image->filename);
784:         }
785: 
786:         $zip = Horde_Compress::factory('zip');
787:         $body = $zip->compress($zipfiles);
788:         if (!empty($gallery)) {
789:             $filename = (!empty($slug) ? $slug : $gallery->id) . '.zip';
790:         } else {
791:             $filename = 'Ansel.zip';
792:         }
793:         $GLOBALS['browser']->downloadHeaders($filename, 'application/zip', false,
794:                                   strlen($body));
795:         echo $body;
796:         exit;
797:     }
798: 
799:     /**
800:      * Generate the JS necessary to embed a gallery / images into another
801:      * external site.
802:      *
803:      * @param array $options  The options to build the view.
804:      *
805:      * @return string  The javascript code
806:      */
807:     static public function embedCode($options)
808:     {
809:         if (empty($options['container'])) {
810:             $domid = uniqid();
811:             $options['container'] = $domid;
812:         } else {
813:             $domid = $options['container'];
814:         }
815: 
816:         $imple = $GLOBALS['injector']
817:             ->getInstance('Horde_Core_Factory_Imple')
818:             ->create(array('ansel', 'Embed'), $options);
819: 
820:         return '<script type="text/javascript" src="' . $imple->getUrl()
821:             . '"></script><div id="' . $domid . '"></div>';
822:     }
823: 
824:     /**
825:      * Get the URL for a tag search link
826:      *
827:      * @TODO: Move this to Tagger
828:      *
829:      * @param array $tags      The tag ids to link to
830:      * @param string $action   The action we want to perform with this tag.
831:      * @param string $owner    The owner we want to filter the results by
832:      *
833:      * @return string  The URL for this tag and action
834:      */
835:     static public function getTagLinks($tags, $action = 'add', $owner = null)
836:     {
837: 
838:         $results = array();
839:         foreach ($tags as $id => $taginfo) {
840:             $params = array('view' => 'Results',
841:                             'tag' => $taginfo['tag_name']);
842:             if (!empty($owner)) {
843:                 $params['owner'] = $owner;
844:             }
845:             if ($action != 'add') {
846:                 $params['actionID'] = $action;
847:             }
848:             $link = Ansel::getUrlFor('view', $params, true);
849:             $results[$id] = $link;
850:         }
851: 
852:         return $results;
853:     }
854: 
855:     /**
856:      * Simple helper to output initial Ansel JS.
857:      */
858:     static public function initJSVariables()
859:     {
860:         if (!$GLOBALS['browser']->isMobile()) {
861:             $code['conf'] = array(
862:                 'BASE_URI' => (string)Horde::url(
863:                     '',
864:                     true,
865:                     array(
866:                         'app' => 'ansel',
867:                         'append_session' => -1)));
868: 
869:             // IF
870:             $code['conf']['maps'] = $GLOBALS['conf']['maps'];
871:             $code['conf']['pixeluri'] = (string)Horde::getServiceLink('pixel', 'ansel');
872:             $code['conf']['markeruri'] = (string)Horde_Themes::img('photomarker.png');
873:             $code['conf']['shadowuri'] = (string)Horde_Themes::img('photomarker-shadow.png');
874:             $code['conf']['havetwitter'] = !empty($GLOBALS['conf']['twitter']['enabled']);
875:             $code['ajax'] = new stdClass();
876:             $code['widgets'] = new stdClass();
877:             Horde::addInlineJsVars(array(
878:                 'var Ansel' => $code));
879:         }
880:     }
881: 
882:    /**
883:      * Initialize the map.
884:      *
885:      * @TODO: Horde 5 - move this to a method in either Core or a new Horde_Map
886:      *        framework pacakge.
887:      * @param array $params Parameters to pass the the map
888:      *
889:      * @return void
890:      */
891:     static public function initHordeMap($params = array())
892:     {
893:         if (empty($params['providers'])) {
894:             $params['providers'] = $GLOBALS['conf']['maps']['providers'];
895:         }
896:         if (empty($params['geocoder'])) {
897:             $params['geocoder'] = $GLOBALS['conf']['maps']['geocoder'];
898:         }
899:         // Language specific file needed?
900:         $language = str_replace('_', '-', $GLOBALS['language']);
901:         if (!file_exists($GLOBALS['registry']->get('jsfs', 'horde') . '/map/lang/' . $language . '.js')) {
902:             $language = 'en-US';
903:         }
904:         $params['conf'] = array(
905:             'language' => $language
906:         );
907:         $params['driver'] = 'Horde';
908:         foreach ($params['providers'] as $layer) {
909:             switch ($layer) {
910:             case 'Google':
911:                 $params['conf']['apikeys']['google'] = $GLOBALS['conf']['api']['googlemaps'];
912:                 break;
913:             case 'Yahoo':
914:                 $params['conf']['apikeys']['yahoo'] = $GLOBALS['conf']['api']['yahoomaps'];
915:                 break;
916:             case 'Cloudmade':
917:                 $params['conf']['apikeys']['cloudmade'] = $GLOBALS['conf']['api']['cloudmade'];
918:                 break;
919:             case 'Mytopo':
920:                 $params['conf']['apikeys']['mytopo'] = $GLOBALS['conf']['api']['mytopo'];
921:                 break;
922:             }
923:         }
924: 
925:         if (!empty($params['geocoder'])) {
926:             switch ($params['geocoder']) {
927:             case 'Google':
928:                 $params['conf']['apikeys']['google'] = $GLOBALS['conf']['api']['googlemaps'];
929:                 break;
930:             case 'Yahoo':
931:                 $params['conf']['apikeys']['yahoo'] = $GLOBALS['conf']['api']['yahoomaps'];
932:                 break;
933:             case 'Cloudmade':
934:                 $params['conf']['apikeys']['cloudmade'] = $GLOBALS['conf']['api']['cloudmade'];
935:                 break;
936:             }
937:         }
938:         $params['jsuri'] = $GLOBALS['registry']->get('jsuri', 'horde') . '/map/';
939:         Horde::addScriptFile('map/map.js', 'horde');
940:         Horde::addScriptFile('map.js');
941:         $js = 'HordeMap.initialize(' . Horde_Serialize::serialize($params, HORDE_SERIALIZE::JSON) . ');';
942:         Horde::addinlineScript($js);
943:     }
944: 
945: }
946: 
API documentation generated by ApiGen